Skip to content

Commit a093ca3

Browse files
Updated pane context menu (#18126)
## Motivation The motivation is that Windows users are more accustomed to working with GUI Menus using a mouse, unlike Linux users. ## Summary of the Pull Request added split pane with profile or duplicate up/down/left/right context menus as submenu added swap panes up/down/left/right context menus as submenu added toggle pane zoom context menu added close other panes context menu ## References : - Relevant Issues : (#18137) ## Type of change : - [x] New feature --------- Co-authored-by: Mike Griese <migrie@microsoft.com>
1 parent dd96ce4 commit a093ca3

File tree

6 files changed

+197
-23
lines changed

6 files changed

+197
-23
lines changed

src/cascadia/TerminalApp/Resources/en-US/Resources.resw

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,45 @@
214214
<data name="SplitPaneText" xml:space="preserve">
215215
<value>Split pane</value>
216216
</data>
217+
<data name="SplitPaneToolTipText" xml:space="preserve">
218+
<value>Right click for split directions - right/down/up/left</value>
219+
</data>
220+
<data name="SplitPaneDownText" xml:space="preserve">
221+
<value>Split pane down</value>
222+
</data>
223+
<data name="SplitPaneRightText" xml:space="preserve">
224+
<value>Split pane right</value>
225+
</data>
226+
<data name="SplitPaneUpText" xml:space="preserve">
227+
<value>Split pane up</value>
228+
</data>
229+
<data name="SplitPaneLeftText" xml:space="preserve">
230+
<value>Split pane left</value>
231+
</data>
232+
<data name="SplitPaneDuplicateText" xml:space="preserve">
233+
<value>Duplicate</value>
234+
</data>
235+
<data name="SwapPaneText" xml:space="preserve">
236+
<value>Swap pane</value>
237+
</data>
238+
<data name="SwapPaneDownText" xml:space="preserve">
239+
<value>Swap pane down</value>
240+
</data>
241+
<data name="SwapPaneRightText" xml:space="preserve">
242+
<value>Swap pane right</value>
243+
</data>
244+
<data name="SwapPaneUpText" xml:space="preserve">
245+
<value>Swap pane up</value>
246+
</data>
247+
<data name="SwapPaneLeftText" xml:space="preserve">
248+
<value>Swap pane left</value>
249+
</data>
250+
<data name="TogglePaneZoomText" xml:space="preserve">
251+
<value>Toggle pane zoom</value>
252+
</data>
253+
<data name="CloseOtherPanesText" xml:space="preserve">
254+
<value>Close other panes</value>
255+
</data>
217256
<data name="SearchWebText" xml:space="preserve">
218257
<value>Web search</value>
219258
</data>

src/cascadia/TerminalApp/TerminalPage.cpp

Lines changed: 136 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4988,9 +4988,48 @@ namespace winrt::TerminalApp::implementation
49884988
};
49894989
};
49904990

4991-
auto makeItem = [&menu, &makeCallback](const winrt::hstring& label,
4991+
auto makeItem = [&makeCallback](const winrt::hstring& label,
4992+
const winrt::hstring& icon,
4993+
const auto& action,
4994+
auto& targetMenu) {
4995+
AppBarButton button{};
4996+
4997+
if (!icon.empty())
4998+
{
4999+
auto iconElement = UI::IconPathConverter::IconWUX(icon);
5000+
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
5001+
button.Icon(iconElement);
5002+
}
5003+
5004+
button.Label(label);
5005+
button.Click(makeCallback(action));
5006+
targetMenu.SecondaryCommands().Append(button);
5007+
};
5008+
5009+
auto makeMenuItem = [](const winrt::hstring& label,
5010+
const winrt::hstring& icon,
5011+
const auto& subMenu,
5012+
auto& targetMenu) {
5013+
AppBarButton button{};
5014+
5015+
if (!icon.empty())
5016+
{
5017+
auto iconElement = UI::IconPathConverter::IconWUX(icon);
5018+
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
5019+
button.Icon(iconElement);
5020+
}
5021+
5022+
button.Label(label);
5023+
button.Flyout(subMenu);
5024+
targetMenu.SecondaryCommands().Append(button);
5025+
};
5026+
5027+
auto makeContextItem = [&makeCallback](const winrt::hstring& label,
49925028
const winrt::hstring& icon,
4993-
const auto& action) {
5029+
const winrt::hstring& tooltip,
5030+
const auto& action,
5031+
const auto& subMenu,
5032+
auto& targetMenu) {
49945033
AppBarButton button{};
49955034

49965035
if (!icon.empty())
@@ -5002,34 +5041,122 @@ namespace winrt::TerminalApp::implementation
50025041

50035042
button.Label(label);
50045043
button.Click(makeCallback(action));
5005-
menu.SecondaryCommands().Append(button);
5044+
WUX::Controls::ToolTipService::SetToolTip(button, box_value(tooltip));
5045+
button.ContextFlyout(subMenu);
5046+
targetMenu.SecondaryCommands().Append(button);
50065047
};
50075048

5049+
const auto focusedProfile = _GetFocusedTabImpl()->GetFocusedProfile();
5050+
auto separatorItem = AppBarSeparator{};
5051+
auto activeProfiles = _settings.ActiveProfiles();
5052+
auto activeProfileCount = gsl::narrow_cast<int>(activeProfiles.Size());
5053+
MUX::Controls::CommandBarFlyout splitPaneMenu{};
5054+
50085055
// Wire up each item to the action that should be performed. By actually
50095056
// connecting these to actions, we ensure the implementation is
50105057
// consistent. This also leaves room for customizing this menu with
50115058
// actions in the future.
50125059

5013-
makeItem(RS_(L"SplitPaneText"), L"\xF246", ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Duplicate } });
5014-
makeItem(RS_(L"DuplicateTabText"), L"\xF5ED", ActionAndArgs{ ShortcutAction::DuplicateTab, nullptr });
5060+
makeItem(RS_(L"DuplicateTabText"), L"\xF5ED", ActionAndArgs{ ShortcutAction::DuplicateTab, nullptr }, menu);
5061+
5062+
const auto focusedProfileName = focusedProfile.Name();
5063+
const auto focusedProfileIcon = focusedProfile.Icon();
5064+
const auto splitPaneDuplicateText = RS_(L"SplitPaneDuplicateText") + L" " + focusedProfileName; // SplitPaneDuplicateText
5065+
5066+
const auto splitPaneRightText = RS_(L"SplitPaneRightText");
5067+
const auto splitPaneDownText = RS_(L"SplitPaneDownText");
5068+
const auto splitPaneUpText = RS_(L"SplitPaneUpText");
5069+
const auto splitPaneLeftText = RS_(L"SplitPaneLeftText");
5070+
const auto splitPaneToolTipText = RS_(L"SplitPaneToolTipText");
5071+
5072+
MUX::Controls::CommandBarFlyout splitPaneContextMenu{};
5073+
makeItem(splitPaneRightText, focusedProfileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Duplicate, SplitDirection::Right, .5, nullptr } }, splitPaneContextMenu);
5074+
makeItem(splitPaneDownText, focusedProfileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Duplicate, SplitDirection::Down, .5, nullptr } }, splitPaneContextMenu);
5075+
makeItem(splitPaneUpText, focusedProfileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Duplicate, SplitDirection::Up, .5, nullptr } }, splitPaneContextMenu);
5076+
makeItem(splitPaneLeftText, focusedProfileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Duplicate, SplitDirection::Left, .5, nullptr } }, splitPaneContextMenu);
5077+
5078+
makeContextItem(splitPaneDuplicateText, focusedProfileIcon, splitPaneToolTipText, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Duplicate, SplitDirection::Automatic, .5, nullptr } }, splitPaneContextMenu, splitPaneMenu);
5079+
5080+
// add menu separator
5081+
const auto separatorAutoItem = AppBarSeparator{};
5082+
5083+
splitPaneMenu.SecondaryCommands().Append(separatorAutoItem);
5084+
5085+
for (auto profileIndex = 0; profileIndex < activeProfileCount; profileIndex++)
5086+
{
5087+
const auto profile = activeProfiles.GetAt(profileIndex);
5088+
const auto profileName = profile.Name();
5089+
const auto profileIcon = profile.Icon();
5090+
5091+
NewTerminalArgs args{};
5092+
args.Profile(profileName);
5093+
5094+
MUX::Controls::CommandBarFlyout splitPaneContextMenu{};
5095+
makeItem(splitPaneRightText, profileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Manual, SplitDirection::Right, .5, args } }, splitPaneContextMenu);
5096+
makeItem(splitPaneDownText, profileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Manual, SplitDirection::Down, .5, args } }, splitPaneContextMenu);
5097+
makeItem(splitPaneUpText, profileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Manual, SplitDirection::Up, .5, args } }, splitPaneContextMenu);
5098+
makeItem(splitPaneLeftText, profileIcon, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Manual, SplitDirection::Left, .5, args } }, splitPaneContextMenu);
5099+
5100+
makeContextItem(profileName, profileIcon, splitPaneToolTipText, ActionAndArgs{ ShortcutAction::SplitPane, SplitPaneArgs{ SplitType::Manual, SplitDirection::Automatic, .5, args } }, splitPaneContextMenu, splitPaneMenu);
5101+
}
5102+
5103+
makeMenuItem(RS_(L"SplitPaneText"), L"\xF246", splitPaneMenu, menu);
50155104

50165105
// Only wire up "Close Pane" if there's multiple panes.
50175106
if (_GetFocusedTabImpl()->GetLeafPaneCount() > 1)
50185107
{
5019-
makeItem(RS_(L"PaneClose"), L"\xE89F", ActionAndArgs{ ShortcutAction::ClosePane, nullptr });
5108+
MUX::Controls::CommandBarFlyout swapPaneMenu{};
5109+
const auto rootPane = _GetFocusedTabImpl()->GetRootPane();
5110+
const auto mruPanes = _GetFocusedTabImpl()->GetMruPanes();
5111+
auto activePane = _GetFocusedTabImpl()->GetActivePane();
5112+
rootPane->WalkTree([&](auto p) {
5113+
if (const auto& c{ p->GetTerminalControl() })
5114+
{
5115+
if (c == control)
5116+
{
5117+
activePane = p;
5118+
}
5119+
}
5120+
});
5121+
5122+
if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Down, mruPanes))
5123+
{
5124+
makeItem(RS_(L"SwapPaneDownText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Down } }, swapPaneMenu);
5125+
}
5126+
5127+
if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Right, mruPanes))
5128+
{
5129+
makeItem(RS_(L"SwapPaneRightText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Right } }, swapPaneMenu);
5130+
}
5131+
5132+
if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Up, mruPanes))
5133+
{
5134+
makeItem(RS_(L"SwapPaneUpText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Up } }, swapPaneMenu);
5135+
}
5136+
5137+
if (auto neighbor = rootPane->NavigateDirection(activePane, FocusDirection::Left, mruPanes))
5138+
{
5139+
makeItem(RS_(L"SwapPaneLeftText"), neighbor->GetProfile().Icon(), ActionAndArgs{ ShortcutAction::SwapPane, SwapPaneArgs{ FocusDirection::Left } }, swapPaneMenu);
5140+
}
5141+
5142+
makeMenuItem(RS_(L"SwapPaneText"), L"\xF1CB", swapPaneMenu, menu);
5143+
5144+
makeItem(RS_(L"TogglePaneZoomText"), L"\xE8A3", ActionAndArgs{ ShortcutAction::TogglePaneZoom, nullptr }, menu);
5145+
makeItem(RS_(L"CloseOtherPanesText"), L"\xE89F", ActionAndArgs{ ShortcutAction::CloseOtherPanes, nullptr }, menu);
5146+
makeItem(RS_(L"PaneClose"), L"\xE89F", ActionAndArgs{ ShortcutAction::ClosePane, nullptr }, menu);
50205147
}
50215148

50225149
if (control.ConnectionState() >= ConnectionState::Closed)
50235150
{
5024-
makeItem(RS_(L"RestartConnectionText"), L"\xE72C", ActionAndArgs{ ShortcutAction::RestartConnection, nullptr });
5151+
makeItem(RS_(L"RestartConnectionText"), L"\xE72C", ActionAndArgs{ ShortcutAction::RestartConnection, nullptr }, menu);
50255152
}
50265153

50275154
if (withSelection)
50285155
{
5029-
makeItem(RS_(L"SearchWebText"), L"\xF6FA", ActionAndArgs{ ShortcutAction::SearchForText, nullptr });
5156+
makeItem(RS_(L"SearchWebText"), L"\xF6FA", ActionAndArgs{ ShortcutAction::SearchForText, nullptr }, menu);
50305157
}
50315158

5032-
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
5159+
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } }, menu);
50335160
}
50345161

50355162
void TerminalPage::_PopulateQuickFixMenu(const TermControl& control,

src/cascadia/TerminalApp/TerminalTab.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ namespace winrt::TerminalApp::implementation
9191
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
9292

9393
std::shared_ptr<Pane> GetRootPane() const { return _rootPane; }
94+
std::vector<uint32_t> GetMruPanes() const { return _mruPanes; }
9495

9596
winrt::TerminalApp::TerminalTabStatus TabStatus()
9697
{

src/cascadia/TerminalControl/Resources/en-US/Resources.resw

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,18 @@ Please either install the missing font or choose another one.</value>
262262
<comment>The tooltip for a paste button</comment>
263263
</data>
264264
<data name="SearchCommandButton.Label" xml:space="preserve">
265+
<value>Find...</value>
266+
<comment>The label of a button for searching for the text</comment>
267+
</data>
268+
<data name="SearchCommandSelectionButton.Label" xml:space="preserve">
265269
<value>Find...</value>
266270
<comment>The label of a button for searching for the selected text</comment>
267271
</data>
268272
<data name="SearchCommandButton.ToolTipService.ToolTip" xml:space="preserve">
273+
<value>Find</value>
274+
<comment>The tooltip for a button for searching for the text</comment>
275+
</data>
276+
<data name="SearchCommandSelectionButton.ToolTipService.ToolTip" xml:space="preserve">
269277
<value>Find</value>
270278
<comment>The tooltip for a button for searching for the selected text</comment>
271279
</data>
@@ -334,4 +342,4 @@ Please either install the missing font or choose another one.</value>
334342
<value>Suggested input: {0}</value>
335343
<comment>{Locked="{0}"} {0} will be replaced with a string of input that is suggested for the user to input</comment>
336344
</data>
337-
</root>
345+
</root>

src/cascadia/TerminalControl/TermControl.xaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
x:Uid="SelectOutputButton"
5353
Click="_SelectOutputHandler"
5454
Icon="AlignLeft" />
55+
<AppBarButton x:Name="SearchCommandButton"
56+
x:Uid="SearchCommandButton"
57+
Click="_SearchCommandHandler"
58+
Icon="Find" />
5559
</mux:CommandBarFlyout.SecondaryCommands>
5660
</mux:CommandBarFlyout>
5761

@@ -65,10 +69,6 @@
6569
Click="_PasteCommandHandler"
6670
Icon="Paste" />
6771
<mux:CommandBarFlyout.SecondaryCommands>
68-
<AppBarButton x:Name="SearchCommandButton"
69-
x:Uid="SearchCommandButton"
70-
Click="_SearchCommandHandler"
71-
Icon="Find" />
7272
<AppBarButton x:Name="SelectCommandWithSelectionButton"
7373
x:Uid="SelectCommandWithSelectionButton"
7474
Click="_SelectCommandHandler">
@@ -82,6 +82,10 @@
8282
x:Uid="SelectOutputWithSelectionButton"
8383
Click="_SelectOutputHandler"
8484
Icon="AlignLeft" />
85+
<AppBarButton x:Name="SearchCommandSelectionButton"
86+
x:Uid="SearchCommandSelectionButton"
87+
Click="_SearchCommandHandler"
88+
Icon="Find" />
8589
</mux:CommandBarFlyout.SecondaryCommands>
8690
</mux:CommandBarFlyout>
8791

src/cascadia/TerminalControl/TerminalControlLib.vcxproj

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
<ConfigurationType>StaticLibrary</ConfigurationType>
1010
<SubSystem>Console</SubSystem>
1111
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
12-
1312
<!-- C++/WinRT sets the depth to 1 if there is a XAML file in the project
1413
Unfortunately for us, we need it to be 3. When the namespace merging
1514
depth is 1, Microsoft.Terminal.Control becomes "Microsoft",
@@ -20,18 +19,14 @@
2019
-->
2120
<CppWinRTNamespaceMergeDepth>3</CppWinRTNamespaceMergeDepth>
2221
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
23-
2422
</PropertyGroup>
25-
2623
<PropertyGroup Label="NuGet Dependencies">
2724
<TerminalCppWinrt>true</TerminalCppWinrt>
2825
<TerminalMUX>true</TerminalMUX>
2926
</PropertyGroup>
30-
3127
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
3228
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
3329
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
34-
3530
<!-- ========================= Headers ======================== -->
3631
<ItemGroup>
3732
<ClInclude Include="pch.h" />
@@ -143,7 +138,9 @@
143138
</ItemGroup>
144139
<!-- ========================= Misc Files ======================== -->
145140
<ItemGroup>
146-
<PRIResource Include="Resources\en-US\Resources.resw" />
141+
<PRIResource Include="Resources\en-US\Resources.resw">
142+
<SubType>Designer</SubType>
143+
</PRIResource>
147144
<OCResourceDirectory Include="Resources" />
148145
</ItemGroup>
149146
<!-- ========================= Project References ======================== -->
@@ -183,9 +180,7 @@
183180
</ItemDefinitionGroup>
184181
<!-- ========================= Globals ======================== -->
185182
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
186-
187183
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
188184
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
189-
190185
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
191-
</Project>
186+
</Project>

0 commit comments

Comments
 (0)