Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add action for closing all unfocused panes #13547

Merged
7 commits merged into from
Aug 26, 2022
1 change: 1 addition & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@
"enum": [
"adjustFontSize",
"clearBuffer",
"closeOtherPanes",
JerBast marked this conversation as resolved.
Show resolved Hide resolved
"closeOtherTabs",
"closePane",
"closeTab",
Expand Down
28 changes: 28 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,34 @@ namespace winrt::TerminalApp::implementation
}
}

void TerminalPage::_HandleCloseOtherPanes(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto terminalTab{ _GetFocusedTabImpl() })
{
const auto activePane = terminalTab->GetActivePane();
if (terminalTab->GetRootPane() != activePane)
{
_UnZoomIfNeeded();

// Accumulate list of all unfocused leaf panes
std::deque<uint32_t> unfocusedPaneIds{};
terminalTab->GetRootPane()->WalkTree([&](auto p) {
JerBast marked this conversation as resolved.
Show resolved Hide resolved
const auto id = p->Id();
if (id.has_value() && id != activePane->Id())
{
unfocusedPaneIds.push_back(id.value());
}
});

// Start by removing the panes that were least recently added
sort(begin(unfocusedPaneIds), end(unfocusedPaneIds), std::greater<uint32_t>());
_CloseUnfocusedPanes(terminalTab->get_weak(), std::move(unfocusedPaneIds));
}
}
args.Handled(true);
JerBast marked this conversation as resolved.
Show resolved Hide resolved
}

void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,9 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
remainingChild->_firstChild = nullptr;
remainingChild->_secondChild = nullptr;
}

// Notify the discarded child that it was closed by its parent
closedChild->_ClosedByParentHandlers();
}

winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst)
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/Pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class Pane : public std::enable_shared_from_this<Pane>

void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states);

WINRT_CALLBACK(ClosedByParent, winrt::delegate<>);
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);

using gotFocusArgs = winrt::delegate<std::shared_ptr<Pane>, winrt::Windows::UI::Xaml::FocusState>;
Expand Down
114 changes: 74 additions & 40 deletions src/cascadia/TerminalApp/TabManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,48 @@ namespace winrt::TerminalApp::implementation
}
}

// Method Description:
// - Removes the pane from the tab it belongs to.
// Arguments:
// - pane: the pane to close.
winrt::Windows::Foundation::IAsyncAction TerminalPage::_HandleClosePaneRequested(std::shared_ptr<Pane> pane)
{
if (const auto control{ pane->GetTerminalControl() })
{
if (control.ReadOnly())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be a pane->ContainsReadOnly call, replaced now with checking if the actual control in the pane is read-only.

Technically, you can select a parent pane. So you could close a parent pane (and close all the leaves within it). I think with this change, we've lost the ability to check if any leaves in a pane are read-only.

A simple fix might be to just add an else (pane->ContainsReadOnly()) after the if (const auto control block, and repeat the walking we had before in that block. A little duplicated, but

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the 'response' to a read-only pane is the same in this case (for parents and leaf nodes), I replaced the existing read-only check with the pane->ContainsReadOnly check and changed the toggling accordingly to avoid duplication (f5a1347)

{
auto warningResult = co_await _ShowCloseReadOnlyDialog();

// If the user didn't explicitly click on close tab - leave
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}
control.ToggleReadOnly();
}
}

// 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));

// If specified, detach before closing to directly update the pane structure
pane->Close();
}

// Method Description:
// - Close the currently focused pane. If the pane is the last pane in the
// tab, the tab will also be closed. This will happen when we handle the
Expand All @@ -772,46 +814,7 @@ namespace winrt::TerminalApp::implementation

if (const auto pane{ terminalTab->GetActivePane() })
{
if (pane->ContainsReadOnly())
{
auto warningResult = co_await _ShowCloseReadOnlyDialog();

// If the user didn't explicitly click on close tab - leave
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}

// Clean read-only mode to prevent additional prompt if closing the pane triggers closing of a hosting tab
pane->WalkTree([](auto p) {
if (const auto control{ p->GetTerminalControl() })
{
if (control.ReadOnly())
{
control.ToggleReadOnly();
}
}
});
}

// 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();
co_await _HandleClosePaneRequested(pane);
}
}
else if (auto index{ _GetFocusedTabIndex() })
Expand All @@ -824,6 +827,37 @@ namespace winrt::TerminalApp::implementation
}
}

// Method Description:
// - Close all other panes except the pane that is currently focused.
// Arguments:
// - weakTab: weak reference to the tab that the pane belongs to.
// - paneIds: collection of the IDs of the panes that are marked for removal.
winrt::fire_and_forget TerminalPage::_CloseUnfocusedPanes(weak_ref<TerminalTab> weakTab, std::deque<uint32_t> paneIds)
{
if (auto strongTab{ weakTab.get() })
{
// Close all unfocused panes one by one
while (!paneIds.empty())
{
const auto id = paneIds.front();
paneIds.pop_front();

if (const auto pane{ strongTab->GetRootPane()->FindPane(id) })
{
pane->ClosedByParent([ids{ std::move(paneIds) }, weakThis{ get_weak() }, weakTab]() {
if (auto strongThis{ weakThis.get() })
{
strongThis->_CloseUnfocusedPanes(weakTab, std::move(ids));
}
});

// Close the pane which will eventually trigger the closed by parent event
co_await _HandleClosePaneRequested(pane);
lhecker marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

// Method Description:
// - Close the tab at the given index.
void TerminalPage::_CloseTabAtIndex(uint32_t index)
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,10 @@ namespace winrt::TerminalApp::implementation
winrt::com_ptr<TerminalTab> _GetFocusedTabImpl() const noexcept;
TerminalApp::TabBase _GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept;

winrt::Windows::Foundation::IAsyncAction _HandleClosePaneRequested(std::shared_ptr<Pane> pane);
winrt::fire_and_forget _SetFocusedTab(const winrt::TerminalApp::TabBase tab);
winrt::fire_and_forget _CloseFocusedPane();
winrt::fire_and_forget _CloseUnfocusedPanes(weak_ref<TerminalTab> weakTab, std::deque<uint32_t> paneIds);
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: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <LibraryResources.h>

static constexpr std::string_view AdjustFontSizeKey{ "adjustFontSize" };
static constexpr std::string_view CloseOtherPanesKey{ "closeOtherPanes" };
static constexpr std::string_view CloseOtherTabsKey{ "closeOtherTabs" };
static constexpr std::string_view ClosePaneKey{ "closePane" };
static constexpr std::string_view CloseTabKey{ "closeTab" };
Expand Down Expand Up @@ -329,6 +330,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static const auto GeneratedActionNames = []() {
return std::unordered_map<ShortcutAction, winrt::hstring>{
{ ShortcutAction::AdjustFontSize, RS_(L"AdjustFontSizeCommandKey") },
{ ShortcutAction::CloseOtherPanes, RS_(L"CloseOtherPanesCommandKey") },
{ ShortcutAction::CloseOtherTabs, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::ClosePane, RS_(L"ClosePaneCommandKey") },
{ ShortcutAction::CloseTab, L"" }, // Intentionally omitted, must be generated by GenerateName
Expand Down
149 changes: 75 additions & 74 deletions src/cascadia/TerminalSettingsModel/AllShortcutActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,80 +23,81 @@
// each action. This is _NOT_ something that should be used when any individual
// case should be customized.

#define ALL_SHORTCUT_ACTIONS \
ON_ALL_ACTIONS(CopyText) \
ON_ALL_ACTIONS(PasteText) \
ON_ALL_ACTIONS(OpenNewTabDropdown) \
ON_ALL_ACTIONS(DuplicateTab) \
ON_ALL_ACTIONS(NewTab) \
ON_ALL_ACTIONS(CloseWindow) \
ON_ALL_ACTIONS(CloseTab) \
ON_ALL_ACTIONS(ClosePane) \
ON_ALL_ACTIONS(NextTab) \
ON_ALL_ACTIONS(PrevTab) \
ON_ALL_ACTIONS(SendInput) \
ON_ALL_ACTIONS(SplitPane) \
ON_ALL_ACTIONS(ToggleSplitOrientation) \
ON_ALL_ACTIONS(TogglePaneZoom) \
ON_ALL_ACTIONS(SwitchToTab) \
ON_ALL_ACTIONS(AdjustFontSize) \
ON_ALL_ACTIONS(ResetFontSize) \
ON_ALL_ACTIONS(ScrollUp) \
ON_ALL_ACTIONS(ScrollDown) \
ON_ALL_ACTIONS(ScrollUpPage) \
ON_ALL_ACTIONS(ScrollDownPage) \
ON_ALL_ACTIONS(ScrollToTop) \
ON_ALL_ACTIONS(ScrollToBottom) \
ON_ALL_ACTIONS(ScrollToMark) \
ON_ALL_ACTIONS(AddMark) \
ON_ALL_ACTIONS(ClearMark) \
ON_ALL_ACTIONS(ClearAllMarks) \
ON_ALL_ACTIONS(ResizePane) \
ON_ALL_ACTIONS(MoveFocus) \
ON_ALL_ACTIONS(MovePane) \
ON_ALL_ACTIONS(SwapPane) \
ON_ALL_ACTIONS(Find) \
ON_ALL_ACTIONS(ToggleShaderEffects) \
ON_ALL_ACTIONS(ToggleFocusMode) \
ON_ALL_ACTIONS(ToggleFullscreen) \
ON_ALL_ACTIONS(ToggleAlwaysOnTop) \
ON_ALL_ACTIONS(OpenSettings) \
ON_ALL_ACTIONS(SetFocusMode) \
ON_ALL_ACTIONS(SetFullScreen) \
ON_ALL_ACTIONS(SetMaximized) \
ON_ALL_ACTIONS(SetColorScheme) \
ON_ALL_ACTIONS(SetTabColor) \
ON_ALL_ACTIONS(OpenTabColorPicker) \
ON_ALL_ACTIONS(RenameTab) \
ON_ALL_ACTIONS(OpenTabRenamer) \
ON_ALL_ACTIONS(ExecuteCommandline) \
ON_ALL_ACTIONS(ToggleCommandPalette) \
ON_ALL_ACTIONS(CloseOtherTabs) \
ON_ALL_ACTIONS(CloseTabsAfter) \
ON_ALL_ACTIONS(TabSearch) \
ON_ALL_ACTIONS(MoveTab) \
ON_ALL_ACTIONS(BreakIntoDebugger) \
ON_ALL_ACTIONS(TogglePaneReadOnly) \
ON_ALL_ACTIONS(FindMatch) \
ON_ALL_ACTIONS(NewWindow) \
ON_ALL_ACTIONS(IdentifyWindow) \
ON_ALL_ACTIONS(IdentifyWindows) \
ON_ALL_ACTIONS(RenameWindow) \
ON_ALL_ACTIONS(OpenWindowRenamer) \
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(OpenSystemMenu) \
ON_ALL_ACTIONS(ExportBuffer) \
ON_ALL_ACTIONS(ClearBuffer) \
ON_ALL_ACTIONS(MultipleActions) \
ON_ALL_ACTIONS(Quit) \
ON_ALL_ACTIONS(AdjustOpacity) \
ON_ALL_ACTIONS(RestoreLastClosed) \
ON_ALL_ACTIONS(SelectAll) \
ON_ALL_ACTIONS(MarkMode) \
ON_ALL_ACTIONS(ToggleBlockSelection) \
ON_ALL_ACTIONS(SwitchSelectionEndpoint)
#define ALL_SHORTCUT_ACTIONS \
ON_ALL_ACTIONS(CopyText) \
ON_ALL_ACTIONS(PasteText) \
ON_ALL_ACTIONS(OpenNewTabDropdown) \
ON_ALL_ACTIONS(DuplicateTab) \
ON_ALL_ACTIONS(NewTab) \
ON_ALL_ACTIONS(CloseWindow) \
ON_ALL_ACTIONS(CloseTab) \
ON_ALL_ACTIONS(ClosePane) \
ON_ALL_ACTIONS(NextTab) \
ON_ALL_ACTIONS(PrevTab) \
ON_ALL_ACTIONS(SendInput) \
ON_ALL_ACTIONS(SplitPane) \
ON_ALL_ACTIONS(ToggleSplitOrientation) \
ON_ALL_ACTIONS(TogglePaneZoom) \
ON_ALL_ACTIONS(SwitchToTab) \
ON_ALL_ACTIONS(AdjustFontSize) \
ON_ALL_ACTIONS(ResetFontSize) \
ON_ALL_ACTIONS(ScrollUp) \
ON_ALL_ACTIONS(ScrollDown) \
ON_ALL_ACTIONS(ScrollUpPage) \
ON_ALL_ACTIONS(ScrollDownPage) \
ON_ALL_ACTIONS(ScrollToTop) \
ON_ALL_ACTIONS(ScrollToBottom) \
ON_ALL_ACTIONS(ScrollToMark) \
ON_ALL_ACTIONS(AddMark) \
ON_ALL_ACTIONS(ClearMark) \
ON_ALL_ACTIONS(ClearAllMarks) \
ON_ALL_ACTIONS(ResizePane) \
ON_ALL_ACTIONS(MoveFocus) \
ON_ALL_ACTIONS(MovePane) \
ON_ALL_ACTIONS(SwapPane) \
ON_ALL_ACTIONS(Find) \
ON_ALL_ACTIONS(ToggleShaderEffects) \
ON_ALL_ACTIONS(ToggleFocusMode) \
ON_ALL_ACTIONS(ToggleFullscreen) \
ON_ALL_ACTIONS(ToggleAlwaysOnTop) \
ON_ALL_ACTIONS(OpenSettings) \
ON_ALL_ACTIONS(SetFocusMode) \
ON_ALL_ACTIONS(SetFullScreen) \
ON_ALL_ACTIONS(SetMaximized) \
ON_ALL_ACTIONS(SetColorScheme) \
ON_ALL_ACTIONS(SetTabColor) \
ON_ALL_ACTIONS(OpenTabColorPicker) \
ON_ALL_ACTIONS(RenameTab) \
ON_ALL_ACTIONS(OpenTabRenamer) \
ON_ALL_ACTIONS(ExecuteCommandline) \
ON_ALL_ACTIONS(ToggleCommandPalette) \
ON_ALL_ACTIONS(CloseOtherTabs) \
ON_ALL_ACTIONS(CloseTabsAfter) \
ON_ALL_ACTIONS(TabSearch) \
ON_ALL_ACTIONS(MoveTab) \
ON_ALL_ACTIONS(BreakIntoDebugger) \
ON_ALL_ACTIONS(TogglePaneReadOnly) \
ON_ALL_ACTIONS(FindMatch) \
ON_ALL_ACTIONS(NewWindow) \
ON_ALL_ACTIONS(IdentifyWindow) \
ON_ALL_ACTIONS(IdentifyWindows) \
ON_ALL_ACTIONS(RenameWindow) \
ON_ALL_ACTIONS(OpenWindowRenamer) \
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(OpenSystemMenu) \
ON_ALL_ACTIONS(ExportBuffer) \
ON_ALL_ACTIONS(ClearBuffer) \
ON_ALL_ACTIONS(MultipleActions) \
ON_ALL_ACTIONS(Quit) \
ON_ALL_ACTIONS(AdjustOpacity) \
ON_ALL_ACTIONS(RestoreLastClosed) \
ON_ALL_ACTIONS(SelectAll) \
ON_ALL_ACTIONS(MarkMode) \
ON_ALL_ACTIONS(ToggleBlockSelection) \
ON_ALL_ACTIONS(SwitchSelectionEndpoint) \
ON_ALL_ACTIONS(CloseOtherPanes)

#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 @@ -567,4 +567,7 @@
<data name="SwitchSelectionEndpointCommandKey" xml:space="preserve">
<value>Switch selection endpoint</value>
</data>
<data name="CloseOtherPanesCommandKey" xml:space="preserve">
<value>Close all other panes</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 @@ -359,6 +359,7 @@
{ "command": { "action": "switchToTab", "index": 4294967295 }, "keys": "ctrl+alt+9" },

// Pane Management
{ "command": "closeOtherPanes" },
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": { "action": "splitPane", "split": "up" } },
{ "command": { "action": "splitPane", "split": "down" }, "keys": "alt+shift+-" },
Expand Down