Merge remote-tracking branch 'origin/main' into dev/migrie/oop/infinity-war

This commit is contained in:
Mike Griese 2021-08-24 10:05:34 -05:00
commit 901bc78966
176 changed files with 8097 additions and 1803 deletions

View file

@ -33,6 +33,7 @@ kje
liga
lje
locl
lorem
maxed
mkmk
mru
@ -59,6 +60,7 @@ TLDR
tokenizes
tonos
tshe
uiatextrange
UIs
und
unregister

View file

@ -2,11 +2,13 @@ ACCEPTFILES
ACCESSDENIED
alignas
alignof
APPLYTOSUBMENUS
bitfield
bitfields
BUILDBRANCH
BUILDMSG
BUILDNUMBER
BYPOSITION
charconv
CLASSNOTAVAILABLE
cmdletbinding
@ -78,7 +80,11 @@ llu
localtime
lround
LSHIFT
MENUCOMMAND
MENUDATA
MENUINFO
memicmp
mptt
mov
msappx
MULTIPLEUSE
@ -93,6 +99,7 @@ NOCHANGEDIR
NOPROGRESS
NOREDIRECTIONBITMAP
NOREPEAT
NOTIFYBYPOS
NOTIFYICON
NOTIFYICONDATA
ntprivapi
@ -140,6 +147,7 @@ Stubless
Subheader
Subpage
syscall
TASKBARCREATED
TBPF
THEMECHANGED
tlg

View file

@ -31,6 +31,7 @@ Kourosh
kowalczyk
leonmsft
Lepilleur
lhecker
lukesampson
Manandhar
mbadolato
@ -66,6 +67,7 @@ sonpham
stakx
thereses
Walisch
Wellons
Wirt
Wojciech
zadjii

View file

@ -514,6 +514,7 @@ DECAWM
DECCKM
DECCOLM
DECDHL
decdld
DECDLD
DECDWL
DECEKBD
@ -787,6 +788,7 @@ flyout
fmodern
fmtarg
fmtid
FNV
FOLDERID
FONTCHANGE
fontdlg
@ -1468,6 +1470,7 @@ Mul
multiline
munged
munges
murmurhash
mutex
mutexes
muxes
@ -2266,6 +2269,7 @@ SWMR
SWP
swprintf
SYMED
symlink
SYNCPAINT
sys
syscalls
@ -2797,6 +2801,7 @@ xes
xff
XFile
XFORM
xIcon
XManifest
XMath
XMFLOAT
@ -2829,6 +2834,7 @@ YCast
YCENTER
YCount
YDPI
yIcon
yml
YOffset
YPosition

View file

@ -154,6 +154,17 @@
"description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"",
"type": "string"
},
"intenseTextStyle": {
"default": "bright",
"description": "Controls how 'intense' text is rendered. Values are \"bold\", \"bright\", \"all\" and \"none\"",
"enum": [
"none",
"bold",
"bright",
"all"
],
"type": "string"
},
"experimental.retroTerminalEffect": {
"description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.",
"type": "boolean"
@ -239,6 +250,7 @@
"identifyWindows",
"moveFocus",
"movePane",
"swapPane",
"moveTab",
"newTab",
"newWindow",
@ -285,7 +297,9 @@
"right",
"up",
"down",
"previous"
"previous",
"nextInOrder",
"previousInOrder"
],
"type": "string"
},
@ -493,6 +507,23 @@
"type": "integer",
"default": 0,
"description": "Which tab to switch to, with the first being 0"
}
}
}
],
"required": [ "index" ]
},
"MovePaneAction": {
"description": "Arguments corresponding to a Move Pane Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "movePane" },
"index": {
"type": "integer",
"default": 0,
"description": "Which tab to move the pane to, with the first being 0"
}
}
}
@ -509,24 +540,24 @@
"direction": {
"$ref": "#/definitions/FocusDirection",
"default": "left",
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane."
"description": "The direction to move focus in, between panes. Direction can be 'previous' to move to the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
}
}
}
],
"required": [ "direction" ]
},
"MovePaneAction": {
"description": "Arguments corresponding to a Move Pane Action",
"SwapPaneAction": {
"description": "Arguments corresponding to a Swap Pane Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "movePane" },
"action": { "type": "string", "pattern": "swapPane" },
"direction": {
"$ref": "#/definitions/FocusDirection",
"default": "left",
"description": "The direction to move the focus pane in, swapping panes. Direction can be 'previous' to swap with the most recently used pane."
"description": "The direction to move the focus pane in, swapping panes. Direction can be 'previous' to swap with the most recently used pane, or 'nextInOrder' or 'previousInOrder' to move to the next or previous pane."
}
}
}
@ -972,6 +1003,7 @@
{ "$ref": "#/definitions/SwitchToTabAction" },
{ "$ref": "#/definitions/MoveFocusAction" },
{ "$ref": "#/definitions/MovePaneAction" },
{ "$ref": "#/definitions/SwapPaneAction" },
{ "$ref": "#/definitions/ResizePaneAction" },
{ "$ref": "#/definitions/SendInputAction" },
{ "$ref": "#/definitions/SplitPaneAction" },
@ -1182,6 +1214,16 @@
"minimum": 0,
"type": [ "integer", "string" ],
"deprecated": true
},
"minimizeToTray": {
"default": "false",
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the system tray through which the user can access their windows.",
"type": "boolean"
},
"alwaysShowTrayIcon": {
"default": "false",
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
"type": "boolean"
},
"actions": {
"description": "Properties are specific to each custom action.",

View file

@ -140,12 +140,12 @@
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>

View file

@ -45,6 +45,7 @@ namespace winrt::SampleApp::implementation
WINRT_PROPERTY(bool, TrimBlockSelection, false);
WINRT_PROPERTY(bool, DetectURLs, true);
WINRT_PROPERTY(bool, IntenseIsBright, true);
// ------------------------ End of Core Settings -----------------------
WINRT_PROPERTY(winrt::hstring, ProfileName);
@ -85,6 +86,8 @@ namespace winrt::SampleApp::implementation
WINRT_PROPERTY(IFontFeatureMap, FontFeatures);
WINRT_PROPERTY(IFontAxesMap, FontAxes);
WINRT_PROPERTY(bool, IntenseIsBold, true);
private:
std::array<winrt::Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE> _ColorTable;

View file

@ -147,13 +147,13 @@
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View file

@ -80,13 +80,13 @@
</ItemGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
</packages>

View file

@ -120,14 +120,14 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />

View file

@ -2,6 +2,6 @@
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
</packages>

View file

@ -95,16 +95,18 @@ bool TextAttribute::IsLegacy() const noexcept
// - defaultFgColor: the default foreground color rgb value.
// - defaultBgColor: the default background color rgb value.
// - reverseScreenMode: true if the screen mode is reversed.
// - blinkingIsFaint: true if blinking should be interpreted as faint.
// - blinkingIsFaint: true if blinking should be interpreted as faint. (defaults to false)
// - boldIsBright: true if "bold" should be interpreted as bright. (defaults to true)
// Return Value:
// - the foreground and background colors that should be displayed.
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
const COLORREF defaultFgColor,
const COLORREF defaultBgColor,
const bool reverseScreenMode,
const bool blinkingIsFaint) const noexcept
const bool blinkingIsFaint,
const bool boldIsBright) const noexcept
{
auto fg = _foreground.GetColor(colorTable, defaultFgColor, IsBold());
auto fg = _foreground.GetColor(colorTable, defaultFgColor, boldIsBright && IsBold());
auto bg = _background.GetColor(colorTable, defaultBgColor);
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
{

View file

@ -68,7 +68,8 @@ public:
const COLORREF defaultFgColor,
const COLORREF defaultBgColor,
const bool reverseScreenMode = false,
const bool blinkingIsFaint = false) const noexcept;
const bool blinkingIsFaint = false,
const bool boldIsBright = true) const noexcept;
bool IsLeadingByte() const noexcept;
bool IsTrailingByte() const noexcept;

View file

@ -22,6 +22,7 @@ class TextAttributeTests
TEST_METHOD(TestTextAttributeColorGetters);
TEST_METHOD(TestReverseDefaultColors);
TEST_METHOD(TestRoundtripDefaultColors);
TEST_METHOD(TestBoldAsBright);
std::array<COLORREF, 256> _colorTable;
COLORREF _defaultFg = RGB(1, 2, 3);
@ -263,3 +264,56 @@ void TextAttributeTests::TestRoundtripDefaultColors()
// Reset the legacy default colors to white on black.
TextAttribute::SetLegacyDefaultAttributes(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
void TextAttributeTests::TestBoldAsBright()
{
const COLORREF darkBlack = til::at(_colorTable, 0);
const COLORREF brightBlack = til::at(_colorTable, 8);
const COLORREF darkGreen = til::at(_colorTable, 2);
TextAttribute attr{};
// verify that calculated foreground/background are the same as the direct
// values when not bold
VERIFY_IS_FALSE(attr.IsBold());
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
// with bold set, calculated foreground/background values shouldn't change for the default colors.
attr.SetBold(true);
VERIFY_IS_TRUE(attr.IsBold());
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
attr.SetIndexedForeground(0);
VERIFY_IS_TRUE(attr.IsBold());
Log::Comment(L"Foreground should be bright black when bold is bright is enabled");
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
Log::Comment(L"Foreground should be dark black when bold is bright is disabled");
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
attr.SetIndexedBackground(2);
VERIFY_IS_TRUE(attr.IsBold());
Log::Comment(L"background should be unaffected by 'bold is bright'");
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
attr.SetBold(false);
VERIFY_IS_FALSE(attr.IsBold());
Log::Comment(L"when not bold, 'bold is bright' changes nothing");
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing");
attr.SetBold(true);
attr.SetIndexedForeground(8);
VERIFY_IS_TRUE(attr.IsBold());
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
}

View file

@ -146,12 +146,12 @@
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />

View file

@ -41,6 +41,10 @@ namespace SettingsModelLocalTests
TEST_METHOD(LayerKeybindings);
TEST_METHOD(UnbindKeybindings);
TEST_METHOD(LayerScancodeKeybindings);
TEST_METHOD(TestExplicitUnbind);
TEST_METHOD(TestArbitraryArgs);
TEST_METHOD(TestSplitPaneArgs);
@ -54,6 +58,7 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestMoveTabArgs);
TEST_METHOD(TestGetKeyBindingForAction);
TEST_METHOD(KeybindingsWithoutVkey);
TEST_CLASS_SETUP(ClassSetup)
{
@ -95,23 +100,33 @@ namespace SettingsModelLocalTests
VirtualKeyModifiers::Control | VirtualKeyModifiers::Menu | VirtualKeyModifiers::Shift | VirtualKeyModifiers::Windows,
255,
0,
L"ctrl+shift+alt+win+vk(255)",
L"win+ctrl+alt+shift+vk(255)",
},
testCase{
VirtualKeyModifiers::Windows,
VirtualKeyModifiers::Control | VirtualKeyModifiers::Menu | VirtualKeyModifiers::Shift | VirtualKeyModifiers::Windows,
0,
123,
L"ctrl+shift+alt+win+sc(123)",
L"win+ctrl+alt+shift+sc(123)",
},
};
for (const auto& tc : testCases)
{
KeyChord expectedKeyChord{ tc.modifiers, tc.vkey, tc.scanCode };
const auto actualString = KeyChordSerialization::ToString(expectedKeyChord);
Log::Comment(NoThrowString().Format(L"Testing case:\"%s\"", tc.expected.data()));
const auto actualString = KeyChordSerialization::ToString({ tc.modifiers, tc.vkey, tc.scanCode });
VERIFY_ARE_EQUAL(tc.expected, actualString);
auto expectedVkey = tc.vkey;
if (!expectedVkey)
{
expectedVkey = MapVirtualKeyW(tc.scanCode, MAPVK_VSC_TO_VK_EX);
}
const auto actualKeyChord = KeyChordSerialization::FromString(actualString);
VERIFY_ARE_EQUAL(expectedKeyChord, actualKeyChord);
VERIFY_ARE_EQUAL(tc.modifiers, actualKeyChord.Modifiers());
VERIFY_ARE_EQUAL(expectedVkey, actualKeyChord.Vkey());
VERIFY_ARE_EQUAL(tc.scanCode, actualKeyChord.ScanCode());
}
}
@ -232,6 +247,31 @@ namespace SettingsModelLocalTests
VERIFY_IS_NULL(actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 }));
}
void KeyBindingsTests::TestExplicitUnbind()
{
const std::string bindings0String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
const std::string bindings1String{ R"([ { "command": "unbound", "keys": ["ctrl+c"] } ])" };
const std::string bindings2String{ R"([ { "command": "copy", "keys": ["ctrl+c"] } ])" };
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
const KeyChord keyChord{ VirtualKeyModifiers::Control, static_cast<int32_t>('C'), 0 };
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
actionMap->LayerJson(bindings0Json);
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
actionMap->LayerJson(bindings1Json);
VERIFY_IS_TRUE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
actionMap->LayerJson(bindings2Json);
VERIFY_IS_FALSE(actionMap->IsKeyChordExplicitlyUnbound(keyChord));
}
void KeyBindingsTests::TestArbitraryArgs()
{
const std::string bindings0String{ R"([
@ -722,4 +762,42 @@ namespace SettingsModelLocalTests
VerifyKeyChordEquality({ VirtualKeyModifiers::Control | VirtualKeyModifiers::Shift, static_cast<int32_t>('P'), 0 }, kbd);
}
}
void KeyBindingsTests::LayerScancodeKeybindings()
{
Log::Comment(L"Layering a keybinding with a character literal on top of"
L" an equivalent sc() key should replace it.");
// Wrap the first one in `R"!(...)!"` because it has `()` internally.
const std::string bindings0String{ R"!([ { "command": "quakeMode", "keys":"win+sc(41)" } ])!" };
const std::string bindings1String{ R"([ { "keys": "win+`", "command": { "action": "globalSummon", "monitor": "any" } } ])" };
const std::string bindings2String{ R"([ { "keys": "ctrl+shift+`", "command": { "action": "quakeMode" } } ])" };
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
auto actionMap = winrt::make_self<implementation::ActionMap>();
VERIFY_ARE_EQUAL(0u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size());
actionMap->LayerJson(bindings1Json);
VERIFY_ARE_EQUAL(1u, actionMap->_KeyMap.size(), L"Layering the second action should replace the first one.");
actionMap->LayerJson(bindings2Json);
VERIFY_ARE_EQUAL(2u, actionMap->_KeyMap.size());
}
void KeyBindingsTests::KeybindingsWithoutVkey()
{
const auto json = VerifyParseSucceeded(R"!([{"command": "quakeMode", "keys":"shift+sc(255)"}])!");
const auto actionMap = winrt::make_self<implementation::ActionMap>();
actionMap->LayerJson(json);
const auto action = actionMap->GetActionByKeyChord({ VirtualKeyModifiers::Shift, 0, 255 });
VERIFY_IS_NOT_NULL(action);
}
}

View file

@ -98,10 +98,10 @@
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<!-- We actually can just straight up reference MUX here, it's fine -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
</Project>

View file

@ -37,7 +37,7 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestTerminalArgsForBinding);
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
TEST_METHOD(MakeSettingsForProfile);
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
TEST_METHOD(TestLayerProfileOnColorScheme);
@ -126,10 +126,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
@ -148,10 +148,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
}
@ -170,10 +170,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
}
@ -192,10 +192,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, guid);
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
}
@ -214,10 +214,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
@ -237,10 +237,10 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
}
@ -257,10 +257,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
@ -278,10 +278,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
@ -301,10 +301,10 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, guid);
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
@ -323,10 +323,10 @@ namespace SettingsModelLocalTests
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid0, guid);
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
@ -346,10 +346,10 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(profile2Guid, guid);
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
VERIFY_ARE_EQUAL(3, termSettings.HistorySize());
@ -371,10 +371,10 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
VERIFY_ARE_EQUAL(guid1, guid);
VERIFY_ARE_EQUAL(guid1, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle());
VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory());
@ -382,9 +382,9 @@ namespace SettingsModelLocalTests
}
}
void TerminalSettingsTests::MakeSettingsForProfileThatDoesntExist()
void TerminalSettingsTests::MakeSettingsForProfile()
{
// Test that making settings throws when the GUID doesn't exist
// Test that making settings generally works.
const std::string settingsString{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
@ -405,32 +405,32 @@ namespace SettingsModelLocalTests
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
const auto guid3 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
const auto profile1 = settings.FindProfile(guid1);
const auto profile2 = settings.FindProfile(guid2);
try
{
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) };
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile1, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
}
try
{
auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) };
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile2, nullptr) };
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
}
catch (...)
{
VERIFY_IS_TRUE(false, L"This call to CreateWithProfileByID should succeed");
VERIFY_IS_TRUE(false, L"This call to CreateWithProfile should succeed");
}
VERIFY_THROWS(auto terminalSettings = TerminalSettings::CreateWithProfileByID(settings, guid3, nullptr), wil::ResultException, L"This call to constructor should fail");
try
{
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };

View file

@ -56,7 +56,7 @@ namespace TerminalAppLocalTests
TEST_METHOD(ParseComboCommandlineIntoArgs);
TEST_METHOD(ParseFocusTabArgs);
TEST_METHOD(ParseMoveFocusArgs);
TEST_METHOD(ParseMovePaneArgs);
TEST_METHOD(ParseSwapPaneArgs);
TEST_METHOD(ParseArgumentsWithParsingTerminators);
TEST_METHOD(ParseFocusPaneArgs);
@ -1208,14 +1208,9 @@ namespace TerminalAppLocalTests
}
}
void CommandlineTest::ParseMovePaneArgs()
void CommandlineTest::ParseSwapPaneArgs()
{
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:useShortForm", L"{false, true}")
END_TEST_METHOD_PROPERTIES()
INIT_TEST_PROPERTY(bool, useShortForm, L"If true, use `mp` instead of `move-pane`");
const wchar_t* subcommand = useShortForm ? L"mp" : L"move-pane";
const wchar_t* subcommand = L"swap-pane";
{
AppCommandlineArgs appArgs{};
@ -1236,9 +1231,9 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
}
@ -1253,9 +1248,9 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
}
@ -1270,9 +1265,9 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(FocusDirection::Up, myArgs.Direction());
}
@ -1287,9 +1282,9 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(FocusDirection::Down, myArgs.Direction());
}
@ -1311,16 +1306,16 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
auto actionAndArgs = appArgs._startupActions.at(1);
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
auto myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(FocusDirection::Left, myArgs.Direction());
actionAndArgs = appArgs._startupActions.at(2);
VERIFY_ARE_EQUAL(ShortcutAction::MovePane, actionAndArgs.Action());
VERIFY_ARE_EQUAL(ShortcutAction::SwapPane, actionAndArgs.Action());
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
myArgs = actionAndArgs.Args().try_as<MovePaneArgs>();
myArgs = actionAndArgs.Args().try_as<SwapPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(FocusDirection::Right, myArgs.Direction());
}

View file

@ -82,7 +82,7 @@ namespace TerminalAppLocalTests
TEST_METHOD(MoveFocusFromZoomedPane);
TEST_METHOD(CloseZoomedPane);
TEST_METHOD(MovePanes);
TEST_METHOD(SwapPanes);
TEST_METHOD(NextMRUTab);
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
@ -430,7 +430,7 @@ namespace TerminalAppLocalTests
Log::Comment(L"Duplicate the tab, and don't crash");
result = RunOnUIThread([&page]() {
page->_DuplicateFocusedTab();
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
VERIFY_ARE_EQUAL(3u, page->_tabs.Size(), L"We should successfully duplicate a tab hosting a deleted profile.");
});
VERIFY_SUCCEEDED(result);
}
@ -530,9 +530,9 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
VERIFY_ARE_EQUAL(2,
VERIFY_ARE_EQUAL(3,
tab->GetLeafPaneCount(),
L"We should gracefully do nothing here - the profile no longer exists.");
L"We should successfully duplicate a pane hosting a deleted profile.");
});
VERIFY_SUCCEEDED(result);
@ -821,7 +821,7 @@ namespace TerminalAppLocalTests
VERIFY_SUCCEEDED(result);
}
void TabTests::MovePanes()
void TabTests::SwapPanes()
{
auto page = _commonSetup();
@ -914,10 +914,10 @@ namespace TerminalAppLocalTests
// -------------------
TestOnUIThread([&]() {
// Set up action
MovePaneArgs args{ FocusDirection::Left };
SwapPaneArgs args{ FocusDirection::Left };
ActionEventArgs eventArgs{ args };
page->_HandleMovePane(nullptr, eventArgs);
page->_HandleSwapPane(nullptr, eventArgs);
});
Sleep(250);
@ -945,10 +945,10 @@ namespace TerminalAppLocalTests
// -------------------
TestOnUIThread([&]() {
// Set up action
MovePaneArgs args{ FocusDirection::Up };
SwapPaneArgs args{ FocusDirection::Up };
ActionEventArgs eventArgs{ args };
page->_HandleMovePane(nullptr, eventArgs);
page->_HandleSwapPane(nullptr, eventArgs);
});
Sleep(250);
@ -976,10 +976,10 @@ namespace TerminalAppLocalTests
// -------------------
TestOnUIThread([&]() {
// Set up action
MovePaneArgs args{ FocusDirection::Right };
SwapPaneArgs args{ FocusDirection::Right };
ActionEventArgs eventArgs{ args };
page->_HandleMovePane(nullptr, eventArgs);
page->_HandleSwapPane(nullptr, eventArgs);
});
Sleep(250);
@ -1007,10 +1007,10 @@ namespace TerminalAppLocalTests
// -------------------
TestOnUIThread([&]() {
// Set up action
MovePaneArgs args{ FocusDirection::Down };
SwapPaneArgs args{ FocusDirection::Down };
ActionEventArgs eventArgs{ args };
page->_HandleMovePane(nullptr, eventArgs);
page->_HandleSwapPane(nullptr, eventArgs);
});
Sleep(250);

View file

@ -92,11 +92,11 @@
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<!-- We actually can just straight up reference MUX here, it's fine -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
</Project>

View file

@ -123,7 +123,7 @@
</Reference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)\src\common.build.post.props" />

View file

@ -28,8 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
CATCH_LOG();
}
// This is a private constructor to be used in unit tests, where we don't
// want each Monarch to necessarily use the current PID.
// This constructor is intended to be used in unit tests,
// but we need to make it public in order to use make_self
// in the tests. It's not exposed through the idl though
// so it's not _truly_ fully public which should be acceptable.
Monarch::Monarch(const uint64_t testPID) :
_ourPID{ testPID }
{
@ -78,6 +80,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
peasant.RenameRequested({ this, &Monarch::_renameRequested });
peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
_peasants[newPeasantsId] = peasant;
TraceLoggingWrite(g_hRemotingProvider,
@ -201,6 +206,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_clearOldMruEntries(id);
}
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_lookupPeasantIdForName",
TraceLoggingWideString(std::wstring{ name }.c_str(), "name", "the name we're looking for"),
TraceLoggingUInt64(result, "peasantID", "the ID of the peasant with that name"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
return result;
}
@ -732,24 +743,55 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
try
{
args.FoundMatch(false);
// If a WindowID is provided from the args, use that first.
uint64_t windowId = 0;
// If no name was provided, then just summon the MRU window.
if (searchedForName.empty())
if (args.WindowID())
{
// Use the value of the `desktop` arg to determine if we should
// limit to the current desktop (desktop:onCurrent) or not
// (desktop:any or desktop:toCurrent)
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
windowId = args.WindowID().Value();
}
else
{
// Try to find a peasant that currently has this name
windowId = _lookupPeasantIdForName(searchedForName);
// If no name was provided, then just summon the MRU window.
if (searchedForName.empty())
{
// Use the value of the `desktop` arg to determine if we should
// limit to the current desktop (desktop:onCurrent) or not
// (desktop:any or desktop:toCurrent)
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop(), false);
}
else
{
// Try to find a peasant that currently has this name
windowId = _lookupPeasantIdForName(searchedForName);
}
}
if (auto targetPeasant{ _getPeasant(windowId) })
{
targetPeasant.Summon(args.SummonBehavior());
args.FoundMatch(true);
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SummonWindow_Success",
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"),
TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"),
TraceLoggingBoolean(args.SummonBehavior().MoveToCurrentDesktop(), "MoveToCurrentDesktop", "if true, move the window to the current virtual desktop"),
TraceLoggingBoolean(args.SummonBehavior().ToggleVisibility(), "ToggleVisibility", "true if we should toggle the visibility of the window"),
TraceLoggingUInt32(args.SummonBehavior().DropdownDuration(), "DropdownDuration", "the duration to dropdown the window"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
else
{
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SummonWindow_NoPeasant",
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"),
TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
catch (...)
@ -762,4 +804,56 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
// Method Description:
// - This method creates a map of peasant IDs to peasant names
// while removing dead peasants.
// Arguments:
// - <none>
// Return Value:
// - A map of peasant IDs to their names.
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> Monarch::GetPeasantNames()
{
auto names = winrt::single_threaded_map<uint64_t, winrt::hstring>();
std::vector<uint64_t> peasantsToErase{};
for (const auto& [id, p] : _peasants)
{
try
{
names.Insert(id, p.WindowName());
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
peasantsToErase.push_back(id);
}
}
// Remove the dead peasants we came across while iterating.
for (const auto& id : peasantsToErase)
{
_peasants.erase(id);
_clearOldMruEntries(id);
}
return names.GetView();
}
void Monarch::SummonAllWindows()
{
auto callback = [](auto&& p, auto&& /*id*/) {
SummonWindowBehavior args{};
args.ToggleVisibility(false);
p.Summon(args);
};
auto onError = [](auto&& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_SummonAll_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forAllPeasantsIgnoringTheDead(callback, onError);
}
}

View file

@ -41,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
struct Monarch : public MonarchT<Monarch>
{
Monarch();
Monarch(const uint64_t testPID);
~Monarch();
uint64_t GetPID();
@ -51,10 +52,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
void SummonAllWindows();
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
Monarch(const uint64_t testPID);
uint64_t _ourPID;
uint64_t _nextPeasantID{ 1 };

View file

@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Remoting
Boolean FoundMatch;
SummonWindowBehavior SummonBehavior;
Windows.Foundation.IReference<UInt64> WindowID;
}
@ -40,6 +41,11 @@ namespace Microsoft.Terminal.Remoting
void HandleActivatePeasant(WindowActivatedArgs args);
void SummonWindow(SummonWindowSelectionArgs args);
void SummonAllWindows();
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames { get; };
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
};
}

View file

@ -20,8 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
}
// This is a private constructor to be used in unit tests, where we don't
// want each Peasant to necessarily use the current PID.
// This constructor is intended to be used in unit tests,
// but we need to make it public in order to use make_self
// in the tests. It's not exposed through the idl though
// so it's not _truly_ fully public which should be acceptable.
Peasant::Peasant(const uint64_t testPID) :
_ourPID{ testPID }
{
@ -31,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
_id = id;
}
uint64_t Peasant::GetID()
{
return _id;
@ -222,4 +225,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestShowTrayIcon()
{
try
{
_ShowTrayIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestShowTrayIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestHideTrayIcon()
{
try
{
_HideTrayIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestHideTrayIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}

View file

@ -28,6 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestIdentifyWindows();
void DisplayWindowId();
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
@ -40,6 +42,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
Peasant(const uint64_t testPID);

View file

@ -64,6 +64,8 @@ namespace Microsoft.Terminal.Remoting
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
void Summon(SummonWindowBehavior behavior);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
@ -71,6 +73,8 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View file

@ -34,6 +34,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
WINRT_PROPERTY(bool, FoundMatch, false);
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, WindowID);
};
}

View file

@ -254,6 +254,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// window, and when the current monarch dies.
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
_BecameMonarchHandlers(*this, nullptr);
}
@ -509,4 +511,57 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_monarch.SummonWindow(args);
}
void WindowManager::SummonAllWindows()
{
if constexpr (Feature_TrayIcon::IsEnabled())
{
_monarch.SummonAllWindows();
}
}
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> WindowManager::GetPeasantNames()
{
// We should only get called when we're the monarch since the monarch
// is the only one that knows about all peasants.
return _monarch.GetPeasantNames();
}
// Method Description:
// - Ask the monarch to show a tray icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestShowTrayIcon()
{
co_await winrt::resume_background();
_peasant.RequestShowTrayIcon();
}
// Method Description:
// - Ask the monarch to hide its tray icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestHideTrayIcon()
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestHideTrayIcon();
}
bool WindowManager::DoesQuakeWindowExist()
{
const auto names = GetPeasantNames();
for (const auto [id, name] : names)
{
if (name == QuakeWindowName)
{
return true;
}
}
return false;
}
}

View file

@ -40,8 +40,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
bool IsMonarch();
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
void SummonAllWindows();
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
winrt::fire_and_forget RequestShowTrayIcon();
winrt::fire_and_forget RequestHideTrayIcon();
bool DoesQuakeWindowExist();
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
bool _shouldCreateWindow{ false };

View file

@ -12,7 +12,14 @@ namespace Microsoft.Terminal.Remoting
IPeasant CurrentWindow();
Boolean IsMonarch { get; };
void SummonWindow(SummonWindowSelectionArgs args);
void SummonAllWindows();
void RequestShowTrayIcon();
void RequestHideTrayIcon();
Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames();
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
};
}

View file

@ -20,7 +20,8 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Include the MUX Controls resources -->
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"
ControlsResourcesVersion="Version1" />
<ResourceDictionary>
<!--
@ -55,12 +56,18 @@
<!-- Define resources for Dark mode here -->
<SolidColorBrush x:Key="TabViewBackground"
Color="#FF333333" />
<SolidColorBrush x:Key="UnfocusedBorderBrush"
Color="#FF333333" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<!-- Define resources for Light mode here -->
<SolidColorBrush x:Key="TabViewBackground"
Color="#FFCCCCCC" />
<SolidColorBrush x:Key="UnfocusedBorderBrush"
Color="#FFCCCCCC" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

View file

@ -143,6 +143,20 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args == nullptr)
{
args.Handled(false);
}
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
{
auto moved = _MovePane(realArgs.TabIndex());
args.Handled(moved);
}
}
void TerminalPage::_HandleSplitPane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
@ -323,10 +337,10 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleMovePane(const IInspectable& /*sender*/,
void TerminalPage::_HandleSwapPane(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
if (const auto& realArgs = args.ActionArgs().try_as<SwapPaneArgs>())
{
if (realArgs.Direction() == FocusDirection::None)
{
@ -335,8 +349,8 @@ namespace winrt::TerminalApp::implementation
}
else
{
_MovePane(realArgs.Direction());
args.Handled(true);
auto swapped = _SwapPane(realArgs.Direction());
args.Handled(swapped);
}
}
}
@ -739,11 +753,10 @@ namespace winrt::TerminalApp::implementation
newTerminalArgs = NewTerminalArgs();
}
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
// Manually fill in the evaluated profile.
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
_OpenNewWindow(false, newTerminalArgs);
actionArgs.Handled(true);
}

View file

@ -193,6 +193,7 @@ void AppCommandlineArgs::_buildParser()
_buildFocusTabParser();
_buildMoveFocusParser();
_buildMovePaneParser();
_buildSwapPaneParser();
_buildFocusPaneParser();
}
@ -297,6 +298,43 @@ void AppCommandlineArgs::_buildSplitPaneParser()
setupSubcommand(_newPaneCommand);
setupSubcommand(_newPaneShort);
}
// Method Description:
// - Adds the `move-pane` subcommand and related options to the commandline parser.
// - Additionally adds the `mp` subcommand, which is just a shortened version of `move-pane`
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppCommandlineArgs::_buildMovePaneParser()
{
_movePaneCommand = _app.add_subcommand("move-pane", RS_A(L"CmdMovePaneDesc"));
_movePaneShort = _app.add_subcommand("mp", RS_A(L"CmdMPDesc"));
auto setupSubcommand = [this](auto* subcommand) {
subcommand->add_option("-t,--tab",
_movePaneTabIndex,
RS_A(L"CmdMovePaneTabArgDesc"));
// When ParseCommand is called, if this subcommand was provided, this
// callback function will be triggered on the same thread. We can be sure
// that `this` will still be safe - this function just lets us know this
// command was parsed.
subcommand->callback([&, this]() {
// Build the action from the values we've parsed on the commandline.
ActionAndArgs movePaneAction{};
if (_movePaneTabIndex >= 0)
{
movePaneAction.Action(ShortcutAction::MovePane);
MovePaneArgs args{ static_cast<unsigned int>(_movePaneTabIndex) };
movePaneAction.Args(args);
_startupActions.push_back(movePaneAction);
}
});
};
setupSubcommand(_movePaneCommand);
setupSubcommand(_movePaneShort);
}
// Method Description:
// - Adds the `focus-tab` subcommand and related options to the commandline parser.
@ -342,6 +380,11 @@ void AppCommandlineArgs::_buildFocusTabParser()
else if (_focusNextTab || _focusPrevTab)
{
focusTabAction.Action(_focusNextTab ? ShortcutAction::NextTab : ShortcutAction::PrevTab);
// GH#10070 - make sure to not use the MRU order when switching
// tabs on the commandline. That wouldn't make any sense!
focusTabAction.Args(_focusNextTab ?
static_cast<IActionArgs>(NextTabArgs(TabSwitcherMode::Disabled)) :
static_cast<IActionArgs>(PrevTabArgs(TabSwitcherMode::Disabled)));
_startupActions.push_back(std::move(focusTabAction));
}
});
@ -351,6 +394,15 @@ void AppCommandlineArgs::_buildFocusTabParser()
setupSubcommand(_focusTabShort);
}
static const std::map<std::string, FocusDirection> focusDirectionMap = {
{ "left", FocusDirection::Left },
{ "right", FocusDirection::Right },
{ "up", FocusDirection::Up },
{ "down", FocusDirection::Down },
{ "nextInOrder", FocusDirection::NextInOrder },
{ "previousInOrder", FocusDirection::PreviousInOrder },
};
// Method Description:
// - Adds the `move-focus` subcommand and related options to the commandline parser.
// - Additionally adds the `mf` subcommand, which is just a shortened version of `move-focus`
@ -364,18 +416,11 @@ void AppCommandlineArgs::_buildMoveFocusParser()
_moveFocusShort = _app.add_subcommand("mf", RS_A(L"CmdMFDesc"));
auto setupSubcommand = [this](auto* subcommand) {
std::map<std::string, FocusDirection> map = {
{ "left", FocusDirection::Left },
{ "right", FocusDirection::Right },
{ "up", FocusDirection::Up },
{ "down", FocusDirection::Down }
};
auto* directionOpt = subcommand->add_option("direction",
_moveFocusDirection,
RS_A(L"CmdMoveFocusDirectionArgDesc"));
directionOpt->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
directionOpt->required();
// When ParseCommand is called, if this subcommand was provided, this
// callback function will be triggered on the same thread. We can be sure
@ -400,42 +445,33 @@ void AppCommandlineArgs::_buildMoveFocusParser()
}
// Method Description:
// - Adds the `move-pane` subcommand and related options to the commandline parser.
// - Additionally adds the `mp` subcommand, which is just a shortened version of `move-pane`
// - Adds the `swap-pane` subcommand and related options to the commandline parser.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppCommandlineArgs::_buildMovePaneParser()
void AppCommandlineArgs::_buildSwapPaneParser()
{
_movePaneCommand = _app.add_subcommand("move-pane", RS_A(L"CmdMovePaneDesc"));
_movePaneShort = _app.add_subcommand("mp", RS_A(L"CmdMPDesc"));
_swapPaneCommand = _app.add_subcommand("swap-pane", RS_A(L"CmdSwapPaneDesc"));
auto setupSubcommand = [this](auto* subcommand) {
std::map<std::string, FocusDirection> map = {
{ "left", FocusDirection::Left },
{ "right", FocusDirection::Right },
{ "up", FocusDirection::Up },
{ "down", FocusDirection::Down }
};
auto* directionOpt = subcommand->add_option("direction",
_movePaneDirection,
RS_A(L"CmdMovePaneDirectionArgDesc"));
_swapPaneDirection,
RS_A(L"CmdSwapPaneDirectionArgDesc"));
directionOpt->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
directionOpt->transform(CLI::CheckedTransformer(focusDirectionMap, CLI::ignore_case));
directionOpt->required();
// When ParseCommand is called, if this subcommand was provided, this
// callback function will be triggered on the same thread. We can be sure
// that `this` will still be safe - this function just lets us know this
// command was parsed.
subcommand->callback([&, this]() {
if (_movePaneDirection != FocusDirection::None)
if (_swapPaneDirection != FocusDirection::None)
{
MovePaneArgs args{ _movePaneDirection };
SwapPaneArgs args{ _swapPaneDirection };
ActionAndArgs actionAndArgs{};
actionAndArgs.Action(ShortcutAction::MovePane);
actionAndArgs.Action(ShortcutAction::SwapPane);
actionAndArgs.Args(args);
_startupActions.push_back(std::move(actionAndArgs));
@ -443,8 +479,7 @@ void AppCommandlineArgs::_buildMovePaneParser()
});
};
setupSubcommand(_movePaneCommand);
setupSubcommand(_movePaneShort);
setupSubcommand(_swapPaneCommand);
}
// Method Description:
@ -568,6 +603,13 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
args.Profile(winrt::to_hstring(_profileName));
}
if (!*subcommand.profileNameOption && !_commandline.empty())
{
// If there's no profile, but there IS a command line, set the tab title to the first part of the command
// This will ensure that the tab we spawn has a name (since it didn't get one from its profile!)
args.TabTitle(winrt::to_hstring(til::at(_commandline, 0)));
}
if (*subcommand.startingDirectoryOption)
{
args.StartingDirectory(winrt::to_hstring(_startingDirectory));
@ -625,6 +667,7 @@ bool AppCommandlineArgs::_noCommandsProvided()
*_moveFocusShort ||
*_movePaneCommand ||
*_movePaneShort ||
*_swapPaneCommand ||
*_focusPaneCommand ||
*_focusPaneShort ||
*_newPaneShort.subcommand ||
@ -653,12 +696,13 @@ void AppCommandlineArgs::_resetStateToDefault()
_splitPaneSize = 0.5f;
_splitDuplicate = false;
_movePaneTabIndex = -1;
_focusTabIndex = -1;
_focusNextTab = false;
_focusPrevTab = false;
_moveFocusDirection = FocusDirection::None;
_movePaneDirection = FocusDirection::None;
_swapPaneDirection = FocusDirection::None;
_focusPaneTarget = -1;

View file

@ -84,6 +84,7 @@ private:
CLI::App* _moveFocusShort;
CLI::App* _movePaneCommand;
CLI::App* _movePaneShort;
CLI::App* _swapPaneCommand;
CLI::App* _focusPaneCommand;
CLI::App* _focusPaneShort;
@ -97,7 +98,7 @@ private:
bool _suppressApplicationTitle{ false };
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _moveFocusDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _movePaneDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _swapPaneDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
// _commandline will contain the command line with which we'll be spawning a new terminal
std::vector<std::string> _commandline;
@ -107,6 +108,7 @@ private:
bool _splitDuplicate{ false };
float _splitPaneSize{ 0.5f };
int _movePaneTabIndex{ -1 };
int _focusTabIndex{ -1 };
bool _focusNextTab{ false };
bool _focusPrevTab{ false };
@ -132,6 +134,7 @@ private:
void _buildFocusTabParser();
void _buildMoveFocusParser();
void _buildMovePaneParser();
void _buildSwapPaneParser();
void _buildFocusPaneParser();
bool _noCommandsProvided();
void _resetStateToDefault();

View file

@ -21,6 +21,11 @@ namespace winrt::TerminalApp::implementation
return false;
}
bool AppKeyBindings::IsKeyChordExplicitlyUnbound(const KeyChord& kc)
{
return _actionMap.IsKeyChordExplicitlyUnbound(kc);
}
void AppKeyBindings::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
{
_dispatch = dispatch;

View file

@ -20,6 +20,7 @@ namespace winrt::TerminalApp::implementation
AppKeyBindings() = default;
bool TryKeyChord(winrt::Microsoft::Terminal::Control::KeyChord const& kc);
bool IsKeyChordExplicitlyUnbound(winrt::Microsoft::Terminal::Control::KeyChord const& kc);
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);

View file

@ -1129,28 +1129,11 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// - Gets the taskbar state value from the last active control
// Return Value:
// - The taskbar state of the last active control
uint64_t AppLogic::GetLastActiveControlTaskbarState()
winrt::TerminalApp::TaskbarState AppLogic::TaskbarState()
{
if (_root)
{
return _root->GetLastActiveControlTaskbarState();
}
return {};
}
// Method Description:
// - Gets the taskbar progress value from the last active control
// Return Value:
// - The taskbar progress of the last active control
uint64_t AppLogic::GetLastActiveControlTaskbarProgress()
{
if (_root)
{
return _root->GetLastActiveControlTaskbarProgress();
return _root->TaskbarState();
}
return {};
}
@ -1459,4 +1442,39 @@ namespace winrt::TerminalApp::implementation
return _root->IsQuakeWindow();
}
bool AppLogic::GetMinimizeToTray()
{
if constexpr (Feature_TrayIcon::IsEnabled())
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}
return _settings.GlobalSettings().MinimizeToTray();
}
else
{
return false;
}
}
bool AppLogic::GetAlwaysShowTrayIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
{
if (!_loadedInitialSettings)
{
// Load settings if we haven't already
LoadSettings();
}
return _settings.GlobalSettings().AlwaysShowTrayIcon();
}
else
{
return false;
}
}
}

View file

@ -90,8 +90,10 @@ namespace winrt::TerminalApp::implementation
void WindowCloseButtonClicked();
uint64_t GetLastActiveControlTaskbarState();
uint64_t GetLastActiveControlTaskbarProgress();
winrt::TerminalApp::TaskbarState TaskbarState();
bool GetMinimizeToTray();
bool GetAlwaysShowTrayIcon();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);

View file

@ -68,8 +68,10 @@ namespace TerminalApp
void TitlebarClicked();
void WindowCloseButtonClicked();
UInt64 GetLastActiveControlTaskbarState();
UInt64 GetLastActiveControlTaskbarProgress();
TaskbarState TaskbarState{ get; };
Boolean GetMinimizeToTray();
Boolean GetAlwaysShowTrayIcon();
FindTargetWindowResult FindTargetWindow(String[] args);

View file

@ -34,7 +34,7 @@ static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Wi
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr };
Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocused) :
Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
_lastActive{ lastFocused },
_profile{ profile }
@ -214,48 +214,172 @@ bool Pane::ResizePane(const ResizeDirection& direction)
}
// Method Description:
// - Attempts to move focus to one of our children. If we have a focused child,
// we'll try to move the focus in the direction requested.
// - If there isn't a pane that exists as a child of this pane in the correct
// direction, we'll return false. This will indicate to our parent that they
// should try and move the focus themselves. In this way, the focus can move
// up and down the tree to the correct pane.
// - This method is _very_ similar to MovePane. Both are trying to find the
// right pane to move (focus) in a direction.
// - Attempt to navigate from the sourcePane according to direction.
// - If the direction is NextInOrder or PreviousInOrder, the next or previous
// leaf in the tree, respectively, will be returned.
// - If the direction is {Up, Down, Left, Right} then the visually-adjacent
// neighbor (if it exists) will be returned. If there are multiple options
// then the first-most leaf will be selected.
// Arguments:
// - direction: The direction to move the focus in.
// - sourcePane: the pane to navigate from
// - direction: which direction to go in
// Return Value:
// - true if we or a child handled this focus move request.
bool Pane::NavigateFocus(const FocusDirection& direction)
// - The result of navigating from source according to direction, which may be
// nullptr (i.e. no pane was found in that direction).
std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> sourcePane, const FocusDirection& direction)
{
// If we're a leaf, do nothing. We can't possibly have a descendant with a
// separator the correct direction.
// Can't navigate anywhere if we are a leaf
if (_IsLeaf())
{
return false;
return nullptr;
}
// If the focus direction does not match the split direction, the focused pane
// If the MRU previous pane is requested we can't move; the tab handles MRU
if (direction == FocusDirection::None || direction == FocusDirection::Previous)
{
return nullptr;
}
// Check if we in-order traversal is requested
if (direction == FocusDirection::NextInOrder)
{
return NextPane(sourcePane);
}
if (direction == FocusDirection::PreviousInOrder)
{
return PreviousPane(sourcePane);
}
// We are left with directional traversal now
// If the focus direction does not match the split direction, the source pane
// and its neighbor must necessarily be contained within the same child.
if (!DirectionMatchesSplit(direction, _splitState))
{
return _firstChild->NavigateFocus(direction) || _secondChild->NavigateFocus(direction);
if (auto p = _firstChild->NavigateDirection(sourcePane, direction))
{
return p;
}
return _secondChild->NavigateDirection(sourcePane, direction);
}
// Since the direction is the same as our split, it is possible that we must
// move focus from from one child to another child.
// We now must keep track of state while we recurse.
auto focusNeighborPair = _FindFocusAndNeighbor(direction, { 0, 0 });
const auto paneNeighborPair = _FindPaneAndNeighbor(sourcePane, direction, { 0, 0 });
// Once we have found the focused pane and its neighbor, wherever they may
// be we can update the focus.
if (focusNeighborPair.focus && focusNeighborPair.neighbor)
if (paneNeighborPair.source && paneNeighborPair.neighbor)
{
focusNeighborPair.neighbor->_FocusFirstChild();
return true;
return paneNeighborPair.neighbor;
}
return false;
return nullptr;
}
// Method Description:
// - Attempts to find the succeeding pane of the provided pane.
// - NB: If targetPane is not a leaf, then this will return one of its children.
// Arguments:
// - targetPane: The pane to search for.
// Return Value:
// - The next pane in tree order after the target pane (if found)
std::shared_ptr<Pane> Pane::NextPane(const std::shared_ptr<Pane> targetPane)
{
// if we are a leaf pane there is no next pane.
if (_IsLeaf())
{
return nullptr;
}
std::shared_ptr<Pane> firstLeaf = nullptr;
std::shared_ptr<Pane> nextPane = nullptr;
bool foundTarget = false;
auto foundNext = WalkTree([&](auto pane) {
// In case the target pane is the last pane in the tree, keep a reference
// to the first leaf so we can wrap around.
if (firstLeaf == nullptr && pane->_IsLeaf())
{
firstLeaf = pane;
}
// If we've found the target pane already, get the next leaf pane.
if (foundTarget && pane->_IsLeaf())
{
nextPane = pane;
return true;
}
// Test if we're the target pane so we know to return the next pane.
if (pane == targetPane)
{
foundTarget = true;
}
return false;
});
// If we found the desired pane just return it
if (foundNext)
{
return nextPane;
}
// If we found the target pane, but not the next pane it means we were the
// last leaf in the tree.
if (foundTarget)
{
return firstLeaf;
}
return nullptr;
}
// Method Description:
// - Attempts to find the preceding pane of the provided pane.
// Arguments:
// - targetPane: The pane to search for.
// Return Value:
// - The previous pane in tree order before the target pane (if found)
std::shared_ptr<Pane> Pane::PreviousPane(const std::shared_ptr<Pane> targetPane)
{
// if we are a leaf pane there is no previous pane.
if (_IsLeaf())
{
return nullptr;
}
std::shared_ptr<Pane> lastLeaf = nullptr;
bool foundTarget = false;
WalkTree([&](auto pane) {
if (pane == targetPane)
{
foundTarget = true;
// If we were not the first leaf, then return the previous leaf.
// Otherwise keep walking the tree to get the last pane.
if (lastLeaf != nullptr)
{
return true;
}
}
if (pane->_IsLeaf())
{
lastLeaf = pane;
}
return false;
});
// If we found the target pane then lastLeaf will either be the preceding
// pane or the last pane in the tree if targetPane is the first leaf.
if (foundTarget)
{
return lastLeaf;
}
return nullptr;
}
// Method Description:
@ -382,6 +506,10 @@ bool Pane::SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second)
updateParent(secondParent);
}
// For now the first pane is always the focused pane, so re-focus to
// make sure the cursor is still in the terminal since the root was moved.
first->_FocusFirstChild();
return true;
}
@ -453,29 +581,29 @@ bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
}
// Method Description:
// - Given the focused pane, and its relative position in the tree, attempt to
// - Given the source pane, and its relative position in the tree, attempt to
// find its visual neighbor within the current pane's tree.
// The neighbor, if it exists, will be a leaf pane.
// Arguments:
// - direction: The direction to search in from the focused pane.
// - focus: the focused pane
// - focusIsSecondSide: If the focused pane is on the "second" side (down/right of split)
// - direction: The direction to search in from the source pane.
// - searchResult: the source pane and its relative position.
// - sourceIsSecondSide: If the source pane is on the "second" side (down/right of split)
// relative to the branch being searched
// - offset: the offset of the current pane
// Return Value:
// - A tuple of Panes, the first being the focused pane if found, and the second
// being the adjacent pane if it exists, and a bool that represents if the move
// goes out of bounds.
Pane::FocusNeighborSearch Pane::_FindNeighborForPane(const FocusDirection& direction,
FocusNeighborSearch searchResult,
const bool focusIsSecondSide,
const Pane::PanePoint offset)
Pane::PaneNeighborSearch Pane::_FindNeighborForPane(const FocusDirection& direction,
PaneNeighborSearch searchResult,
const bool sourceIsSecondSide,
const Pane::PanePoint offset)
{
// Test if the move will go out of boundaries. E.g. if the focus is already
// on the second child of some pane and it attempts to move right, there
// can't possibly be a neighbor to be found in the first child.
if ((focusIsSecondSide && (direction == FocusDirection::Right || direction == FocusDirection::Down)) ||
(!focusIsSecondSide && (direction == FocusDirection::Left || direction == FocusDirection::Up)))
if ((sourceIsSecondSide && (direction == FocusDirection::Right || direction == FocusDirection::Down)) ||
(!sourceIsSecondSide && (direction == FocusDirection::Left || direction == FocusDirection::Up)))
{
return searchResult;
}
@ -483,7 +611,7 @@ Pane::FocusNeighborSearch Pane::_FindNeighborForPane(const FocusDirection& direc
// If we are a leaf node test if we adjacent to the focus node
if (_IsLeaf())
{
if (_IsAdjacent(searchResult.focus, searchResult.focusOffset, shared_from_this(), offset, direction))
if (_IsAdjacent(searchResult.source, searchResult.sourceOffset, shared_from_this(), offset, direction))
{
searchResult.neighbor = shared_from_this();
}
@ -501,30 +629,36 @@ Pane::FocusNeighborSearch Pane::_FindNeighborForPane(const FocusDirection& direc
{
secondOffset.x += gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualWidth());
}
auto focusNeighborSearch = _firstChild->_FindNeighborForPane(direction, searchResult, focusIsSecondSide, firstOffset);
if (focusNeighborSearch.neighbor)
auto sourceNeighborSearch = _firstChild->_FindNeighborForPane(direction, searchResult, sourceIsSecondSide, firstOffset);
if (sourceNeighborSearch.neighbor)
{
return focusNeighborSearch;
return sourceNeighborSearch;
}
return _secondChild->_FindNeighborForPane(direction, searchResult, focusIsSecondSide, secondOffset);
return _secondChild->_FindNeighborForPane(direction, searchResult, sourceIsSecondSide, secondOffset);
}
// Method Description:
// - Searches the tree to find the currently focused pane, and if it exists, the
// - Searches the tree to find the source pane, and if it exists, the
// visually adjacent pane by direction.
// Arguments:
// - sourcePane: The pane to find the neighbor of.
// - direction: The direction to search in from the focused pane.
// - offset: The offset, with the top-left corner being (0,0), that the current pane is relative to the root.
// Return Value:
// - The (partial) search result. If the search was successful, the focus and its neighbor will be returned.
// - The (partial) search result. If the search was successful, the pane and its neighbor will be returned.
// Otherwise, the neighbor will be null and the focus will be null/non-null if it was found.
Pane::FocusNeighborSearch Pane::_FindFocusAndNeighbor(const FocusDirection& direction, const Pane::PanePoint offset)
Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr<Pane> sourcePane, const FocusDirection& direction, const Pane::PanePoint offset)
{
// If we are the currently focused pane, return ourselves
// If we are the source pane, return ourselves
if (this == sourcePane.get())
{
return { shared_from_this(), nullptr, offset };
}
if (_IsLeaf())
{
return { _lastActive ? shared_from_this() : nullptr, nullptr, offset };
return { nullptr, nullptr, offset };
}
// Search the first child, which has no offset from the parent pane
@ -540,100 +674,47 @@ Pane::FocusNeighborSearch Pane::_FindFocusAndNeighbor(const FocusDirection& dire
secondOffset.x += gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualWidth());
}
auto focusNeighborSearch = _firstChild->_FindFocusAndNeighbor(direction, firstOffset);
auto sourceNeighborSearch = _firstChild->_FindPaneAndNeighbor(sourcePane, direction, firstOffset);
// If we have both the focus element and its neighbor, we are done
if (focusNeighborSearch.focus && focusNeighborSearch.neighbor)
if (sourceNeighborSearch.source && sourceNeighborSearch.neighbor)
{
return focusNeighborSearch;
return sourceNeighborSearch;
}
// if we only found the focus, then we search the second branch for the
// neighbor.
if (focusNeighborSearch.focus)
if (sourceNeighborSearch.source)
{
// If we can possibly have both sides of a direction, check if the sibling has the neighbor
if (DirectionMatchesSplit(direction, _splitState))
{
return _secondChild->_FindNeighborForPane(direction, focusNeighborSearch, false, secondOffset);
return _secondChild->_FindNeighborForPane(direction, sourceNeighborSearch, false, secondOffset);
}
return focusNeighborSearch;
return sourceNeighborSearch;
}
// If we didn't find the focus at all, we need to search the second branch
// for the focus (and possibly its neighbor).
focusNeighborSearch = _secondChild->_FindFocusAndNeighbor(direction, secondOffset);
sourceNeighborSearch = _secondChild->_FindPaneAndNeighbor(sourcePane, direction, secondOffset);
// We found both so we are done.
if (focusNeighborSearch.focus && focusNeighborSearch.neighbor)
if (sourceNeighborSearch.source && sourceNeighborSearch.neighbor)
{
return focusNeighborSearch;
return sourceNeighborSearch;
}
// We only found the focus, which means that its neighbor might be in the
// first branch.
if (focusNeighborSearch.focus)
if (sourceNeighborSearch.source)
{
// If we can possibly have both sides of a direction, check if the sibling has the neighbor
if (DirectionMatchesSplit(direction, _splitState))
{
return _firstChild->_FindNeighborForPane(direction, focusNeighborSearch, true, firstOffset);
return _firstChild->_FindNeighborForPane(direction, sourceNeighborSearch, true, firstOffset);
}
return focusNeighborSearch;
return sourceNeighborSearch;
}
return { nullptr, nullptr, offset };
}
// Method Description:
// - Attempts to swap places of the focused pane with one of our children. This
// will swap with the visually adjacent leaf pane if one exists in the
// direction requested, maintaining the existing tree structure.
// This breaks down into a few possible cases
// - If the move direction would encounter the edge of the pane, no move occurs
// - If the focused pane has a single neighbor according to the direction,
// then it will swap with it.
// - If the focused pane has multiple neighbors, it will swap with the
// first-most leaf of the neighboring panes.
// Arguments:
// - direction: The direction to move the focused pane in.
// Return Value:
// - true if we or a child handled this pane move request.
bool Pane::MovePane(const FocusDirection& direction)
{
// If we're a leaf, do nothing. We can't possibly swap anything.
if (_IsLeaf())
{
return false;
}
// If we get a request to move to the previous pane return false because
// that needs to be handled at the tab level.
if (direction == FocusDirection::Previous)
{
return false;
}
// If the move direction does not match the split direction, the focused pane
// and its neighbor must necessarily be contained within the same child.
if (!DirectionMatchesSplit(direction, _splitState))
{
return _firstChild->MovePane(direction) || _secondChild->MovePane(direction);
}
// Since the direction is the same as our split, it is possible that we must
// swap a pane from one child to the other child.
// We now must keep track of state while we recurse.
auto focusNeighborPair = _FindFocusAndNeighbor(direction, { 0, 0 });
// Once we have found the focused pane and its neighbor, wherever they may
// be, we can swap them.
if (focusNeighborPair.focus && focusNeighborPair.neighbor)
{
auto swapped = SwapPanes(focusNeighborPair.focus, focusNeighborPair.neighbor);
focusNeighborPair.focus->_FocusFirstChild();
return swapped;
}
return false;
}
// Method Description:
// - Called when our attached control is closed. Triggers listeners to our close
// event, if we're a leaf pane.
@ -677,11 +758,9 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio
return;
}
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
auto paneProfile = settings.FindProfile(_profile.value());
if (paneProfile)
if (_profile)
{
auto mode = paneProfile.CloseOnExit();
const auto mode = _profile.CloseOnExit();
if ((mode == CloseOnExitMode::Always) ||
(mode == CloseOnExitMode::Graceful && newConnectionState == ConnectionState::Closed))
{
@ -705,27 +784,25 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
{
return;
}
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
auto paneProfile = settings.FindProfile(_profile.value());
if (paneProfile)
if (_profile)
{
// We don't want to do anything if nothing is set, so check for that first
if (static_cast<int>(paneProfile.BellStyle()) != 0)
if (static_cast<int>(_profile.BellStyle()) != 0)
{
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
{
// Audible is set, play the sound
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
{
_control.BellLightOn();
}
// raise the event with the bool value corresponding to the taskbar flag
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar));
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar));
}
}
}
@ -866,18 +943,18 @@ void Pane::SetActive()
}
// Method Description:
// - Returns nullopt if no children of this pane were the last control to be
// focused, or the GUID of the profile of the last control to be focused (if
// there was one).
// - Returns nullptr if no children of this pane were the last control to be
// focused, or the profile of the last control to be focused (if there was
// one).
// Arguments:
// - <none>
// Return Value:
// - nullopt if no children of this pane were the last control to be
// focused, else the GUID of the profile of the last control to be focused
std::optional<GUID> Pane::GetFocusedProfile()
// - nullptr if no children of this pane were the last control to be
// focused, else the profile of the last control to be focused
Profile Pane::GetFocusedProfile()
{
auto lastFocused = GetActivePane();
return lastFocused ? lastFocused->_profile : std::nullopt;
return lastFocused ? lastFocused->_profile : nullptr;
}
// Method Description:
@ -972,43 +1049,111 @@ void Pane::_FocusFirstChild()
}
// Method Description:
// - Attempts to update the settings of this pane or any children of this pane.
// * If this pane is a leaf, and our profile guid matches the parameter, then
// we'll apply the new settings to our control.
// * If we're not a leaf, we'll recurse on our children.
// - Updates the settings of this pane, presuming that it is a leaf.
// Arguments:
// - settings: The new TerminalSettings to apply to any matching controls
// - profile: The GUID of the profile these settings should apply to.
// - profile: The profile from which these settings originated.
// Return Value:
// - <none>
void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const GUID& profile)
void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
{
if (!_IsLeaf())
assert(_IsLeaf());
_profile = profile;
auto controlSettings = _control.Settings().as<TerminalSettings>();
// Update the parent of the control's settings object (and not the object itself) so
// that any overrides made by the control don't get affected by the reload
controlSettings.SetParent(settings.DefaultSettings());
auto unfocusedSettings{ settings.UnfocusedSettings() };
if (unfocusedSettings)
{
_firstChild->UpdateSettings(settings, profile);
_secondChild->UpdateSettings(settings, profile);
// Note: the unfocused settings needs to be entirely unchanged _except_ we need to
// set its parent to the settings object that lives in the control. This is because
// the overrides made by the control live in that settings object, so we want to make
// sure the unfocused settings inherit from that.
unfocusedSettings.SetParent(controlSettings);
}
else
_control.UnfocusedAppearance(unfocusedSettings);
_control.UpdateSettings();
}
// Method Description:
// - Attempts to add the provided pane as a split of the current pane.
// Arguments:
// - pane: the new pane to add
// - splitType: How the pane should be attached
// Return Value:
// - the new reference to the child created from the current pane.
std::shared_ptr<Pane> Pane::AttachPane(std::shared_ptr<Pane> pane, SplitState splitType)
{
// Splice the new pane into the tree
const auto [first, _] = _Split(splitType, .5, pane);
// If the new pane has a child that was the focus, re-focus it
// to steal focus from the currently focused pane.
if (pane->_HasFocusedChild())
{
if (profile == _profile)
{
auto controlSettings = _control.Settings().as<TerminalSettings>();
// Update the parent of the control's settings object (and not the object itself) so
// that any overrides made by the control don't get affected by the reload
controlSettings.SetParent(settings.DefaultSettings());
auto unfocusedSettings{ settings.UnfocusedSettings() };
if (unfocusedSettings)
pane->WalkTree([](auto p) {
if (p->_lastActive)
{
// Note: the unfocused settings needs to be entirely unchanged _except_ we need to
// set its parent to the settings object that lives in the control. This is because
// the overrides made by the control live in that settings object, so we want to make
// sure the unfocused settings inherit from that.
unfocusedSettings.SetParent(controlSettings);
p->_FocusFirstChild();
return true;
}
_control.UnfocusedAppearance(unfocusedSettings);
_control.UpdateSettings();
}
return false;
});
}
return first;
}
// Method Description:
// - Attempts to find the parent of the target pane,
// if found remove the pane from the tree and return it.
// - If the removed pane was (or contained the focus) the first sibling will
// gain focus.
// Arguments:
// - pane: the pane to detach
// Return Value:
// - The removed pane, if found.
std::shared_ptr<Pane> Pane::DetachPane(std::shared_ptr<Pane> pane)
{
// We can't remove a pane if we only have a reference to a leaf, even if we
// are the pane.
if (_IsLeaf())
{
return nullptr;
}
// Check if either of our children matches the search
const auto isFirstChild = _firstChild == pane;
const auto isSecondChild = _secondChild == pane;
if (isFirstChild || isSecondChild)
{
// Keep a reference to the child we are removing
auto detached = isFirstChild ? _firstChild : _secondChild;
// Remove the child from the tree, replace the current node with the
// other child.
_CloseChild(isFirstChild);
detached->_borders = Borders::None;
detached->_UpdateBorders();
// Trigger the detached event on each child
detached->WalkTree([](auto pane) {
pane->_PaneDetachedHandlers(pane);
return false;
});
return detached;
}
if (const auto detached = _firstChild->DetachPane(pane))
{
return detached;
}
return _secondChild->DetachPane(pane);
}
// Method Description:
@ -1073,12 +1218,10 @@ void Pane::_CloseChild(const bool closeFirst)
// them.
_lastActive = _firstChild->_lastActive || _secondChild->_lastActive;
// Remove all the ui elements of our children. This'll make sure we can
// re-attach the TermControl to our Grid.
_firstChild->_root.Children().Clear();
_secondChild->_root.Children().Clear();
_firstChild->_border.Child(nullptr);
_secondChild->_border.Child(nullptr);
// Remove all the ui elements of the remaining child. This'll make sure
// we can re-attach the TermControl to our Grid.
remainingChild->_root.Children().Clear();
remainingChild->_border.Child(nullptr);
// Reset our UI:
_root.Children().Clear();
@ -1125,17 +1268,8 @@ void Pane::_CloseChild(const bool closeFirst)
}
else
{
// Determine which border flag we gave to the child when we first split
// it, so that we can take just that flag away from them.
Borders clearBorderFlag = Borders::None;
if (_splitState == SplitState::Horizontal)
{
clearBorderFlag = closeFirst ? Borders::Top : Borders::Bottom;
}
else if (_splitState == SplitState::Vertical)
{
clearBorderFlag = closeFirst ? Borders::Left : Borders::Right;
}
// Find what borders need to persist after we close the child
auto remainingBorders = _GetCommonBorders();
// First stash away references to the old panes and their tokens
const auto oldFirstToken = _firstClosedToken;
@ -1192,13 +1326,9 @@ void Pane::_CloseChild(const bool closeFirst)
_root.Children().Append(_firstChild->GetRootElement());
_root.Children().Append(_secondChild->GetRootElement());
// Take the flag away from the children that they inherited from their
// parent, and update their borders to visually match
WI_ClearAllFlags(_firstChild->_borders, clearBorderFlag);
WI_ClearAllFlags(_secondChild->_borders, clearBorderFlag);
_UpdateBorders();
_firstChild->_UpdateBorders();
_secondChild->_UpdateBorders();
// Propagate the new borders down to the children.
_borders = remainingBorders;
_ApplySplitDefinitions();
// If the closed child was focused, transfer the focus to it's first sibling.
if (closedChild->_lastActive)
@ -1734,13 +1864,13 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
// we'll create two new children, and place them side-by-side in our Grid.
// Arguments:
// - splitType: what type of split we want to create.
// - profile: The profile GUID to associate with the newly created pane.
// - profile: The profile to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - The two newly created Panes
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState splitType,
const float splitSize,
const GUID& profile,
const Profile& profile,
const TermControl& control)
{
if (!_IsLeaf())
@ -1757,7 +1887,8 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState s
return { nullptr, nullptr };
}
return _Split(splitType, splitSize, profile, control);
auto newPane = std::make_shared<Pane>(profile, control);
return _Split(splitType, splitSize, newPane);
}
// Method Description:
@ -1827,14 +1958,13 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
// creates a new Pane to host the control, registers event handlers.
// Arguments:
// - splitType: what type of split we should create.
// - profile: The profile GUID to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// - splitSize: what fraction of the pane the new pane should get
// - newPane: the pane to add as a child
// Return Value:
// - The two newly created Panes
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState splitType,
const float splitSize,
const GUID& profile,
const TermControl& control)
std::shared_ptr<Pane> newPane)
{
if (splitType == SplitState::None)
{
@ -1870,11 +2000,11 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
// Create two new Panes
// Move our control, guid into the first one.
// Move the new guid, control into the second.
_firstChild = std::make_shared<Pane>(_profile.value(), _control);
_firstChild = std::make_shared<Pane>(_profile, _control);
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
_profile = std::nullopt;
_profile = nullptr;
_control = { nullptr };
_secondChild = std::make_shared<Pane>(profile, control);
_secondChild = newPane;
_CreateRowColDefinitions();
@ -2010,6 +2140,33 @@ bool Pane::FocusPane(const uint32_t id)
}
return false;
}
// Method Description:
// - Focuses the given pane if it is in the tree.
// This deliberately mirrors FocusPane(id) instead of just calling
// _FocusFirstChild directly.
// Arguments:
// - the pane to focus
// Return Value:
// - true if focus was set
bool Pane::FocusPane(const std::shared_ptr<Pane> pane)
{
if (_IsLeaf() && this == pane.get())
{
// Make sure to use _FocusFirstChild here - that'll properly update the
// focus if we're in startup.
_FocusFirstChild();
return true;
}
else
{
if (_firstChild && _secondChild)
{
return _firstChild->FocusPane(pane) ||
_secondChild->FocusPane(pane);
}
}
return false;
}
// Method Description:
// - Recursive function that finds a pane with the given ID
@ -2456,10 +2613,10 @@ void Pane::_SetupResources()
s_focusedBorderBrush = SolidColorBrush{ Colors::Black() };
}
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
if (res.HasKey(tabViewBackgroundKey))
const auto unfocusedBorderBrushKey = winrt::box_value(L"UnfocusedBorderBrush");
if (res.HasKey(unfocusedBorderBrushKey))
{
winrt::Windows::Foundation::IInspectable obj = res.Lookup(tabViewBackgroundKey);
winrt::Windows::Foundation::IInspectable obj = res.Lookup(unfocusedBorderBrushKey);
s_unfocusedBorderBrush = obj.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>();
}
else
@ -2548,6 +2705,30 @@ bool Pane::ContainsReadOnly() const
return _IsLeaf() ? _control.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
}
// Method Description:
// - If we're a parent, place the taskbar state for all our leaves into the
// provided vector.
// - If we're a leaf, place our own state into the vector.
// Arguments:
// - states: a vector that will receive all the states of all leaves in the tree
// Return Value:
// - <none>
void Pane::CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states)
{
if (_IsLeaf())
{
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(_control.TaskbarState(),
_control.TaskbarProgress()) };
states.push_back(tbState);
}
else
{
_firstChild->CollectTaskbarStates(states);
_secondChild->CollectTaskbarStates(states);
}
}
DEFINE_EVENT(Pane, GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
DEFINE_EVENT(Pane, LostFocus, _LostFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
DEFINE_EVENT(Pane, PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>);
DEFINE_EVENT(Pane, Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>);

View file

@ -21,6 +21,7 @@
#pragma once
#include "../../cascadia/inc/cppwinrt_utils.h"
#include "TaskbarState.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
@ -28,6 +29,11 @@ namespace TerminalAppLocalTests
class TabTests;
};
namespace winrt::TerminalApp::implementation
{
struct TerminalTab;
}
enum class Borders : int
{
None = 0x0,
@ -41,13 +47,21 @@ DEFINE_ENUM_FLAG_OPERATORS(Borders);
class Pane : public std::enable_shared_from_this<Pane>
{
public:
Pane(const GUID& profile,
Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control,
const bool lastFocused = false);
std::shared_ptr<Pane> GetActivePane();
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
std::optional<GUID> GetFocusedProfile();
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
// Method Description:
// - If this is a leaf pane, return its profile.
// - If this is a branch/root pane, return nullptr.
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
{
return _profile;
}
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
@ -57,17 +71,19 @@ public:
void SetActive();
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const GUID& profile);
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
void Relayout();
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool MovePane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second);
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
std::shared_ptr<Pane> PreviousPane(const std::shared_ptr<Pane> pane);
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
const float splitSize,
const GUID& profile,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control);
bool ToggleSplitOrientation();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
@ -80,6 +96,10 @@ public:
void Shutdown();
void Close();
std::shared_ptr<Pane> AttachPane(std::shared_ptr<Pane> pane,
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
int GetLeafPaneCount() const noexcept;
void Maximize(std::shared_ptr<Pane> zoomedPane);
@ -88,18 +108,47 @@ public:
std::optional<uint32_t> Id() noexcept;
void Id(uint32_t id) noexcept;
bool FocusPane(const uint32_t id);
bool FocusPane(const std::shared_ptr<Pane> pane);
std::shared_ptr<Pane> FindPane(const uint32_t id);
bool ContainsReadOnly() const;
// Method Description:
// - A helper method for ad-hoc recursion on a pane tree. Walks the pane
// tree, calling a predicate on each pane in a depth-first pattern.
// - If the predicate returns true, recursion is stopped early.
// Arguments:
// - f: The function to be applied to each pane.
// Return Value:
// - true if the predicate returned true on any pane.
template<typename F>
//requires std::predicate<F, std::shared_ptr<Pane>>
bool WalkTree(F f)
{
if (f(shared_from_this()))
{
return true;
}
if (!_IsLeaf())
{
return _firstChild->WalkTree(f) || _secondChild->WalkTree(f);
}
return false;
}
void CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states);
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
DECLARE_EVENT(LostFocus, _LostFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
DECLARE_EVENT(PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>);
DECLARE_EVENT(Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>);
private:
struct PanePoint;
struct FocusNeighborSearch;
struct PaneNeighborSearch;
struct SnapSizeResult;
struct SnapChildrenSizeResult;
struct LayoutSizeNode;
@ -119,7 +168,7 @@ private:
std::optional<uint32_t> _id;
bool _lastActive{ false };
std::optional<GUID> _profile{ std::nullopt };
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
winrt::event_token _connectionStateChangedToken{ 0 };
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };
@ -140,8 +189,7 @@ private:
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
const float splitSize,
const GUID& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control);
std::shared_ptr<Pane> newPane);
void _CreateRowColDefinitions();
void _ApplySplitDefinitions();
@ -153,12 +201,13 @@ private:
std::shared_ptr<Pane> _FindParentOfPane(const std::shared_ptr<Pane> pane);
bool _IsAdjacent(const std::shared_ptr<Pane> first, const PanePoint firstOffset, const std::shared_ptr<Pane> second, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
FocusNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
FocusNeighborSearch searchResult,
const bool focusIsSecondSide,
const PanePoint offset);
FocusNeighborSearch _FindFocusAndNeighbor(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
const PanePoint offset);
PaneNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
PaneNeighborSearch searchResult,
const bool focusIsSecondSide,
const PanePoint offset);
PaneNeighborSearch _FindPaneAndNeighbor(const std::shared_ptr<Pane> sourcePane,
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
const PanePoint offset);
void _CloseChild(const bool closeFirst);
winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst);
@ -227,11 +276,11 @@ private:
float y;
};
struct FocusNeighborSearch
struct PaneNeighborSearch
{
std::shared_ptr<Pane> focus;
std::shared_ptr<Pane> source;
std::shared_ptr<Pane> neighbor;
PanePoint focusOffset;
PanePoint sourceOffset;
};
struct SnapSizeResult
@ -271,5 +320,6 @@ private:
void _AssignChildNode(std::unique_ptr<LayoutSizeNode>& nodeField, const LayoutSizeNode* const newNode);
};
friend struct winrt::TerminalApp::implementation::TerminalTab;
friend class ::TerminalAppLocalTests::TabTests;
};

View file

@ -282,6 +282,16 @@
<data name="CmdFocusTabTargetArgDesc" xml:space="preserve">
<value>Move focus the tab at the given index</value>
</data>
<data name="CmdMovePaneTabArgDesc" xml:space="preserve">
<value>Move focused pane to the tab at the given index</value>
</data>
<data name="CmdMovePaneDesc" xml:space="preserve">
<value>Move focused pane to another tab</value>
</data>
<data name="CmdMPDesc" xml:space="preserve">
<value>An alias for the "move-pane" subcommand.</value>
<comment>{Locked="\"move-pane\""}</comment>
</data>
<data name="CmdSplitPaneSizeArgDesc" xml:space="preserve">
<value>Specify the size as a percentage of the parent pane. Valid values are between (0,1), exclusive.</value>
</data>
@ -359,14 +369,10 @@
<data name="CmdMoveFocusDirectionArgDesc" xml:space="preserve">
<value>The direction to move focus in</value>
</data>
<data name="CmdMovePaneDesc" xml:space="preserve">
<data name="CmdSwapPaneDesc" xml:space="preserve">
<value>Swap the focused pane with the adjacent pane in the specified direction</value>
</data>
<data name="CmdMPDesc" xml:space="preserve">
<value>An alias for the "move-pane" subcommand.</value>
<comment>{Locked="\"move-pane\""}</comment>
</data>
<data name="CmdMovePaneDirectionArgDesc" xml:space="preserve">
<data name="CmdSwapPaneDirectionArgDesc" xml:space="preserve">
<value>The direction to move the focused pane in</value>
</data>
<data name="CmdFocusDesc" xml:space="preserve">
@ -647,6 +653,14 @@
<data name="CommandPaletteMenuItem" xml:space="preserve">
<value>Command Palette</value>
</data>
<data name="TrayIconFocusTerminal" xml:space="preserve">
<value>Focus Terminal</value>
<comment>This is displayed as a label for the context menu item that focuses the terminal.</comment>
</data>
<data name="TrayIconWindowSubmenu" xml:space="preserve">
<value>Windows</value>
<comment>This is displayed as a label for the context menu item that holds the submenu of available windows.</comment>
</data>
<data name="ShellExtension_OpenInTerminalMenuItem_Dev" xml:space="preserve">
<value>Open in Windows Terminal (Dev)</value>
<comment>{Locked} The dev build will never be seen in multiple languages</comment>

View file

@ -59,10 +59,10 @@ namespace winrt::TerminalApp::implementation
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
try
{
const auto profileGuid{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
_CreateNewTabFromSettings(profileGuid, settings, existingConnection);
_CreateNewTabWithProfileAndSettings(profile, settings, existingConnection);
const uint32_t tabCount = _tabs.Size();
const bool usedManualProfile = (newTerminalArgs != nullptr) &&
@ -70,7 +70,7 @@ namespace winrt::TerminalApp::implementation
newTerminalArgs.Profile().empty());
// Lookup the name of the color scheme used by this profile.
const auto scheme = _settings.GetColorSchemeForProfile(profileGuid);
const auto scheme = _settings.GetColorSchemeForProfile(profile);
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
// that as the empty string.
const auto schemeName = scheme ? scheme.Name() : L"\0";
@ -82,7 +82,7 @@ namespace winrt::TerminalApp::implementation
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"),
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
TraceLoggingFloat64(settings.DefaultSettings().TintOpacity(), "TintOpacity", "Opacity preference from the settings"),
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
@ -95,45 +95,12 @@ namespace winrt::TerminalApp::implementation
CATCH_RETURN();
// Method Description:
// - Creates a new tab with the given settings. If the tab bar is not being
// currently displayed, it will be shown.
// - Sets up state, event handlers, etc on a tab object that was just made.
// Arguments:
// - profileGuid: ID to use to lookup profile settings for this connection
// - settings: the TerminalSettings object to use to create the TerminalControl with.
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
// - newTabImpl: the uninitialized tab.
void TerminalPage::_InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl)
{
// Initialize the new tab
// Create a connection based on the values in our settings object if we weren't given one.
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings.DefaultSettings());
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
// We need to tell it about our size information so it can match the dimensions of what
// we are about to present.
if (existingConnection)
{
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
}
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
if (_settings.GlobalSettings().DebugFeaturesEnabled())
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
if (bothAltsPressed)
{
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
}
}
// Give term control a child of the settings so that any overrides go in the child
// This way, when we do a settings reload we just update the parent and the overrides remain
auto term = _InitControl(settings, connection);
auto newTabImpl = winrt::make_self<TerminalTab>(profileGuid, term);
newTabImpl->Initialize();
// Add the new tab to the list of our tabs.
_tabs.Append(*newTabImpl);
@ -146,7 +113,7 @@ namespace winrt::TerminalApp::implementation
_UpdateTabIndices();
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(term, *newTabImpl);
_RegisterTabEvents(*newTabImpl);
// Don't capture a strong ref to the tab. If the tab is removed as this
// is called, we don't really care anymore about handling the event.
@ -208,10 +175,12 @@ namespace winrt::TerminalApp::implementation
_tabView.TabItems().Append(tabViewItem);
// Set this tab's icon to the icon from the user's profile
const auto profile = _settings.FindProfile(profileGuid);
if (profile != nullptr && !profile.Icon().empty())
if (const auto profile{ newTabImpl->GetFocusedProfile() })
{
newTabImpl->UpdateIcon(profile.Icon());
if (!profile.Icon().empty())
{
newTabImpl->UpdateIcon(profile.Icon());
}
}
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
@ -244,19 +213,73 @@ namespace winrt::TerminalApp::implementation
}
});
if (debugConnection) // this will only be set if global debugging is on and tap is active
{
auto newControl = _InitControl(settings, debugConnection);
_RegisterTerminalEvents(newControl, *newTabImpl);
// Split (auto) with the debug tap.
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl);
}
// This kicks off TabView::SelectionChanged, in response to which
// we'll attach the terminal's Xaml control to the Xaml root.
_tabView.SelectedItem(tabViewItem);
}
// Method Description:
// - Create a new tab using a specified pane as the root.
// Arguments:
// - pane: The pane to use as the root.
void TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane)
{
auto newTabImpl = winrt::make_self<TerminalTab>(pane);
_InitializeTab(newTabImpl);
}
// Method Description:
// - Creates a new tab with the given settings. If the tab bar is not being
// currently displayed, it will be shown.
// Arguments:
// - profile: profile settings for this connection
// - settings: the TerminalSettings object to use to create the TerminalControl with.
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
void TerminalPage::_CreateNewTabWithProfileAndSettings(const Profile& profile, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
{
// Initialize the new tab
// Create a connection based on the values in our settings object if we weren't given one.
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, settings.DefaultSettings());
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
// We need to tell it about our size information so it can match the dimensions of what
// we are about to present.
if (existingConnection)
{
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
}
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
if (_settings.GlobalSettings().DebugFeaturesEnabled())
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
if (bothAltsPressed)
{
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
}
}
// Give term control a child of the settings so that any overrides go in the child
// This way, when we do a settings reload we just update the parent and the overrides remain
auto term = _InitControl(settings, connection);
auto newTabImpl = winrt::make_self<TerminalTab>(profile, term);
_RegisterTerminalEvents(term);
_InitializeTab(newTabImpl);
if (debugConnection) // this will only be set if global debugging is on and tap is active
{
auto newControl = _InitControl(settings, debugConnection);
_RegisterTerminalEvents(newControl);
// Split (auto) with the debug tap.
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profile, newControl);
}
}
// Method Description:
// - Get the icon of the currently focused terminal control, and set its
// tab's icon to that icon.
@ -264,19 +287,9 @@ namespace winrt::TerminalApp::implementation
// - tab: the Tab to update the title for.
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
{
const auto lastFocusedProfileOpt = tab.GetFocusedProfile();
if (lastFocusedProfileOpt.has_value())
if (const auto profile = tab.GetFocusedProfile())
{
const auto lastFocusedProfile = lastFocusedProfileOpt.value();
const auto matchingProfile = _settings.FindProfile(lastFocusedProfile);
if (matchingProfile)
{
tab.UpdateIcon(matchingProfile.Icon());
}
else
{
tab.UpdateIcon({});
}
tab.UpdateIcon(profile.Icon());
}
}
@ -330,23 +343,16 @@ namespace winrt::TerminalApp::implementation
{
try
{
// TODO: GH#5047 - In the future, we should get the Profile of
// the focused pane, and use that to build a new instance of the
// settings so we can duplicate this tab/pane.
// TODO: GH#5047 - We're duplicating the whole profile, which might
// be a dangling reference to old settings.
//
// Currently, if the profile doesn't exist anymore in our
// settings, we'll silently do nothing.
//
// In the future, it will be preferable to just duplicate the
// current control's settings, but we can't do that currently,
// because we won't be able to create a new instance of the
// connection without keeping an instance of the original Profile
// object around.
// In the future, it may be preferable to just duplicate the
// current control's live settings (which will include changes
// made through VT).
const auto& profileGuid = tab.GetFocusedProfile();
if (profileGuid.has_value())
if (const auto profile = tab.GetFocusedProfile())
{
const auto settingsCreateResult{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid.value(), *_bindings) };
const auto settingsCreateResult{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
@ -354,7 +360,7 @@ namespace winrt::TerminalApp::implementation
settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory);
}
_CreateNewTabFromSettings(profileGuid.value(), settingsCreateResult);
_CreateNewTabWithProfileAndSettings(profile, settingsCreateResult);
const auto runtimeTabText{ tab.GetTabText() };
if (!runtimeTabText.empty())

View file

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "TaskbarState.h"
#include "TaskbarState.g.cpp"
namespace winrt::TerminalApp::implementation
{
// Default to unset, 0%.
TaskbarState::TaskbarState() :
TaskbarState(0, 0){};
TaskbarState::TaskbarState(const uint64_t dispatchTypesState, const uint64_t progressParam) :
_State{ dispatchTypesState },
_Progress{ progressParam } {}
uint64_t TaskbarState::Priority() const
{
// This seemingly nonsensical ordering is from
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate#how-the-taskbar-button-chooses-the-progress-indicator-for-a-group
switch (_State)
{
case 0: // Clear = 0,
return 5;
case 1: // Set = 1,
return 3;
case 2: // Error = 2,
return 1;
case 3: // Indeterminate = 3,
return 4;
case 4: // Paused = 4
return 2;
}
// Here, return 6, to definitely be greater than all the other valid values.
// This should never really happen.
return 6;
}
int TaskbarState::ComparePriority(const winrt::TerminalApp::TaskbarState& lhs, const winrt::TerminalApp::TaskbarState& rhs)
{
return lhs.Priority() < rhs.Priority();
}
}

View file

@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "inc/cppwinrt_utils.h"
#include "TaskbarState.g.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class TabTests;
};
namespace winrt::TerminalApp::implementation
{
struct TaskbarState : TaskbarStateT<TaskbarState>
{
public:
TaskbarState();
TaskbarState(const uint64_t dispatchTypesState, const uint64_t progress);
static int ComparePriority(const winrt::TerminalApp::TaskbarState& lhs, const winrt::TerminalApp::TaskbarState& rhs);
uint64_t Priority() const;
WINRT_PROPERTY(uint64_t, State, 0);
WINRT_PROPERTY(uint64_t, Progress, 0);
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(TaskbarState);
}

View file

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
[default_interface] runtimeclass TaskbarState
{
TaskbarState();
TaskbarState(UInt64 dispatchTypesState, UInt64 progress);
UInt64 State{ get; };
UInt64 Progress{ get; };
UInt64 Priority { get; };
}
}

View file

@ -90,6 +90,9 @@
<DependentUpon>TabBase.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TabPaletteItem.h" />
<ClInclude Include="TaskbarState.h">
<DependentUpon>TaskbarState.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalTab.h">
<DependentUpon>TerminalTab.idl</DependentUpon>
</ClInclude>
@ -166,6 +169,9 @@
<DependentUpon>TabBase.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TabPaletteItem.cpp" />
<ClCompile Include="TaskbarState.cpp">
<DependentUpon>TaskbarState.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalTab.cpp">
<DependentUpon>TerminalTab.idl</DependentUpon>
</ClCompile>
@ -258,6 +264,7 @@
</Midl>
<Midl Include="TabBase.idl" />
<Midl Include="TabPaletteItem.idl" />
<Midl Include="TaskbarState.idl" />
<Midl Include="TerminalTab.idl" />
<Midl Include="TerminalPage.idl">
<DependentUpon>TerminalPage.xaml</DependentUpon>
@ -373,13 +380,13 @@
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View file

@ -142,6 +142,44 @@ namespace winrt::TerminalApp::implementation
}
CATCH_LOG();
if (_settings.GlobalSettings().UseAcrylicInTabRow())
{
const auto res = Application::Current().Resources();
const auto lightKey = winrt::box_value(L"Light");
const auto darkKey = winrt::box_value(L"Dark");
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
for (auto const& dictionary : res.MergedDictionaries())
{
// Don't change MUX resources
if (dictionary.Source())
{
continue;
}
for (auto const& kvPair : dictionary.ThemeDictionaries())
{
const auto themeDictionary = kvPair.Value().as<winrt::Windows::UI::Xaml::ResourceDictionary>();
if (themeDictionary.HasKey(tabViewBackgroundKey))
{
const auto backgroundSolidBrush = themeDictionary.Lookup(tabViewBackgroundKey).as<Media::SolidColorBrush>();
const til::color backgroundColor = backgroundSolidBrush.Color();
const auto acrylicBrush = Media::AcrylicBrush();
acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
acrylicBrush.FallbackColor(backgroundColor);
acrylicBrush.TintColor(backgroundColor);
acrylicBrush.TintOpacity(0.5);
themeDictionary.Insert(tabViewBackgroundKey, acrylicBrush);
}
}
}
}
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
_tabView.CanReorderTabs(!isElevated);
_tabView.CanDragTabs(!isElevated);
@ -419,6 +457,11 @@ namespace winrt::TerminalApp::implementation
co_return;
}
}
// GH#6586: now that we're done processing all startup commands,
// focus the active control. This will work as expected for both
// commandline invocations and for `wt` action invocations.
_GetActiveControl().Focus(FocusState::Programmatic);
}
if (initial)
{
@ -733,7 +776,12 @@ namespace winrt::TerminalApp::implementation
// Manually fill in the evaluated profile.
if (newTerminalArgs.ProfileIndex() != nullptr)
{
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(this->_settings.GetProfileForArgs(newTerminalArgs)));
// We want to promote the index to a GUID because there is no "launch to profile index" command.
const auto profile = _settings.GetProfileForArgs(newTerminalArgs);
if (profile)
{
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
}
}
this->_OpenNewWindow(false, newTerminalArgs);
}
@ -756,14 +804,18 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Creates a new connection based on the profile settings
// Arguments:
// - the profile GUID we want the settings from
// - the profile we want the settings from
// - the terminal settings
// Return value:
// - the desired connection
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(GUID profileGuid,
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(Profile profile,
TerminalSettings settings)
{
const auto profile = _settings.FindProfile(profileGuid);
if (!profile)
{
// Use the default profile if we didn't get one as an argument.
profile = _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
}
TerminalConnection::ITerminalConnection connection{ nullptr };
@ -788,7 +840,8 @@ namespace winrt::TerminalApp::implementation
else
{
std::wstring guidWString = Utils::GuidToString(profileGuid);
// profile is guaranteed to exist here
std::wstring guidWString = Utils::GuidToString(profile.Guid());
StringMap envMap{};
envMap.Insert(L"WT_PROFILE_ID", guidWString);
@ -837,7 +890,7 @@ namespace winrt::TerminalApp::implementation
"ConnectionCreated",
TraceLoggingDescription("Event emitted upon the creation of a connection"),
TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"),
TraceLoggingGuid(profileGuid, "ProfileGuid", "The profile's GUID"),
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The profile's GUID"),
TraceLoggingGuid(sessionGuid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
@ -980,11 +1033,9 @@ namespace winrt::TerminalApp::implementation
// handle. This includes:
// * the Copy and Paste events, for setting and retrieving clipboard data
// on the right thread
// * the TitleChanged event, for changing the text of the tab
// Arguments:
// - term: The newly created TermControl to connect the events for
// - hostingTab: The Tab that's hosting this TermControl instance
void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab)
void TerminalPage::_RegisterTerminalEvents(TermControl term)
{
term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler });
@ -999,10 +1050,20 @@ namespace winrt::TerminalApp::implementation
term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler });
term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
// Add an event handler for when the terminal or tab wants to set a
// progress indicator on the taskbar
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
}
// Bind Tab events to the TermControl and the Tab's Pane
hostingTab.Initialize(term);
// Method Description:
// - Connects event handlers to the TerminalTab for events that we want to
// handle. This includes:
// * the TitleChanged event, for changing the text of the tab
// * the Color{Selected,Cleared} events to change the color of a tab.
// Arguments:
// - hostingTab: The Tab that's hosting this TermControl instance
void TerminalPage::_RegisterTabEvents(TerminalTab& hostingTab)
{
auto weakTab{ hostingTab.get_weak() };
auto weakThis{ get_weak() };
// PropertyChanged is the generic mechanism by which the Tab
@ -1054,7 +1115,6 @@ namespace winrt::TerminalApp::implementation
// Add an event handler for when the terminal or tab wants to set a
// progress indicator on the taskbar
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
// TODO GH#3327: Once we support colorizing the NewTab button based on
// the color of the tab, we'll want to make sure to call
@ -1115,18 +1175,19 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Attempt to swap the positions of the focused pane with another pane.
// See Pane::MovePane for details.
// See Pane::SwapPane for details.
// Arguments:
// - direction: The direction to move the focused pane in.
// Return Value:
// - <none>
void TerminalPage::_MovePane(const FocusDirection& direction)
// - true if panes were swapped.
bool TerminalPage::_SwapPane(const FocusDirection& direction)
{
if (const auto terminalTab{ _GetFocusedTabImpl() })
{
_UnZoomIfNeeded();
terminalTab->MovePane(direction);
return terminalTab->SwapPane(direction);
}
return false;
}
TermControl TerminalPage::_GetActiveControl()
@ -1188,6 +1249,56 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// - Moves the currently active pane on the currently active tab to the
// specified tab. If the tab index is greater than the number of
// tabs, then a new tab will be created for the pane. Similarly, if a pane
// is the last remaining pane on a tab, that tab will be closed upon moving.
// - No move will occur if the tabIdx is the same as the current tab, or if
// the specified tab is not a host of terminals (such as the settings tab).
// Arguments:
// - tabIdx: The target tab index.
// Return Value:
// - true if the pane was successfully moved to the new tab.
bool TerminalPage::_MovePane(const uint32_t tabIdx)
{
auto focusedTab{ _GetFocusedTabImpl() };
if (!focusedTab)
{
return false;
}
// If we are trying to move from the current tab to the current tab do nothing.
if (_GetFocusedTabIndex() == tabIdx)
{
return false;
}
// Moving the pane from the current tab might close it, so get the next
// tab before its index changes.
if (_tabs.Size() > tabIdx)
{
auto targetTab = _GetTerminalTabImpl(_tabs.GetAt(tabIdx));
// if the selected tab is not a host of terminals (e.g. settings)
// don't attempt to add a pane to it.
if (!targetTab)
{
return false;
}
auto pane = focusedTab->DetachPane();
targetTab->AttachPane(pane);
_SetFocusedTab(*targetTab);
}
else
{
auto pane = focusedTab->DetachPane();
_CreateNewTabFromPane(pane);
}
return true;
}
// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given TermControl into the newly created pane.
@ -1248,23 +1359,20 @@ namespace winrt::TerminalApp::implementation
try
{
TerminalSettingsCreateResult controlSettings{ nullptr };
GUID realGuid;
bool profileFound = false;
Profile profile{ nullptr };
if (splitMode == SplitType::Duplicate)
{
std::optional<GUID> current_guid = tab.GetFocusedProfile();
if (current_guid)
profile = tab.GetFocusedProfile();
if (profile)
{
profileFound = true;
controlSettings = TerminalSettings::CreateWithProfileByID(_settings, current_guid.value(), *_bindings);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
}
realGuid = current_guid.value();
}
// TODO: GH#5047 - In the future, we should get the Profile of
// the focused pane, and use that to build a new instance of the
@ -1279,13 +1387,13 @@ namespace winrt::TerminalApp::implementation
// connection without keeping an instance of the original Profile
// object around.
}
if (!profileFound)
if (!profile)
{
realGuid = _settings.GetProfileForArgs(newTerminalArgs);
profile = _settings.GetProfileForArgs(newTerminalArgs);
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
}
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings.DefaultSettings());
const auto controlConnection = _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
@ -1306,11 +1414,19 @@ namespace winrt::TerminalApp::implementation
auto newControl = _InitControl(controlSettings, controlConnection);
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(newControl, tab);
_RegisterTerminalEvents(newControl);
_UnZoomIfNeeded();
tab.SplitPane(realSplitType, splitSize, realGuid, newControl);
tab.SplitPane(realSplitType, splitSize, profile, newControl);
// After GH#6586, the control will no longer focus itself
// automatically when it's finished being laid out. Manually focus
// the control here instead.
if (_startupState == StartupState::Initialized)
{
_GetActiveControl().Focus(FocusState::Programmatic);
}
}
CATCH_LOG();
}
@ -1893,40 +2009,56 @@ namespace winrt::TerminalApp::implementation
_HookupKeyBindings(_settings.ActionMap());
// Refresh UI elements
auto profiles = _settings.ActiveProfiles();
for (const auto& profile : profiles)
// Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
// when we stabilize its guid this will become fully safe.
std::unordered_map<winrt::guid, std::pair<Profile, TerminalSettingsCreateResult>> profileGuidSettingsMap;
const auto profileDefaults{ _settings.ProfileDefaults() };
const auto allProfiles{ _settings.AllProfiles() };
profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
// Include the Defaults profile for consideration
profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
for (const auto& newProfile : allProfiles)
{
const auto profileGuid = profile.Guid();
try
{
// This can throw an exception if the profileGuid does
// not belong to an actual profile in the list of profiles.
auto settings{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid, *_bindings) };
for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
terminalTab->UpdateSettings(settings, profileGuid);
}
}
}
CATCH_LOG();
// Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
// panes may not be using all of their profiles at the same time. Lazy evaluation is king!
profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
}
// GH#2455: If there are any panes with controls that had been
// initialized with a Profile that no longer exists in our list of
// profiles, we'll leave it unmodified. The profile doesn't exist
// anymore, so we can't possibly update its settings.
// Update the icon of the tab for the currently focused profile in that tab.
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
// and profiles so the Title and Icon will be set once and only once on init.
for (auto tab : _tabs)
for (const auto& tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
{
terminalTab->UpdateSettings();
// Manually enumerate the panes in each tab; this will let us recycle TerminalSettings
// objects but only have to iterate one time.
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
if (const auto profile{ pane->GetProfile() })
{
const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
// GH#2455: If there are any panes with controls that had been
// initialized with a Profile that no longer exists in our list of
// profiles, we'll leave it unmodified. The profile doesn't exist
// anymore, so we can't possibly update its settings.
if (found != profileGuidSettingsMap.cend())
{
auto& pair{ found->second };
if (!pair.second)
{
pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
}
pane->UpdateSettings(pair.second, pair.first);
}
}
return false;
});
// Update the icon of the tab for the currently focused profile in that tab.
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
// and profiles so the Title and Icon will be set once and only once on init.
_UpdateTabIcon(*terminalTab);
// Force the TerminalTab to re-grab its currently active control's title.
@ -2077,29 +2209,35 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// - Gets the taskbar state value from the last active control
// - Get the combined taskbar state for the page. This is the combination of
// all the states of all the tabs, which are themselves a combination of
// all their panes. Taskbar states are given a priority based on the rules
// in:
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate
// under "How the Taskbar Button Chooses the Progress Indicator for a Group"
// Arguments:
// - <none>
// Return Value:
// - The taskbar state of the last active control
uint64_t TerminalPage::GetLastActiveControlTaskbarState()
// - A TaskbarState object representing the combined taskbar state and
// progress percentage of all our tabs.
winrt::TerminalApp::TaskbarState TerminalPage::TaskbarState() const
{
if (auto control{ _GetActiveControl() })
{
return control.TaskbarState();
}
return {};
}
auto state{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>() };
// Method Description:
// - Gets the taskbar progress value from the last active control
// Return Value:
// - The taskbar progress of the last active control
uint64_t TerminalPage::GetLastActiveControlTaskbarProgress()
{
if (auto control{ _GetActiveControl() })
for (const auto& tab : _tabs)
{
return control.TaskbarProgress();
if (auto tabImpl{ _GetTerminalTabImpl(tab) })
{
auto tabState{ tabImpl->GetCombinedTaskbarState() };
// lowest priority wins
if (tabState.Priority() < state.Priority())
{
state = tabState;
}
}
}
return {};
return state;
}
// Method Description:

View file

@ -85,8 +85,7 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter);
uint64_t GetLastActiveControlTaskbarState();
uint64_t GetLastActiveControlTaskbarProgress();
winrt::TerminalApp::TaskbarState TaskbarState() const;
void ShowKeyboardServiceWarning();
winrt::hstring KeyboardServiceDisabledText();
@ -189,8 +188,9 @@ namespace winrt::TerminalApp::implementation
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
void _CreateNewTabFromSettings(GUID profileGuid, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
void _CreateNewTabWithProfileAndSettings(const Microsoft::Terminal::Settings::Model::Profile& profile, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
@ -226,7 +226,9 @@ namespace winrt::TerminalApp::implementation
void _RemoveTab(const winrt::TerminalApp::TabBase& tab);
winrt::fire_and_forget _RemoveTabs(const std::vector<winrt::TerminalApp::TabBase> tabs);
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term, TerminalTab& hostingTab);
void _InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl);
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term);
void _RegisterTabEvents(TerminalTab& hostingTab);
void _DismissTabContextMenus();
void _FocusCurrentTab(const bool focusAlways);
@ -237,7 +239,8 @@ namespace winrt::TerminalApp::implementation
void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
bool _SelectTab(uint32_t tabIndex);
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
void _MovePane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _MovePane(const uint32_t tabIdx);
winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl();
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;

View file

@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TaskbarState.idl";
namespace TerminalApp
{
@ -42,8 +43,7 @@ namespace TerminalApp
void ShowKeyboardServiceWarning();
String KeyboardServiceDisabledText { get; };
UInt64 GetLastActiveControlTaskbarState();
UInt64 GetLastActiveControlTaskbarProgress();
TaskbarState TaskbarState{ get; };
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;

View file

@ -25,19 +25,66 @@ namespace winrt
namespace winrt::TerminalApp::implementation
{
TerminalTab::TerminalTab(const GUID& profile, const TermControl& control)
TerminalTab::TerminalTab(const Profile& profile, const TermControl& control)
{
_rootPane = std::make_shared<Pane>(profile, control, true);
_rootPane->Id(_nextPaneId);
_activePane = _rootPane;
_mruPanes.insert(_mruPanes.begin(), _nextPaneId);
++_nextPaneId;
_rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
_Setup();
}
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
{
_rootPane = rootPane;
_activePane = nullptr;
auto firstId = _nextPaneId;
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
// update the IDs on each pane
if (pane->_IsLeaf())
{
pane->Id(_nextPaneId);
_nextPaneId++;
}
// Try to find the pane marked active (if it exists)
if (pane->_lastActive)
{
_activePane = pane;
}
return false;
});
// In case none of the panes were already marked as the focus, just
// focus the first one.
if (_activePane == nullptr)
{
_rootPane->FocusPane(firstId);
_activePane = _rootPane->GetActivePane();
}
// Set the active control
_mruPanes.insert(_mruPanes.begin(), _activePane->Id().value());
_Setup();
}
// Method Description:
// - Shared setup for the constructors. Assumed that _rootPane has been set.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalTab::_Setup()
{
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
_ClosedHandlers(nullptr, nullptr);
});
_activePane = _rootPane;
Content(_rootPane->GetRootElement());
_MakeTabViewItem();
@ -144,19 +191,31 @@ namespace winrt::TerminalApp::implementation
// that was last focused.
TermControl TerminalTab::GetActiveTerminalControl() const
{
return _activePane->GetTerminalControl();
if (_activePane)
{
return _activePane->GetTerminalControl();
}
return nullptr;
}
// Method Description:
// - Called after construction of a Tab object to bind event handlers to its
// associated Pane and TermControl object
// associated Pane and TermControl objects
// Arguments:
// - control: reference to the TermControl object to bind event to
// - <none>
// Return Value:
// - <none>
void TerminalTab::Initialize(const TermControl& control)
void TerminalTab::Initialize()
{
_BindEventHandlers(control);
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
// Attach event handlers to each new pane
_AttachEventHandlersToPane(pane);
if (auto control = pane->GetTerminalControl())
{
_AttachEventHandlersToControl(pane->Id().value(), control);
}
return false;
});
}
// Method Description:
@ -177,10 +236,9 @@ namespace winrt::TerminalApp::implementation
{
lastFocusedControl.Focus(_focusState);
// Update our own progress state, and fire an event signaling
// Update our own progress state. This will fire an event signaling
// that our taskbar progress changed.
_UpdateProgressState();
_TaskbarProgressChangedHandlers(lastFocusedControl, nullptr);
}
// When we gain focus, remove the bell indicator if it is active
if (_tabStatus.BellIndicator())
@ -199,35 +257,19 @@ namespace winrt::TerminalApp::implementation
// Return Value:
// - nullopt if no children of this tab were the last control to be
// focused, else the GUID of the profile of the last control to be focused
std::optional<GUID> TerminalTab::GetFocusedProfile() const noexcept
Profile TerminalTab::GetFocusedProfile() const noexcept
{
return _activePane->GetFocusedProfile();
}
// Method Description:
// - Called after construction of a Tab object to bind event handlers to its
// associated Pane and TermControl object
// Arguments:
// - control: reference to the TermControl object to bind event to
// - Attempts to update the settings that apply to this tab.
// - Panes are handled elsewhere, by somebody who can establish broader knowledge
// of the settings that apply to all tabs.
// Return Value:
// - <none>
void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept
void TerminalTab::UpdateSettings()
{
_AttachEventHandlersToPane(_rootPane);
_AttachEventHandlersToControl(control);
}
// Method Description:
// - Attempts to update the settings of this tab's tree of panes.
// Arguments:
// - settings: The new TerminalSettingsCreateResult to apply to any matching controls
// - profile: The GUID of the profile these settings should apply to.
// Return Value:
// - <none>
void TerminalTab::UpdateSettings(const TerminalSettingsCreateResult& settings, const GUID& profile)
{
_rootPane->UpdateSettings(settings, profile);
// The tabWidthMode may have changed, update the header control accordingly
_UpdateHeaderControlMaxWidth();
}
@ -406,7 +448,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::SplitPane(SplitState splitType,
const float splitSize,
const GUID& profile,
const Profile& profile,
TermControl& control)
{
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
@ -426,10 +468,10 @@ namespace winrt::TerminalApp::implementation
++_nextPaneId;
}
_activePane = first;
_AttachEventHandlersToControl(control);
// Add a event handlers to the new panes' GotFocus event. When the pane
// gains focus, we'll mark it as the new active pane.
_AttachEventHandlersToControl(second->Id().value(), control);
_AttachEventHandlersToPane(first);
_AttachEventHandlersToPane(second);
@ -440,11 +482,121 @@ namespace winrt::TerminalApp::implementation
_UpdateActivePane(second);
}
// Method Description:
// - Removes the currently active pane from this tab. If that was the only
// remaining pane, then the entire tab is closed as well.
// Arguments:
// - <none>
// Return Value:
// - The removed pane, if the remove succeeded.
std::shared_ptr<Pane> TerminalTab::DetachPane()
{
// if we only have one pane, remove it entirely
// and close this tab
if (_rootPane == _activePane)
{
return DetachRoot();
}
// Attempt to remove the active pane from the tree
if (const auto pane = _rootPane->DetachPane(_activePane))
{
// Just make sure that the remaining pane is marked active
_UpdateActivePane(_rootPane->GetActivePane());
return pane;
}
return nullptr;
}
// Method Description:
// - Closes this tab and returns the root pane to be used elsewhere.
// Arguments:
// - <none>
// Return Value:
// - The root pane.
std::shared_ptr<Pane> TerminalTab::DetachRoot()
{
// remove the closed event handler since we are closing the tab
// manually.
_rootPane->Closed(_rootClosedToken);
auto p = _rootPane;
p->WalkTree([](auto pane) {
pane->_PaneDetachedHandlers(pane);
return false;
});
// Clean up references and close the tab
_rootPane = nullptr;
_activePane = nullptr;
Content(nullptr);
_ClosedHandlers(nullptr, nullptr);
return p;
}
// Method Description:
// - Add an arbitrary pane to this tab. This will be added as a split on the
// currently active pane.
// Arguments:
// - pane: The pane to add.
// Return Value:
// - <none>
void TerminalTab::AttachPane(std::shared_ptr<Pane> pane)
{
// Add the new event handlers to the new pane(s)
// and update their ids.
pane->WalkTree([&](auto p) {
_AttachEventHandlersToPane(p);
if (p->_IsLeaf())
{
p->Id(_nextPaneId);
_nextPaneId++;
}
if (auto control = p->GetTerminalControl())
{
_AttachEventHandlersToControl(p->Id().value(), control);
}
return false;
});
// pass the old id to the new child
const auto previousId = _activePane->Id();
// Add the new pane as an automatic split on the active pane.
auto first = _activePane->AttachPane(pane, SplitState::Automatic);
// under current assumptions this condition should always be true.
if (previousId)
{
first->Id(previousId.value());
}
else
{
first->Id(_nextPaneId);
++_nextPaneId;
}
// Update with event handlers on the new child.
_activePane = first;
_AttachEventHandlersToPane(first);
// Make sure that we have the right pane set as the active pane
pane->WalkTree([&](auto p) {
if (p->_lastActive)
{
_UpdateActivePane(p);
return true;
}
return false;
});
}
// Method Description:
// - Find the currently active pane, and then switch the split direction of
// its parent. E.g. switch from Horizontal to Vertical.
// Return Value:
// - <none>
void TerminalTab::ToggleSplitOrientation()
{
@ -509,7 +661,12 @@ namespace winrt::TerminalApp::implementation
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
return _rootPane->NavigateFocus(direction);
if (auto newFocus = _rootPane->NavigateDirection(_activePane, direction))
{
return _rootPane->FocusPane(newFocus);
}
return false;
}
}
@ -520,26 +677,33 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - direction: The direction to move the pane in.
// Return Value:
// - <none>
void TerminalTab::MovePane(const FocusDirection& direction)
// - true if two panes were swapped.
bool TerminalTab::SwapPane(const FocusDirection& direction)
{
if (direction == FocusDirection::Previous)
{
if (_mruPanes.size() < 2)
{
return;
return false;
}
if (auto lastPane = _rootPane->FindPane(_mruPanes.at(1)))
{
_rootPane->SwapPanes(_activePane, lastPane);
return _rootPane->SwapPanes(_activePane, lastPane);
}
}
else
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
_rootPane->MovePane(direction);
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction))
{
return _rootPane->SwapPanes(_activePane, neighbor);
}
return false;
}
return false;
}
bool TerminalTab::FocusPane(const uint32_t id)
@ -551,7 +715,10 @@ namespace winrt::TerminalApp::implementation
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
void TerminalTab::Shutdown()
{
_rootPane->Shutdown();
if (_rootPane)
{
_rootPane->Shutdown();
}
}
// Method Description:
@ -596,6 +763,34 @@ namespace winrt::TerminalApp::implementation
_headerControl.BeginRename();
}
// Method Description:
// - Removes any event handlers set by the tab on the given pane's control.
// The pane's ID is the most stable identifier for a given control, because
// the control itself doesn't have a particular ID and its pointer is
// unstable since it is moved when panes split.
// Arguments:
// - paneId: The ID of the pane that contains the given control.
// - control: the control to remove events from.
// Return Value:
// - <none>
void TerminalTab::_DetachEventHandlersFromControl(const uint32_t paneId, const TermControl& control)
{
auto it = _controlEvents.find(paneId);
if (it != _controlEvents.end())
{
auto& events = it->second;
control.TitleChanged(events.titleToken);
control.FontSizeChanged(events.fontToken);
control.TabColorChanged(events.colorToken);
control.SetTaskbarProgress(events.taskbarToken);
control.ReadOnlyChanged(events.readOnlyToken);
control.FocusFollowMouseRequested(events.focusToken);
_controlEvents.erase(paneId);
}
}
// Method Description:
// - Register any event handlers that we may need with the given TermControl.
// This should be called on each and every TermControl that we add to the tree
@ -603,15 +798,17 @@ namespace winrt::TerminalApp::implementation
// * notify us when the control's title changed, so we can update our own
// title (if necessary)
// Arguments:
// - paneId: the ID of the pane that this control belongs to.
// - control: the TermControl to add events to.
// Return Value:
// - <none>
void TerminalTab::_AttachEventHandlersToControl(const TermControl& control)
void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control)
{
auto weakThis{ get_weak() };
auto dispatcher = TabViewItem().Dispatcher();
ControlEventTokens events{};
control.TitleChanged([weakThis](auto&&, auto&&) {
events.titleToken = control.TitleChanged([weakThis](auto&&, auto&&) {
// Check if Tab's lifetime has expired
if (auto tab{ weakThis.get() })
{
@ -626,16 +823,16 @@ namespace winrt::TerminalApp::implementation
// On the latter event, we tell the root pane to resize itself so that its descendants
// (including ourself) can properly snap to character grids. In future, we may also
// want to do that on regular font changes.
control.FontSizeChanged([this](const int /* fontWidth */,
const int /* fontHeight */,
const bool isInitialChange) {
events.fontToken = control.FontSizeChanged([this](const int /* fontWidth */,
const int /* fontHeight */,
const bool isInitialChange) {
if (isInitialChange)
{
_rootPane->Relayout();
}
});
control.TabColorChanged([weakThis](auto&&, auto&&) {
events.colorToken = control.TabColorChanged([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
// The control's tabColor changed, but it is not necessarily the
@ -645,7 +842,7 @@ namespace winrt::TerminalApp::implementation
}
});
control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
co_await winrt::resume_foreground(dispatcher);
// Check if Tab's lifetime has expired
if (auto tab{ weakThis.get() })
@ -654,14 +851,14 @@ namespace winrt::TerminalApp::implementation
}
});
control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
events.readOnlyToken = control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_RecalculateAndApplyReadOnly();
}
});
control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
events.focusToken = control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
if (const auto tab{ weakThis.get() })
{
if (tab->_focusState != FocusState::Unfocused)
@ -673,6 +870,31 @@ namespace winrt::TerminalApp::implementation
}
}
});
_controlEvents[paneId] = events;
}
// Method Description:
// - Get the combined taskbar state for the tab. This is the combination of
// all the states of all our panes. Taskbar states are given a priority
// based on the rules in:
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate
// under "How the Taskbar Button Chooses the Progress Indicator for a
// Group"
// Arguments:
// - <none>
// Return Value:
// - A TaskbarState object representing the combined taskbar state and
// progress percentage of all our panes.
winrt::TerminalApp::TaskbarState TerminalTab::GetCombinedTaskbarState() const
{
std::vector<winrt::TerminalApp::TaskbarState> states;
if (_rootPane)
{
_rootPane->CollectTaskbarStates(states);
}
return states.empty() ? winrt::make<winrt::TerminalApp::implementation::TaskbarState>() :
*std::min_element(states.begin(), states.end(), TerminalApp::implementation::TaskbarState::ComparePriority);
}
// Method Description:
@ -690,37 +912,39 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::_UpdateProgressState()
{
if (const auto& activeControl{ GetActiveTerminalControl() })
{
const auto taskbarState = activeControl.TaskbarState();
// The progress of the control changed, but not necessarily the progress of the tab.
// Set the tab's progress ring to the active pane's progress
if (taskbarState > 0)
{
if (taskbarState == 3)
{
// 3 is the indeterminate state, set the progress ring as such
_tabStatus.IsProgressRingIndeterminate(true);
}
else
{
// any non-indeterminate state has a value, set the progress ring as such
_tabStatus.IsProgressRingIndeterminate(false);
const auto state{ GetCombinedTaskbarState() };
const auto progressValue = gsl::narrow<uint32_t>(activeControl.TaskbarProgress());
_tabStatus.ProgressValue(progressValue);
}
// Hide the tab icon (the progress ring is placed over it)
HideIcon(true);
_tabStatus.IsProgressRingActive(true);
const auto taskbarState = state.State();
// The progress of the control changed, but not necessarily the progress of the tab.
// Set the tab's progress ring to the active pane's progress
if (taskbarState > 0)
{
if (taskbarState == 3)
{
// 3 is the indeterminate state, set the progress ring as such
_tabStatus.IsProgressRingIndeterminate(true);
}
else
{
// Show the tab icon
HideIcon(false);
_tabStatus.IsProgressRingActive(false);
// any non-indeterminate state has a value, set the progress ring as such
_tabStatus.IsProgressRingIndeterminate(false);
const auto progressValue = gsl::narrow<uint32_t>(state.Progress());
_tabStatus.ProgressValue(progressValue);
}
// Hide the tab icon (the progress ring is placed over it)
HideIcon(true);
_tabStatus.IsProgressRingActive(true);
}
else
{
// Show the tab icon
HideIcon(false);
_tabStatus.IsProgressRingActive(false);
}
// fire an event signaling that our taskbar progress changed.
_TaskbarProgressChangedHandlers(nullptr, nullptr);
}
// Method Description:
@ -777,7 +1001,7 @@ namespace winrt::TerminalApp::implementation
auto weakThis{ get_weak() };
std::weak_ptr<Pane> weakPane{ pane };
pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
auto gotFocusToken = pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
// Do nothing if the Tab's lifetime is expired or pane isn't new.
auto tab{ weakThis.get() };
@ -797,7 +1021,7 @@ namespace winrt::TerminalApp::implementation
}
});
pane->LostFocus([weakThis](std::shared_ptr<Pane> /*sender*/) {
auto lostFocusToken = pane->LostFocus([weakThis](std::shared_ptr<Pane> /*sender*/) {
// Do nothing if the Tab's lifetime is expired or pane isn't new.
auto tab{ weakThis.get() };
@ -811,7 +1035,7 @@ namespace winrt::TerminalApp::implementation
// Add a Closed event handler to the Pane. If the pane closes out from
// underneath us, and it's zoomed, we want to be able to make sure to
// update our state accordingly to un-zoom that pane. See GH#7252.
pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
if (auto tab{ weakThis.get() })
{
if (tab->_zoomedPane)
@ -836,7 +1060,7 @@ namespace winrt::TerminalApp::implementation
});
// Add a PaneRaiseBell event handler to the Pane
pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
auto bellToken = pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
if (auto tab{ weakThis.get() })
{
if (visual)
@ -858,6 +1082,40 @@ namespace winrt::TerminalApp::implementation
}
}
});
// box the event token so that we can give a reference to it in the
// event handler.
auto detachedToken = std::make_shared<winrt::event_token>();
// Add a Detached event handler to the Pane to clean up tab state
// and other event handlers when a pane is removed from this tab.
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, bellToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
// Make sure we do this at most once
if (auto pane{ weakPane.lock() })
{
pane->Detached(*detachedToken);
pane->GotFocus(gotFocusToken);
pane->LostFocus(lostFocusToken);
pane->Closed(closedToken);
pane->PaneRaiseBell(bellToken);
if (auto tab{ weakThis.get() })
{
if (auto control = pane->GetTerminalControl())
{
tab->_DetachEventHandlersFromControl(pane->Id().value(), control);
}
for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i)
{
if (*i == pane->Id())
{
tab->_mruPanes.erase(i);
break;
}
}
}
}
});
}
// Method Description:

View file

@ -21,21 +21,26 @@ namespace winrt::TerminalApp::implementation
struct TerminalTab : TerminalTabT<TerminalTab, TabBase>
{
public:
TerminalTab(const GUID& profile, const winrt::Microsoft::Terminal::Control::TermControl& control);
TerminalTab(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, const winrt::Microsoft::Terminal::Control::TermControl& control);
TerminalTab(std::shared_ptr<Pane> rootPane);
// Called after construction to perform the necessary setup, which relies on weak_ptr
void Initialize(const winrt::Microsoft::Terminal::Control::TermControl& control);
void Initialize();
winrt::Microsoft::Terminal::Control::TermControl GetActiveTerminalControl() const;
std::optional<GUID> GetFocusedProfile() const noexcept;
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile() const noexcept;
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
winrt::fire_and_forget Scroll(const int delta);
std::shared_ptr<Pane> DetachRoot();
std::shared_ptr<Pane> DetachPane();
void AttachPane(std::shared_ptr<Pane> pane);
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
const float splitSize,
const GUID& profile,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
winrt::Microsoft::Terminal::Control::TermControl& control);
void ToggleSplitOrientation();
@ -54,10 +59,10 @@ namespace winrt::TerminalApp::implementation
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
void MovePane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool FocusPane(const uint32_t id);
void UpdateSettings(const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const GUID& profile);
void UpdateSettings();
winrt::fire_and_forget UpdateTitle();
void Shutdown() override;
@ -83,6 +88,9 @@ namespace winrt::TerminalApp::implementation
void TogglePaneReadOnly();
std::shared_ptr<Pane> GetActivePane() const;
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
std::shared_ptr<Pane> GetRootPane() const { return _rootPane; }
winrt::TerminalApp::TerminalTabStatus TabStatus()
{
@ -101,6 +109,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> _rootPane{ nullptr };
std::shared_ptr<Pane> _activePane{ nullptr };
std::shared_ptr<Pane> _zoomedPane{ nullptr };
winrt::hstring _lastIconPath{};
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
@ -108,6 +117,19 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TabHeaderControl _headerControl{};
winrt::TerminalApp::TerminalTabStatus _tabStatus{};
struct ControlEventTokens
{
winrt::event_token titleToken;
winrt::event_token fontToken;
winrt::event_token colorToken;
winrt::event_token taskbarToken;
winrt::event_token readOnlyToken;
winrt::event_token focusToken;
};
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;
winrt::event_token _rootClosedToken{};
std::vector<uint32_t> _mruPanes;
uint32_t _nextPaneId{ 0 };
@ -120,6 +142,8 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
void _Setup();
std::optional<Windows::UI::Xaml::DispatcherTimer> _bellIndicatorTimer;
void _BellIndicatorTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
@ -132,9 +156,8 @@ namespace winrt::TerminalApp::implementation
void _RefreshVisualState();
void _BindEventHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control) noexcept;
void _AttachEventHandlersToControl(const winrt::Microsoft::Terminal::Control::TermControl& control);
void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
void _UpdateActivePane(std::shared_ptr<Pane> pane);

View file

@ -89,13 +89,13 @@
</ItemGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
</packages>

View file

@ -104,6 +104,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnTerminalTaskbarProgressChanged = std::bind(&ControlCore::_terminalTaskbarProgressChanged, this);
_terminal->TaskbarProgressChangedCallback(pfnTerminalTaskbarProgressChanged);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
// the UIA Engine to the renderer. This prevents us from signaling changes to the cursor or buffer.
{
// First create the render thread.
// Then stash a local pointer to the render thread so we can initialize it and enable it
// to paint itself *after* we hand off its ownership to the renderer.
// We split up construction and initialization of the render thread object this way
// because the renderer and render thread have circular references to each other.
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
auto* const localPointerToThread = renderThread.get();
// Now create the renderer and initialize the render thread.
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
_renderer->SetRendererEnteredErrorStateCallback([weakThis = get_weak()]() {
if (auto strongThis{ weakThis.get() })
{
strongThis->_RendererEnteredErrorStateHandlers(*strongThis, nullptr);
}
});
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
}
// Get our dispatcher. If we're hosted in-proc with XAML, this will get
// us the same dispatcher as TermControl::Dispatcher(). If we're out of
// proc, this'll return null. We'll need to instead make a new
@ -196,27 +222,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return false;
}
// First create the render thread.
// Then stash a local pointer to the render thread so we can initialize it and enable it
// to paint itself *after* we hand off its ownership to the renderer.
// We split up construction and initialization of the render thread object this way
// because the renderer and render thread have circular references to each other.
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
auto* const localPointerToThread = renderThread.get();
// Now create the renderer and initialize the render thread.
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
::Microsoft::Console::Render::IRenderTarget& renderTarget = *_renderer;
_renderer->SetRendererEnteredErrorStateCallback([weakThis = get_weak()]() {
if (auto strongThis{ weakThis.get() })
{
strongThis->_RendererEnteredErrorStateHandlers(*strongThis, nullptr);
}
});
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
// Set up the DX Engine
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
_renderer->AddRenderEngine(dxEngine.get());
@ -248,7 +253,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_settings.InitialCols(width);
_settings.InitialRows(height);
_terminal->CreateFromSettings(_settings, renderTarget);
_terminal->CreateFromSettings(_settings, *_renderer);
// IMPORTANT! Set this callback up sooner than later. If we do it
// after Enable, then it'll be possible to paint the frame once
@ -264,6 +269,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
_renderEngine->SetIntenseIsBold(_settings.IntenseIsBold());
_updateAntiAliasingMode(_renderEngine.get());
@ -600,6 +606,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
_updateAntiAliasingMode(_renderEngine.get());
// Refresh our font with the renderer
@ -629,6 +636,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_renderEngine->SetSelectionBackground(til::color{ newAppearance.SelectionBackground() });
_renderEngine->SetRetroTerminalEffect(newAppearance.RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(newAppearance.PixelShaderPath());
_renderEngine->SetIntenseIsBold(_settings.IntenseIsBold());
_renderer->TriggerRedrawAll();
}
}
@ -804,6 +812,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
// Convert our new dimensions to characters
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
{ static_cast<short>(size.cx), static_cast<short>(size.cy) });
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
const auto currentVP = _terminal->GetViewport();
// Don't actually resize if viewport dimensions didn't change
if (vp.Height() == currentVP.Height() && vp.Width() == currentVP.Width())
{
return;
}
_terminal->ClearSelection();
// Tell the dx engine that our window is now the new size.
@ -812,11 +832,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Invalidate everything
_renderer->TriggerRedrawAll();
// Convert our new dimensions to characters
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
{ static_cast<short>(size.cx), static_cast<short>(size.cy) });
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
// If this function succeeds with S_FALSE, then the terminal didn't
// actually change size. No need to notify the connection of this no-op.
const HRESULT hr = _terminal->UserResize({ vp.Width(), vp.Height() });
@ -1455,10 +1470,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
{
if (_renderer)
{
_renderer->AddRenderEngine(pEngine);
}
// _renderer will always exist since it's introduced in the ctor
_renderer->AddRenderEngine(pEngine);
}
bool ControlCore::IsInReadOnlyMode() const

View file

@ -614,7 +614,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::InteractivityAutomationPeer ControlInteractivity::OnCreateAutomationPeer()
try
{
auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
const auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
_uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
_core->AttachUiaEngine(_uiaEngine.get());

View file

@ -11,6 +11,8 @@ namespace Microsoft.Terminal.Control
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode;
Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment;
Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment;
Boolean IntenseIsBold;
// IntenseIsBright is in Core Appearance
// Experimental settings
Boolean RetroTerminalEffect;

View file

@ -9,5 +9,6 @@ namespace Microsoft.Terminal.Control
interface IKeyBindings
{
Boolean TryKeyChord(KeyChord kc);
Boolean IsKeyChordExplicitlyUnbound(KeyChord kc);
}
}

View file

@ -21,9 +21,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
KeyChord::KeyChord(bool ctrl, bool alt, bool shift, bool win, int32_t vkey, int32_t scanCode) noexcept :
_modifiers{ modifiersFromBooleans(ctrl, alt, shift, win) },
_vkey{ vkey },
_scanCode{ scanCode }
KeyChord(modifiersFromBooleans(ctrl, alt, shift, win), vkey, scanCode)
{
}
@ -32,19 +30,75 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_vkey{ vkey },
_scanCode{ scanCode }
{
// ActionMap needs to identify KeyChords which should "layer" (overwrite) each other.
// For instance win+sc(41) and win+` both specify the same KeyChord on an US keyboard layout
// from the perspective of a user. Either of the two should correctly overwrite the other.
// We can help ActionMap with this by ensuring that Vkey() is always valid.
if (!_vkey)
{
_vkey = MapVirtualKeyW(scanCode, MAPVK_VSC_TO_VK_EX);
}
assert(_vkey || _scanCode);
}
VirtualKeyModifiers KeyChord::Modifiers() noexcept
uint64_t KeyChord::Hash() const noexcept
{
// Two KeyChords are equal if they have the same modifiers and either identical
// Vkey or ScanCode, with Vkey being preferred. See KeyChord::Equals().
// This forces us to _either_ hash _vkey or _scanCode.
//
// Additionally the hash value with _vkey==123 and _scanCode==123 must be different.
// --> Taint hashes of KeyChord without _vkey.
auto h = static_cast<uint64_t>(_modifiers) << 32;
h |= _vkey ? _vkey : (_scanCode | 0xBABE0000);
// I didn't like how std::hash uses the byte-wise FNV1a for integers.
// So I built my own std::hash with murmurhash3.
h ^= h >> 33;
h *= UINT64_C(0xff51afd7ed558ccd);
h ^= h >> 33;
h *= UINT64_C(0xc4ceb9fe1a85ec53);
h ^= h >> 33;
return h;
}
bool KeyChord::Equals(const Control::KeyChord& other) const noexcept
{
// Two KeyChords are equal if they have the same modifiers and either identical
// Vkey or ScanCode, with Vkey being preferred. Vkey is preferred because:
// ActionMap needs to identify KeyChords which should "layer" (overwrite) each other.
// For instance win+sc(41) and win+` both specify the same KeyChord on an US keyboard layout
// from the perspective of a user. Either of the two should correctly overwrite the other.
//
// Two problems exist here:
// * Since a value of 0 indicates that the Vkey/ScanCode isn't set, we cannot use == to compare them.
// Otherwise we return true, even if the Vkey/ScanCode isn't set on both sides.
// * Whenever Equals() returns true, the Hash() value _must_ be identical.
// For instance the code below ensures the preference of Vkey over ScanCode by:
// this->_vkey || other->_vkey ? ...vkey... : ...scanCode...
// We cannot use "&&", even if it would be technically more correct, as this would mean the
// return value of this function would be dependent on the existence of a Vkey in "other".
// But Hash() has no "other" argument to consider when deciding if its Vkey or ScanCode should be hashed.
//
// Bitwise operators are used because MSVC doesn't support compiling
// boolean operators into bitwise ones at the time of writing.
const auto otherSelf = winrt::get_self<KeyChord>(other);
return _modifiers == otherSelf->_modifiers && ((_vkey | otherSelf->_vkey) ? _vkey == otherSelf->_vkey : _scanCode == otherSelf->_scanCode);
}
VirtualKeyModifiers KeyChord::Modifiers() const noexcept
{
return _modifiers;
}
void KeyChord::Modifiers(VirtualKeyModifiers const& value) noexcept
void KeyChord::Modifiers(const VirtualKeyModifiers value) noexcept
{
_modifiers = value;
}
int32_t KeyChord::Vkey() noexcept
int32_t KeyChord::Vkey() const noexcept
{
return _vkey;
}
@ -54,7 +108,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_vkey = value;
}
int32_t KeyChord::ScanCode() noexcept
int32_t KeyChord::ScanCode() const noexcept
{
return _scanCode;
}

View file

@ -13,11 +13,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
KeyChord(const winrt::Windows::System::VirtualKeyModifiers modifiers, int32_t vkey, int32_t scanCode) noexcept;
KeyChord(bool ctrl, bool alt, bool shift, bool win, int32_t vkey, int32_t scanCode) noexcept;
winrt::Windows::System::VirtualKeyModifiers Modifiers() noexcept;
void Modifiers(winrt::Windows::System::VirtualKeyModifiers const& value) noexcept;
int32_t Vkey() noexcept;
uint64_t Hash() const noexcept;
bool Equals(const Control::KeyChord& other) const noexcept;
winrt::Windows::System::VirtualKeyModifiers Modifiers() const noexcept;
void Modifiers(const winrt::Windows::System::VirtualKeyModifiers value) noexcept;
int32_t Vkey() const noexcept;
void Vkey(int32_t value) noexcept;
int32_t ScanCode() noexcept;
int32_t ScanCode() const noexcept;
void ScanCode(int32_t value) noexcept;
private:

View file

@ -10,6 +10,9 @@ namespace Microsoft.Terminal.Control
KeyChord(Windows.System.VirtualKeyModifiers modifiers, Int32 vkey, Int32 scanCode);
KeyChord(Boolean ctrl, Boolean alt, Boolean shift, Boolean win, Int32 vkey, Int32 scanCode);
UInt64 Hash();
Boolean Equals(KeyChord other);
Windows.System.VirtualKeyModifiers Modifiers;
Int32 Vkey;
Int32 ScanCode;

View file

@ -540,7 +540,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_changeBackgroundColor(bg);
// Apply padding as swapChainPanel's margin
auto newMargin = ParseThicknessFromPadding(newSettings.Padding());
const auto newMargin = ParseThicknessFromPadding(newSettings.Padding());
SwapChainPanel().Margin(newMargin);
TSFInputControl().Margin(newMargin);
@ -676,13 +676,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - The automation peer for our control
Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
{
if (_initializedTerminal && !_IsClosing()) // only set up the automation peer if we're ready to go live
// MSFT 33353327: We're purposefully not using _initializedTerminal to ensure we're fully initialized.
// Doing so makes us return nullptr when XAML requests an automation peer.
// Instead, we need to give XAML an automation peer, then fix it later.
if (!_IsClosing())
{
// create a custom automation peer with this code pattern:
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
if (const auto& interactivityAutoPeer{ _interactivity.OnCreateAutomationPeer() })
{
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, interactivityAutoPeer);
const auto margins{ SwapChainPanel().Margin() };
const Core::Padding padding{ margins.Left,
margins.Top,
margins.Right,
margins.Bottom };
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
interactivityAutoPeer.SetParentProvider(_automationPeer.GetParentProvider());
return _automationPeer;
}
@ -846,8 +854,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
DispatcherTimer cursorTimer;
cursorTimer.Interval(std::chrono::milliseconds(blinkTime));
cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick });
cursorTimer.Start();
_cursorTimer.emplace(std::move(cursorTimer));
// As of GH#6586, don't start the cursor timer immediately, and
// don't show the cursor initially. We'll show the cursor and start
// the timer when the control is first focused. cursorTimer.Start();
_core.CursorOn(false);
}
else
{
@ -876,14 +887,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Now that the renderer is set up, update the appearance for initialization
_UpdateAppearanceFromUIThread(_settings);
// Focus the control here. If we do it during control initialization, then
// focus won't actually get passed to us. I believe this is because
// we're not technically a part of the UI tree yet, so focusing us
// becomes a no-op.
this->Focus(FocusState::Programmatic);
_initializedTerminal = true;
// MSFT 33353327: If the AutomationPeer was created before we were done initializing,
// make sure it's properly set up now.
if (_automationPeer)
{
_automationPeer.UpdateControlBounds();
const auto margins{ GetPadding() };
_automationPeer.SetControlPadding(Core::Padding{ margins.Left,
margins.Top,
margins.Right,
margins.Bottom });
}
// Likewise, run the event handlers outside of lock (they could
// be reentrant)
_InitializedHandlers(*this, nullptr);
@ -934,28 +951,39 @@ namespace winrt::Microsoft::Terminal::Control::implementation
(void)_TrySendKeyEvent(VK_MENU, scanCode, modifiers, false);
handled = true;
}
else if (vkey == VK_F7 && down)
else if ((vkey == VK_F7 || vkey == VK_SPACE) && down)
{
// Manually generate an F7 event into the key bindings or terminal.
// This is required as part of GH#638.
// Or do so for alt+space; only send to terminal when explicitly unbound
// That is part of #GH7125
auto bindings{ _settings.KeyBindings() };
bool isUnbound = false;
const KeyChord kc = {
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
modifiers.IsWinPressed(),
gsl::narrow_cast<WORD>(vkey),
0
};
if (bindings)
{
handled = bindings.TryKeyChord({
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
modifiers.IsWinPressed(),
VK_F7,
0,
});
handled = bindings.TryKeyChord(kc);
if (!handled)
{
isUnbound = bindings.IsKeyChordExplicitlyUnbound(kc);
}
}
if (!handled)
const bool sendToTerminal = vkey == VK_F7 || (vkey == VK_SPACE && isUnbound);
if (!handled && sendToTerminal)
{
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
(void)_TrySendKeyEvent(VK_F7, scanCode, modifiers, true);
(void)_TrySendKeyEvent(gsl::narrow_cast<WORD>(vkey), scanCode, modifiers, true);
// GH#6438: Note that we're _not_ sending the key up here - that'll
// get passed through XAML to our KeyUp handler normally.
handled = true;
@ -1365,6 +1393,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_RestorePointerCursorHandlers(*this, nullptr);
const auto point = args.GetCurrentPoint(*this);
// GH#10329 - we don't need to handle horizontal scrolls. Only vertical ones.
// So filter out the horizontal ones.
if (point.Properties().IsHorizontalMouseWheel())
{
return;
}
auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() },
point.Properties().MouseWheelDelta(),

View file

@ -192,8 +192,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
// _closing isn't atomic and may only be accessed from the main thread.
assert(Dispatcher().HasThreadAccess());
if (const auto dispatcher = Dispatcher())
{
assert(dispatcher.HasThreadAccess());
}
#endif
return _closing;
}

View file

@ -31,13 +31,14 @@ namespace XamlAutomation
namespace winrt::Microsoft::Terminal::Control::implementation
{
TermControlAutomationPeer::TermControlAutomationPeer(TermControl* owner,
const Core::Padding padding,
Control::InteractivityAutomationPeer impl) :
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer
_termControl{ owner },
_contentAutomationPeer{ impl }
{
UpdateControlBounds();
SetControlPadding(padding);
// Listen for UIA signalling events from the implementation. We need to
// be the one to actually raise these automation events, so they go
// through the UI tree correctly.

View file

@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
public:
TermControlAutomationPeer(Microsoft::Terminal::Control::implementation::TermControl* owner,
const Core::Padding padding,
Control::InteractivityAutomationPeer implementation);
void UpdateControlBounds();

View file

@ -66,5 +66,6 @@ namespace Microsoft.Terminal.Core
Microsoft.Terminal.Core.Color CursorColor;
CursorStyle CursorShape;
UInt32 CursorHeight;
Boolean IntenseIsBright;
};
}

View file

@ -52,7 +52,8 @@ Terminal::Terminal() :
_selection{ std::nullopt },
_taskbarState{ 0 },
_taskbarProgress{ 0 },
_trimBlockSelection{ false }
_trimBlockSelection{ false },
_intenseIsBright{ true }
{
auto dispatch = std::make_unique<TerminalDispatch>(*this);
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
@ -173,6 +174,7 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance)
til::color newBackgroundColor{ appearance.DefaultBackground() };
_defaultBg = newBackgroundColor.with_alpha(0);
_defaultFg = appearance.DefaultForeground();
_intenseIsBright = appearance.IntenseIsBright();
for (int i = 0; i < 16; i++)
{
@ -594,14 +596,6 @@ bool Terminal::SendKeyEvent(const WORD vkey,
const auto isAltOnlyPressed = states.IsAltPressed() && !states.IsCtrlPressed();
// DON'T manually handle Alt+Space - the system will use this to bring up
// the system menu for restore, min/maximize, size, move, close.
// (This doesn't apply to Ctrl+Alt+Space.)
if (isAltOnlyPressed && vkey == VK_SPACE)
{
return false;
}
// By default Windows treats Ctrl+Alt as an alias for AltGr.
// When the altGrAliasing setting is set to false, this behaviour should be disabled.
//
@ -672,13 +666,6 @@ bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButt
// - false otherwise.
bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states)
{
// DON'T manually handle Alt+Space - the system will use this to bring up
// the system menu for restore, min/maximize, size, move, close.
if (ch == L' ' && states.IsAltPressed() && !states.IsCtrlPressed())
{
return false;
}
auto vkey = _TakeVirtualKeyFromLastKeyEvent(scanCode);
if (vkey == 0 && scanCode != 0)
{
@ -1004,6 +991,31 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
_buffer->IncrementCircularBuffer();
proposedCursorPosition.Y--;
rowsPushedOffTopOfBuffer++;
// Update our selection too, so it doesn't move as the buffer is cycled
if (_selection)
{
// If the start of the selection is above 0, we can reduce both the start and end by 1
if (_selection->start.Y > 0)
{
_selection->start.Y -= 1;
_selection->end.Y -= 1;
}
else
{
// The start of the selection is at 0, if the end is greater than 0, then only reduce the end
if (_selection->end.Y > 0)
{
_selection->start.X = 0;
_selection->end.Y -= 1;
}
else
{
// Both the start and end of the selection are at 0, clear the selection
_selection.reset();
}
}
}
}
// manually erase our pattern intervals since the locations have changed now

View file

@ -282,6 +282,7 @@ private:
bool _suppressApplicationTitle;
bool _bracketedPasteMode;
bool _trimBlockSelection;
bool _intenseIsBright;
size_t _taskbarState;
size_t _taskbarProgress;

View file

@ -16,7 +16,7 @@ try
_WriteBuffer(stringView);
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
bool Terminal::ExecuteChar(wchar_t wch) noexcept
try
@ -24,7 +24,7 @@ try
_WriteBuffer({ &wch, 1 });
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
TextAttribute Terminal::GetTextAttributes() const noexcept
{
@ -54,7 +54,7 @@ try
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
COORD Terminal::GetCursorPosition() noexcept
{
@ -75,7 +75,7 @@ try
_buffer->GetCursor().SetColor(color);
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - Moves the cursor down one line, and possibly also to the leftmost column.
@ -101,7 +101,7 @@ try
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - deletes count characters starting from the cursor's current position
@ -150,7 +150,7 @@ try
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - Inserts count spaces starting from the cursor's current position, moving over the existing text
@ -205,7 +205,7 @@ try
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
bool Terminal::EraseCharacters(const size_t numChars) noexcept
try
@ -218,7 +218,7 @@ try
_buffer->Write(eraseIter, absoluteCursorPos);
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method description:
// - erases a line of text, either from
@ -264,7 +264,7 @@ try
_buffer->Write(eraseIter, startPos, false);
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method description:
// - erases text in the buffer in two ways depending on erase type
@ -348,7 +348,7 @@ try
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
bool Terminal::WarningBell() noexcept
try
@ -356,7 +356,7 @@ try
_pfnWarningBell();
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
bool Terminal::SetWindowTitle(std::wstring_view title) noexcept
try
@ -368,7 +368,7 @@ try
}
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - Updates the value in the colortable at index tableIndex to the new color
@ -387,7 +387,7 @@ try
_buffer->GetRenderTarget().TriggerRedrawAll();
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - Sets the cursor style to the given style.
@ -457,7 +457,7 @@ try
_buffer->GetRenderTarget().TriggerRedrawAll();
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - Updates the default background color from a COLORREF, format 0x00BBGGRR.
@ -475,7 +475,7 @@ try
_buffer->GetRenderTarget().TriggerRedrawAll();
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
til::color Terminal::GetDefaultBackground() const noexcept
{
@ -509,7 +509,7 @@ try
_buffer->GetRenderTarget().TriggerRedrawAll();
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
bool Terminal::EnableVT200MouseMode(const bool enabled) noexcept
{
@ -591,7 +591,7 @@ try
return true;
}
CATCH_LOG_RETURN_FALSE()
CATCH_RETURN_FALSE()
// Method Description:
// - Updates the buffer's current text attributes to start a hyperlink

View file

@ -50,7 +50,8 @@ std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute&
_defaultFg,
_defaultBg,
_screenReversed,
_blinkingState.IsBlinkingFaint());
_blinkingState.IsBlinkingFaint(),
_intenseIsBright);
colors.first |= 0xff000000;
// We only care about alpha for the default BG (which enables acrylic)
// If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque.

View file

@ -107,7 +107,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
};
struct Actions : ActionsT<Actions>
struct Actions : public HasScrollViewer<Actions>, ActionsT<Actions>
{
public:
Actions();

View file

@ -374,7 +374,7 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel MaxWidth="600"
Spacing="8"
Style="{StaticResource SettingsStackStyle}">

View file

@ -43,7 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_CALLBACK(AddNew, AddNewArgs);
};
struct AddProfile : AddProfileT<AddProfile>
struct AddProfile : public HasScrollViewer<AddProfile>, AddProfileT<AddProfile>
{
public:
AddProfile();

View file

@ -21,7 +21,7 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource SettingsStackStyle}">
<Button x:Uid="AddProfile_AddNewButton"
AutomationProperties.AutomationId="AddProfile_AddNewButton"

View file

@ -131,6 +131,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto backgroundImgCheckboxTooltip{ ToolTipService::GetToolTip(UseDesktopImageCheckBox()) };
Automation::AutomationProperties::SetFullDescription(UseDesktopImageCheckBox(), unbox_value<hstring>(backgroundImgCheckboxTooltip));
INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content");
}
// Method Description:
@ -256,6 +258,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowAllFonts" });
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" });
}
else if (settingName == L"IntenseTextStyle")
{
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" });
}
// YOU THERE ADDING A NEW APPEARANCE SETTING
// Make sure you add a block like
//
// else if (settingName == L"MyNewSetting")
// {
// _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentMyNewSetting" });
// }
//
// To make sure that changes to the AppearanceViewModel will
// propagate back up to the actual UI (in Appearances). The
// CurrentMyNewSetting properties are the ones that are bound in
// XAML. If you don't do this right (or only raise a property
// changed for "MyNewSetting"), then things like the reset
// button won't work right.
});
// make sure to send all the property changed events once here
@ -271,6 +291,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentFontFace" });
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowAllFonts" });
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" });
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" });
}
}

View file

@ -92,6 +92,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageOpacity);
OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageStretchMode);
OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageAlignment);
OBSERVABLE_PROJECTED_SETTING(_appearance, IntenseTextStyle);
private:
Model::AppearanceConfig _appearance;
@ -136,6 +137,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance, BackgroundImageStretchMode);
GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance, IntenseTextStyle);
private:
bool _ShowAllFonts;
void _UpdateBIAlignmentControl(const int32_t val);

View file

@ -45,6 +45,7 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Double, BackgroundImageOpacity);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.ConvergedAlignment, BackgroundImageAlignment);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Settings.Model.IntenseStyle, IntenseTextStyle);
}
[default_interface] runtimeclass Appearances : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged
@ -73,5 +74,8 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentFontFace { get; };
Windows.UI.Xaml.Controls.Slider BIOpacitySlider { get; };
IInspectable CurrentIntenseTextStyle;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> IntenseTextStyleList { get; };
}
}

View file

@ -428,5 +428,23 @@
</Grid>
</local:SettingContainer>
</StackPanel>
<!-- Grouping: Text Formatting -->
<StackPanel Style="{StaticResource PivotStackStyle}">
<TextBlock x:Uid="Appearance_TextFormattingHeader"
Style="{StaticResource SubtitleTextBlockStyle}" />
<!-- Intense is bold, bright -->
<local:SettingContainer x:Uid="Appearance_IntenseTextStyle"
Margin="0"
ClearSettingValue="{x:Bind Appearance.ClearIntenseTextStyle}"
HasSettingValue="{x:Bind Appearance.HasIntenseTextStyle, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.IntenseTextStyleOverrideSource, Mode=OneWay}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind IntenseTextStyleList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentIntenseTextStyle, Mode=TwoWay}" />
</local:SettingContainer>
</StackPanel>
</StackPanel>
</UserControl>

View file

@ -20,7 +20,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(winrt::hstring, LastSelectedScheme, L"");
};
struct ColorSchemes : ColorSchemesT<ColorSchemes>
struct ColorSchemes : public HasScrollViewer<ColorSchemes>, ColorSchemesT<ColorSchemes>
{
ColorSchemes();

View file

@ -93,7 +93,7 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Margin="{StaticResource StandardIndentMargin}"
Spacing="24">

View file

@ -18,7 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(Model::GlobalAppSettings, Globals, nullptr)
};
struct GlobalAppearance : GlobalAppearanceT<GlobalAppearance>
struct GlobalAppearance : public HasScrollViewer<GlobalAppearance>, GlobalAppearanceT<GlobalAppearance>
{
public:
GlobalAppearance();

View file

@ -24,7 +24,7 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Language -->
<local:SettingContainer x:Uid="Globals_Language"
@ -57,6 +57,11 @@
<ToggleSwitch IsOn="{x:Bind State.Globals.ShowTabsInTitlebar, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Show Acrylic in Tab Row -->
<local:SettingContainer x:Uid="Globals_AcrylicTabRow">
<ToggleSwitch IsOn="{x:Bind State.Globals.UseAcrylicInTabRow, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Show Title in Titlebar -->
<local:SettingContainer x:Uid="Globals_ShowTitleInTitlebar">
<ToggleSwitch IsOn="{x:Bind State.Globals.ShowTitleInTitlebar, Mode=TwoWay}" />

View file

@ -18,7 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(Model::GlobalAppSettings, Globals, nullptr)
};
struct Interaction : InteractionT<Interaction>
struct Interaction : public HasScrollViewer<Interaction>, InteractionT<Interaction>
{
Interaction();

View file

@ -24,7 +24,7 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Copy On Select -->
<local:SettingContainer x:Uid="Globals_CopyOnSelect"

View file

@ -47,4 +47,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto profile{ winrt::unbox_value<Model::Profile>(value) };
_State.Settings().GlobalSettings().DefaultProfile(profile.Guid());
}
winrt::Windows::Foundation::Collections::IObservableVector<IInspectable> Launch::DefaultProfiles() const
{
const auto allProfiles = _State.Settings().AllProfiles();
std::vector<IInspectable> profiles;
profiles.reserve(allProfiles.Size());
// Remove profiles from the selection which have been explicitly deleted.
// We do want to show hidden profiles though, as they are just hidden
// from menus, but still work as the startup profile for instance.
for (const auto& profile : allProfiles)
{
if (!profile.Deleted())
{
profiles.emplace_back(profile);
}
}
return winrt::single_threaded_observable_vector(std::move(profiles));
}
}

View file

@ -18,7 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
};
struct Launch : LaunchT<Launch>
struct Launch : public HasScrollViewer<Launch>, LaunchT<Launch>
{
public:
Launch();
@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
IInspectable CurrentDefaultProfile();
void CurrentDefaultProfile(const IInspectable& value);
winrt::Windows::Foundation::Collections::IObservableVector<IInspectable> DefaultProfiles() const;
WINRT_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr);

View file

@ -16,6 +16,9 @@ namespace Microsoft.Terminal.Settings.Editor
LaunchPageNavigationState State { get; };
IInspectable CurrentDefaultProfile;
// I wish this was a IObservableVector<Microsoft.Terminal.Settings.Model.Profile>, but:
// https://github.com/microsoft/microsoft-ui-xaml/issues/5395
IObservableVector<IInspectable> DefaultProfiles { get; };
IInspectable CurrentLaunchMode;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };

View file

@ -33,14 +33,14 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Default Profile -->
<local:SettingContainer x:Uid="Globals_DefaultProfile"
Margin="0">
<ComboBox x:Name="DefaultProfile"
ItemsSource="{x:Bind State.Settings.AllProfiles, Mode=OneWay}"
ItemsSource="{x:Bind DefaultProfiles}"
SelectedItem="{x:Bind CurrentDefaultProfile, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>

View file

@ -387,14 +387,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void MainPage::_InitializeProfilesList()
{
const auto menuItems = SettingsNav().MenuItems();
// Manually create a NavigationViewItem for each profile
// and keep a reference to them in a map so that we
// can easily modify the correct one when the associated
// profile changes.
for (const auto& profile : _settingsClone.AllProfiles())
{
auto navItem = _CreateProfileNavViewItem(_viewModelForProfile(profile, _settingsClone));
SettingsNav().MenuItems().Append(navItem);
if (!profile.Deleted())
{
auto navItem = _CreateProfileNavViewItem(_viewModelForProfile(profile, _settingsClone));
menuItems.Append(navItem);
}
}
// Top off (the end of the nav view) with the Add Profile item
@ -407,7 +412,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
icon.Glyph(L"\xE710");
addProfileItem.Icon(icon);
SettingsNav().MenuItems().Append(addProfileItem);
menuItems.Append(addProfileItem);
}
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile)

View file

@ -117,7 +117,17 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Frame x:Name="contentFrame"
Grid.Row="0" />
Grid.Row="0">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition>
<NavigationThemeTransition.DefaultNavigationTransitionInfo>
<DrillInNavigationTransitionInfo />
</NavigationThemeTransition.DefaultNavigationTransitionInfo>
</NavigationThemeTransition>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
<!-- Explicitly set the background color on grid to prevent the navigation animation from overflowing it -->
<Grid Grid.Row="1"
Height="100"

View file

@ -312,12 +312,12 @@
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>

View file

@ -18,11 +18,6 @@ using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Microsoft::Terminal::Settings::Model;
static const std::array<winrt::guid, 2> InBoxProfileGuids{
winrt::guid{ 0x61c54bbd, 0xc2c6, 0x5271, { 0x96, 0xe7, 0x00, 0x9a, 0x87, 0xff, 0x44, 0xbf } }, // Windows Powershell
winrt::guid{ 0x0caa0dad, 0x35be, 0x5f56, { 0xa8, 0xff, 0xaf, 0xce, 0xee, 0xaa, 0x61, 0x01 } } // Command Prompt
};
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
@ -221,25 +216,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
bool ProfileViewModel::CanDeleteProfile() const
{
const auto guid{ Guid() };
if (IsBaseLayer())
{
return false;
}
else if (std::find(std::begin(InBoxProfileGuids), std::end(InBoxProfileGuids), guid) != std::end(InBoxProfileGuids))
{
// in-box profile
return false;
}
else if (!Source().empty())
{
// dynamic profile
return false;
}
else
{
return true;
}
return !IsBaseLayer();
}
Editor::AppearanceViewModel ProfileViewModel::DefaultAppearance()
@ -383,21 +360,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
ProfileViewModel::UpdateFontList();
}
// Set the text disclaimer for the text box
hstring disclaimer{};
const auto guid{ _State.Profile().Guid() };
if (std::find(std::begin(InBoxProfileGuids), std::end(InBoxProfileGuids), guid) != std::end(InBoxProfileGuids))
{
// load disclaimer for in-box profiles
disclaimer = RS_(L"Profile_DeleteButtonDisclaimerInBox");
}
else if (!_State.Profile().Source().empty())
{
// load disclaimer for dynamic profiles
disclaimer = RS_(L"Profile_DeleteButtonDisclaimerDynamic");
}
DeleteButtonDisclaimer().Text(disclaimer);
// Check the use parent directory box if the starting directory is empty
if (_State.Profile().StartingDirectory().empty())
{

View file

@ -153,7 +153,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme> _Schemes;
};
struct Profiles : ProfilesT<Profiles>
struct Profiles : public HasScrollViewer<Profiles>, ProfilesT<Profiles>
{
public:
Profiles();

View file

@ -56,7 +56,7 @@
SelectionChanged="Pivot_SelectionChanged">
<!-- General Tab -->
<PivotItem x:Uid="Profile_General">
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource PivotStackStyle}">
<!-- Name -->
@ -144,88 +144,84 @@
</local:SettingContainer>
<!-- Delete Button -->
<StackPanel Margin="{StaticResource StandardControlMargin}">
<Button x:Name="DeleteButton"
IsEnabled="{x:Bind State.Profile.CanDeleteProfile}"
Style="{StaticResource DeleteButtonStyle}">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackground"
Color="Firebrick" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="#C23232" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="#A21212" />
<SolidColorBrush x:Key="ButtonForeground"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="White" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackground"
Color="Firebrick" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="#C23232" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="#A21212" />
<SolidColorBrush x:Key="ButtonForeground"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="White" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="ButtonBackground"
Color="{ThemeResource SystemColorButtonFaceColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonForeground"
Color="{ThemeResource SystemColorButtonTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="{ThemeResource SystemColorHighlightTextColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Content>
<StackPanel Orientation="Horizontal">
<FontIcon FontSize="{StaticResource StandardIconSize}"
Glyph="&#xE74D;" />
<TextBlock x:Uid="Profile_DeleteButton"
Margin="10,0,0,0" />
<Button x:Name="DeleteButton"
Margin="{StaticResource StandardControlMargin}"
Style="{StaticResource DeleteButtonStyle}"
Visibility="{x:Bind State.Profile.CanDeleteProfile}">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonBackground"
Color="Firebrick" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="#C23232" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="#A21212" />
<SolidColorBrush x:Key="ButtonForeground"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="White" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="ButtonBackground"
Color="Firebrick" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="#C23232" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="#A21212" />
<SolidColorBrush x:Key="ButtonForeground"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="White" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="White" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="ButtonBackground"
Color="{ThemeResource SystemColorButtonFaceColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPointerOver"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonBackgroundPressed"
Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="ButtonForeground"
Color="{ThemeResource SystemColorButtonTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPointerOver"
Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="ButtonForegroundPressed"
Color="{ThemeResource SystemColorHighlightTextColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Button.Resources>
<Button.Content>
<StackPanel Orientation="Horizontal">
<FontIcon FontSize="{StaticResource StandardIconSize}"
Glyph="&#xE74D;" />
<TextBlock x:Uid="Profile_DeleteButton"
Margin="10,0,0,0" />
</StackPanel>
</Button.Content>
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock x:Uid="Profile_DeleteConfirmationMessage"
Style="{StaticResource CustomFlyoutTextStyle}" />
<Button x:Uid="Profile_DeleteConfirmationButton"
Click="DeleteConfirmation_Click" />
</StackPanel>
</Button.Content>
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock x:Uid="Profile_DeleteConfirmationMessage"
Style="{StaticResource CustomFlyoutTextStyle}" />
<Button x:Uid="Profile_DeleteConfirmationButton"
Click="DeleteConfirmation_Click" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<TextBlock x:Name="DeleteButtonDisclaimer"
VerticalAlignment="Center"
Style="{StaticResource DisclaimerStyle}" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</ScrollViewer>
</PivotItem>
<!-- Appearance Tab -->
<PivotItem x:Uid="Profile_Appearance">
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel>
<!-- Control Preview -->
<Border x:Name="ControlPreview"
@ -397,7 +393,7 @@
<!-- Advanced Tab -->
<PivotItem x:Uid="Profile_Advanced">
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource PivotStackStyle}">
<!-- Suppress Application Title -->
<local:SettingContainer x:Uid="Profile_SuppressApplicationTitle"

View file

@ -32,7 +32,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget);
};
struct ReadOnlyActions : ReadOnlyActionsT<ReadOnlyActions>
struct ReadOnlyActions : public HasScrollViewer<ReadOnlyActions>, ReadOnlyActionsT<ReadOnlyActions>
{
public:
ReadOnlyActions();

View file

@ -182,7 +182,7 @@
</ResourceDictionary>
</Page.Resources>
<ScrollViewer>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource SettingsStackStyle}">
<TextBlock x:Uid="Globals_KeybindingsDisclaimer"
Style="{StaticResource DisclaimerStyle}" />

View file

@ -18,7 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(Model::GlobalAppSettings, Globals, nullptr)
};
struct Rendering : RenderingT<Rendering>
struct Rendering : public HasScrollViewer<Rendering>, RenderingT<Rendering>
{
Rendering();

Some files were not shown because too many files have changed in this diff Show more