Merge branch 'main' of https://github.com/microsoft/terminal into dev/pabhoj/cursor_light
This commit is contained in:
commit
8c183b4125
9
.github/actions/spelling/allow/apis.txt
vendored
9
.github/actions/spelling/allow/apis.txt
vendored
|
@ -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
|
||||
|
@ -133,12 +140,14 @@ SRWLOCK
|
|||
STDCPP
|
||||
STDMETHOD
|
||||
strchr
|
||||
strcpy
|
||||
streambuf
|
||||
strtoul
|
||||
Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
syscall
|
||||
TASKBARCREATED
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
tlg
|
||||
|
|
20
.github/actions/spelling/expect/expect.txt
vendored
20
.github/actions/spelling/expect/expect.txt
vendored
|
@ -169,6 +169,7 @@ brandings
|
|||
BRK
|
||||
Browsable
|
||||
bsearch
|
||||
Bspace
|
||||
bstr
|
||||
BTNFACE
|
||||
buf
|
||||
|
@ -270,10 +271,12 @@ cmder
|
|||
CMDEXT
|
||||
Cmdlet
|
||||
cmdline
|
||||
cmh
|
||||
CMOUSEBUTTONS
|
||||
cmp
|
||||
cmpeq
|
||||
cmt
|
||||
cmw
|
||||
cmyk
|
||||
CNL
|
||||
cnt
|
||||
|
@ -406,11 +409,13 @@ csbiex
|
|||
csharp
|
||||
CSHORT
|
||||
CSIDL
|
||||
Cspace
|
||||
csproj
|
||||
Csr
|
||||
csrmsg
|
||||
CSRSS
|
||||
csrutil
|
||||
css
|
||||
cstdarg
|
||||
cstddef
|
||||
cstdio
|
||||
|
@ -509,6 +514,8 @@ DECAWM
|
|||
DECCKM
|
||||
DECCOLM
|
||||
DECDHL
|
||||
decdld
|
||||
DECDLD
|
||||
DECDWL
|
||||
DECEKBD
|
||||
DECID
|
||||
|
@ -781,6 +788,7 @@ flyout
|
|||
fmodern
|
||||
fmtarg
|
||||
fmtid
|
||||
FNV
|
||||
FOLDERID
|
||||
FONTCHANGE
|
||||
fontdlg
|
||||
|
@ -789,6 +797,7 @@ FONTENUMPROC
|
|||
FONTFACE
|
||||
FONTFAMILY
|
||||
FONTHEIGHT
|
||||
FONTINFO
|
||||
fontlist
|
||||
FONTOK
|
||||
FONTSIZE
|
||||
|
@ -902,6 +911,7 @@ github
|
|||
gitlab
|
||||
gle
|
||||
globals
|
||||
GLYPHENTRY
|
||||
gmail
|
||||
GMEM
|
||||
GNUC
|
||||
|
@ -950,6 +960,7 @@ hdrstop
|
|||
HEIGHTSCROLL
|
||||
hfile
|
||||
hfont
|
||||
hfontresource
|
||||
hglobal
|
||||
hhh
|
||||
HHmm
|
||||
|
@ -1272,6 +1283,7 @@ locsrc
|
|||
locstudio
|
||||
Loewen
|
||||
LOGFONT
|
||||
LOGFONTA
|
||||
LOGFONTW
|
||||
logissue
|
||||
lowercased
|
||||
|
@ -1458,6 +1470,7 @@ Mul
|
|||
multiline
|
||||
munged
|
||||
munges
|
||||
murmurhash
|
||||
mutex
|
||||
mutexes
|
||||
muxes
|
||||
|
@ -1935,6 +1948,7 @@ realloc
|
|||
reamapping
|
||||
rects
|
||||
redef
|
||||
redefinable
|
||||
Redir
|
||||
redirector
|
||||
redist
|
||||
|
@ -1980,6 +1994,7 @@ rfc
|
|||
rftp
|
||||
rgb
|
||||
rgba
|
||||
RGBCOLOR
|
||||
rgbi
|
||||
rgci
|
||||
rgfae
|
||||
|
@ -2149,6 +2164,7 @@ SIGDN
|
|||
SINGLEFLAG
|
||||
SINGLETHREADED
|
||||
siup
|
||||
sixel
|
||||
SIZEBOX
|
||||
sizeof
|
||||
SIZESCROLL
|
||||
|
@ -2253,6 +2269,7 @@ SWMR
|
|||
SWP
|
||||
swprintf
|
||||
SYMED
|
||||
symlink
|
||||
SYNCPAINT
|
||||
sys
|
||||
syscalls
|
||||
|
@ -2754,6 +2771,7 @@ WTo
|
|||
wtof
|
||||
wtoi
|
||||
WTs
|
||||
WTSOFTFONT
|
||||
wtw
|
||||
wtypes
|
||||
Wubi
|
||||
|
@ -2783,6 +2801,7 @@ xes
|
|||
xff
|
||||
XFile
|
||||
XFORM
|
||||
xIcon
|
||||
XManifest
|
||||
XMath
|
||||
XMFLOAT
|
||||
|
@ -2815,6 +2834,7 @@ YCast
|
|||
YCENTER
|
||||
YCount
|
||||
YDPI
|
||||
yIcon
|
||||
yml
|
||||
YOffset
|
||||
YPosition
|
||||
|
|
|
@ -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": "all",
|
||||
"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"
|
||||
|
@ -240,6 +251,7 @@
|
|||
"identifyWindows",
|
||||
"moveFocus",
|
||||
"movePane",
|
||||
"swapPane",
|
||||
"moveTab",
|
||||
"newTab",
|
||||
"newWindow",
|
||||
|
@ -494,6 +506,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -517,13 +546,13 @@
|
|||
],
|
||||
"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",
|
||||
|
@ -984,6 +1013,7 @@
|
|||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/MovePaneAction" },
|
||||
{ "$ref": "#/definitions/SwapPaneAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" },
|
||||
{ "$ref": "#/definitions/SendInputAction" },
|
||||
{ "$ref": "#/definitions/SplitPaneAction" },
|
||||
|
@ -1195,6 +1225,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.",
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(LayerKeybindings);
|
||||
TEST_METHOD(UnbindKeybindings);
|
||||
|
||||
TEST_METHOD(LayerScancodeKeybindings);
|
||||
|
||||
TEST_METHOD(TestExplicitUnbind);
|
||||
|
||||
TEST_METHOD(TestArbitraryArgs);
|
||||
TEST_METHOD(TestSplitPaneArgs);
|
||||
|
||||
|
@ -95,23 +99,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 +246,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 +761,31 @@ 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ namespace TerminalAppLocalTests
|
|||
TEST_METHOD(MoveFocusFromZoomedPane);
|
||||
TEST_METHOD(CloseZoomedPane);
|
||||
|
||||
TEST_METHOD(MovePanes);
|
||||
TEST_METHOD(SwapPanes);
|
||||
|
||||
TEST_METHOD(NextMRUTab);
|
||||
TEST_METHOD(VerifyCommandPaletteTabSwitcherOrder);
|
||||
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,54 @@ 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>
|
||||
void WindowManager::RequestShowTrayIcon()
|
||||
{
|
||||
_peasant.RequestShowTrayIcon();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ask the monarch to hide its tray icon.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void WindowManager::RequestHideTrayIcon()
|
||||
{
|
||||
_peasant.RequestHideTrayIcon();
|
||||
}
|
||||
|
||||
bool WindowManager::DoesQuakeWindowExist()
|
||||
{
|
||||
const auto names = GetPeasantNames();
|
||||
for (const auto [id, name] : names)
|
||||
{
|
||||
if (name == QuakeWindowName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
void RequestShowTrayIcon();
|
||||
void 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 };
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,7 +349,7 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else
|
||||
{
|
||||
_MovePane(realArgs.Direction());
|
||||
_SwapPane(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
@ -400,16 +438,14 @@ 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 = {
|
||||
|
@ -420,8 +456,8 @@ void AppCommandlineArgs::_buildMovePaneParser()
|
|||
};
|
||||
|
||||
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->required();
|
||||
|
@ -430,12 +466,12 @@ void AppCommandlineArgs::_buildMovePaneParser()
|
|||
// 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:
|
||||
|
@ -625,6 +660,7 @@ bool AppCommandlineArgs::_noCommandsProvided()
|
|||
*_moveFocusShort ||
|
||||
*_movePaneCommand ||
|
||||
*_movePaneShort ||
|
||||
*_swapPaneCommand ||
|
||||
*_focusPaneCommand ||
|
||||
*_focusPaneShort ||
|
||||
*_newPaneShort.subcommand ||
|
||||
|
@ -653,12 +689,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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -203,12 +203,16 @@ namespace winrt::TerminalApp::implementation
|
|||
_isElevated = _isUserAdmin();
|
||||
_root = winrt::make_self<TerminalPage>();
|
||||
|
||||
_reloadSettings = std::make_shared<ThrottledFuncTrailing<>>(_root->Dispatcher(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() {
|
||||
_reloadSettings = std::make_shared<ThrottledFuncTrailing<>>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() {
|
||||
if (auto self{ weakSelf.get() })
|
||||
{
|
||||
self->_ReloadSettings();
|
||||
}
|
||||
});
|
||||
|
||||
_languageProfileNotifier = winrt::make_self<LanguageProfileNotifier>([this]() {
|
||||
_reloadSettings->Run();
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1125,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 {};
|
||||
}
|
||||
|
@ -1455,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
|
||||
#include "AppLogic.g.h"
|
||||
#include "FindTargetWindowResult.g.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "Jumplist.h"
|
||||
#include "LanguageProfileNotifier.h"
|
||||
#include "TerminalPage.h"
|
||||
|
||||
#include <inc/cppwinrt_utils.h>
|
||||
#include <ThrottledFunc.h>
|
||||
|
@ -89,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);
|
||||
|
||||
|
@ -110,12 +113,8 @@ namespace winrt::TerminalApp::implementation
|
|||
// ALSO: If you add any UIElements as roots here, make sure they're
|
||||
// updated in _ApplyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _reloadSettings;
|
||||
til::throttled_func_trailing<> _reloadState;
|
||||
winrt::hstring _settingsLoadExceptionText;
|
||||
HRESULT _settingsLoadedResult = S_OK;
|
||||
bool _loadedInitialSettings = false;
|
||||
|
@ -124,6 +123,15 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
::TerminalApp::AppCommandlineArgs _appArgs;
|
||||
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
|
||||
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _reloadSettings;
|
||||
til::throttled_func_trailing<> _reloadState;
|
||||
|
||||
// These fields invoke _reloadSettings and must be destroyed before _reloadSettings.
|
||||
// (C++ destroys members in reverse-declaration-order.)
|
||||
winrt::com_ptr<LanguageProfileNotifier> _languageProfileNotifier;
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view<const hstring> args,
|
||||
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
42
src/cascadia/TerminalApp/LanguageProfileNotifier.cpp
Normal file
42
src/cascadia/TerminalApp/LanguageProfileNotifier.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LanguageProfileNotifier.h"
|
||||
|
||||
using namespace winrt::TerminalApp::implementation;
|
||||
|
||||
LanguageProfileNotifier::LanguageProfileNotifier(std::function<void()>&& callback) :
|
||||
_callback{ std::move(callback) },
|
||||
_currentKeyboardLayout{ GetKeyboardLayout(0) }
|
||||
{
|
||||
const auto manager = wil::CoCreateInstance<ITfThreadMgr>(CLSID_TF_ThreadMgr);
|
||||
_source = manager.query<ITfSource>();
|
||||
if (FAILED(_source->AdviseSink(IID_ITfInputProcessorProfileActivationSink, static_cast<ITfInputProcessorProfileActivationSink*>(this), &_cookie)))
|
||||
{
|
||||
_cookie = TF_INVALID_COOKIE;
|
||||
THROW_LAST_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
LanguageProfileNotifier::~LanguageProfileNotifier()
|
||||
{
|
||||
if (_cookie != TF_INVALID_COOKIE)
|
||||
{
|
||||
_source->UnadviseSink(_cookie);
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP LanguageProfileNotifier::OnActivated(DWORD /*dwProfileType*/, LANGID /*langid*/, REFCLSID /*clsid*/, REFGUID /*catid*/, REFGUID /*guidProfile*/, HKL hkl, DWORD /*dwFlags*/)
|
||||
{
|
||||
if (hkl && hkl != _currentKeyboardLayout)
|
||||
{
|
||||
_currentKeyboardLayout = hkl;
|
||||
try
|
||||
{
|
||||
_callback();
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
21
src/cascadia/TerminalApp/LanguageProfileNotifier.h
Normal file
21
src/cascadia/TerminalApp/LanguageProfileNotifier.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
class LanguageProfileNotifier : public winrt::implements<LanguageProfileNotifier, ITfInputProcessorProfileActivationSink>
|
||||
{
|
||||
public:
|
||||
explicit LanguageProfileNotifier(std::function<void()>&& callback);
|
||||
~LanguageProfileNotifier();
|
||||
STDMETHODIMP OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags);
|
||||
|
||||
private:
|
||||
std::function<void()> _callback;
|
||||
wil::com_ptr<ITfSource> _source;
|
||||
DWORD _cookie = TF_INVALID_COOKIE;
|
||||
HKL _currentKeyboardLayout;
|
||||
};
|
||||
}
|
|
@ -220,7 +220,7 @@ bool Pane::ResizePane(const ResizeDirection& direction)
|
|||
// 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
|
||||
// - This method is _very_ similar to SwapPane. Both are trying to find the
|
||||
// right pane to move (focus) in a direction.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
|
@ -595,7 +595,7 @@ Pane::FocusNeighborSearch Pane::_FindFocusAndNeighbor(const FocusDirection& dire
|
|||
// - 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)
|
||||
bool Pane::SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
// If we're a leaf, do nothing. We can't possibly swap anything.
|
||||
if (_IsLeaf())
|
||||
|
@ -614,7 +614,7 @@ bool Pane::MovePane(const FocusDirection& direction)
|
|||
// and its neighbor must necessarily be contained within the same child.
|
||||
if (!DirectionMatchesSplit(direction, _splitState))
|
||||
{
|
||||
return _firstChild->MovePane(direction) || _secondChild->MovePane(direction);
|
||||
return _firstChild->SwapPane(direction) || _secondChild->SwapPane(direction);
|
||||
}
|
||||
|
||||
// Since the direction is the same as our split, it is possible that we must
|
||||
|
@ -1011,6 +1011,85 @@ void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const GU
|
|||
}
|
||||
}
|
||||
|
||||
// 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())
|
||||
{
|
||||
pane->WalkTree([](auto p) {
|
||||
if (p->_lastActive)
|
||||
{
|
||||
p->_FocusFirstChild();
|
||||
return true;
|
||||
}
|
||||
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:
|
||||
// - Closes one of our children. In doing so, takes the control from the other
|
||||
// child, and makes this pane a leaf node again.
|
||||
|
@ -1073,12 +1152,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 +1202,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 +1260,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)
|
||||
|
@ -1757,7 +1821,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 +1892,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)
|
||||
{
|
||||
|
@ -1874,7 +1938,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
|
|||
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
|
||||
_profile = std::nullopt;
|
||||
_control = { nullptr };
|
||||
_secondChild = std::make_shared<Pane>(profile, control);
|
||||
_secondChild = newPane;
|
||||
|
||||
_CreateRowColDefinitions();
|
||||
|
||||
|
@ -2548,6 +2612,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>>);
|
||||
|
|
|
@ -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,
|
||||
|
@ -62,7 +68,7 @@ public:
|
|||
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);
|
||||
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second);
|
||||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
|
@ -80,6 +86,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);
|
||||
|
@ -92,10 +102,38 @@ public:
|
|||
|
||||
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;
|
||||
|
@ -140,8 +178,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();
|
||||
|
@ -271,5 +308,6 @@ private:
|
|||
void _AssignChildNode(std::unique_ptr<LayoutSizeNode>& nodeField, const LayoutSizeNode* const newNode);
|
||||
};
|
||||
|
||||
friend struct winrt::TerminalApp::implementation::TerminalTab;
|
||||
friend class ::TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,13 @@ 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 profileGuid = newTabImpl->GetFocusedProfile())
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
const auto profile = _settings.FindProfile(profileGuid.value());
|
||||
if (profile != nullptr && !profile.Icon().empty())
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
|
||||
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
|
||||
|
@ -244,19 +214,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:
|
||||
// - 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)
|
||||
{
|
||||
// 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);
|
||||
_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, profileGuid, newControl);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the icon of the currently focused terminal control, and set its
|
||||
// tab's icon to that icon.
|
||||
|
|
45
src/cascadia/TerminalApp/TaskbarState.cpp
Normal file
45
src/cascadia/TerminalApp/TaskbarState.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
34
src/cascadia/TerminalApp/TaskbarState.h
Normal file
34
src/cascadia/TerminalApp/TaskbarState.h
Normal 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);
|
||||
}
|
15
src/cascadia/TerminalApp/TaskbarState.idl
Normal file
15
src/cascadia/TerminalApp/TaskbarState.idl
Normal 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; };
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@
|
|||
<Page Include="TabRowControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="TabHeaderControl.xaml">
|
||||
<Page Include="TabHeaderControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="HighlightedTextControl.xaml">
|
||||
|
@ -74,6 +74,7 @@
|
|||
<ClInclude Include="Commandline.h" />
|
||||
<ClInclude Include="CommandLinePaletteItem.h" />
|
||||
<ClInclude Include="Jumplist.h" />
|
||||
<ClInclude Include="LanguageProfileNotifier.h" />
|
||||
<ClInclude Include="MinMaxCloseControl.h">
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -89,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>
|
||||
|
@ -112,7 +116,7 @@
|
|||
<ClInclude Include="HighlightedTextControl.h">
|
||||
<DependentUpon>HighlightedTextControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HighlightedText.h" />
|
||||
<ClInclude Include="HighlightedText.h" />
|
||||
<ClInclude Include="ColorPickupFlyout.h">
|
||||
<DependentUpon>ColorPickupFlyout.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -139,7 +143,7 @@
|
|||
<ClInclude Include="AppLogic.h">
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h"/>
|
||||
<ClInclude Include="Toast.h" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
@ -149,6 +153,7 @@
|
|||
<ClCompile Include="AppCommandlineArgs.cpp" />
|
||||
<ClCompile Include="Commandline.cpp" />
|
||||
<ClCompile Include="Jumplist.cpp" />
|
||||
<ClCompile Include="LanguageProfileNotifier.cpp" />
|
||||
<ClCompile Include="MinMaxCloseControl.cpp">
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -164,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>
|
||||
|
@ -195,7 +203,7 @@
|
|||
<ClCompile Include="HighlightedTextControl.cpp">
|
||||
<DependentUpon>HighlightedTextControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HighlightedText.cpp" />
|
||||
<ClCompile Include="HighlightedText.cpp" />
|
||||
<ClCompile Include="ColorPickupFlyout.cpp">
|
||||
<DependentUpon>ColorPickupFlyout.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -256,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>
|
||||
|
@ -280,7 +289,7 @@
|
|||
<DependentUpon>HighlightedTextControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="HighlightedText.idl" />
|
||||
<Midl Include="HighlightedText.idl" />
|
||||
<Midl Include="ColorPickupFlyout.idl">
|
||||
<DependentUpon>ColorPickupFlyout.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
<ClCompile Include="Pane.cpp">
|
||||
<Filter>pane</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pane.LayoutSizeNode.cpp">
|
||||
<Filter>pane</Filter>
|
||||
</ClCompile>
|
||||
|
@ -23,7 +20,6 @@
|
|||
<ClCompile Include="Commandline.cpp" />
|
||||
<ClCompile Include="ColorHelper.cpp" />
|
||||
<ClCompile Include="DebugTapConnection.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
<ClCompile Include="Jumplist.cpp" />
|
||||
<ClCompile Include="Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
|
@ -49,10 +45,10 @@
|
|||
<ClCompile Include="HighlightedText.cpp">
|
||||
<Filter>highlightedText</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Toast.cpp" />
|
||||
<ClCompile Include="LanguageProfileNotifier.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Utils.h" />
|
||||
<ClInclude Include="TerminalWarnings.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="App.base.h">
|
||||
<Filter>app</Filter>
|
||||
|
@ -60,9 +56,6 @@
|
|||
<ClInclude Include="Pane.h">
|
||||
<Filter>pane</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Tab.h">
|
||||
<Filter>tab</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AppCommandlineArgs.h" />
|
||||
<ClInclude Include="Commandline.h" />
|
||||
<ClInclude Include="DebugTapConnection.h" />
|
||||
|
@ -92,14 +85,13 @@
|
|||
<ClInclude Include="HighlightedText.h">
|
||||
<Filter>highlightedText</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h" />
|
||||
<ClInclude Include="LanguageProfileNotifier.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="AppLogic.idl">
|
||||
<Filter>app</Filter>
|
||||
</Midl>
|
||||
<Midl Include="ActionArgs.idl">
|
||||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
<Midl Include="AppKeyBindings.idl">
|
||||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
|
@ -107,9 +99,6 @@
|
|||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
<Midl Include="IDirectKeyListener.idl" />
|
||||
<Midl Include="ITab.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
<Midl Include="SettingsTab.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
|
@ -125,6 +114,9 @@
|
|||
<Midl Include="TerminalTabStatus.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
<Midl Include="PaletteItemTemplateSelector.idl" />
|
||||
<Midl Include="TabBase.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
@ -160,9 +152,6 @@
|
|||
<Midl Include="ActionPaletteItem.idl">
|
||||
<Filter>commandPalette</Filter>
|
||||
</Midl>
|
||||
<Midl Include="FilterableListItem.idl">
|
||||
<Filter>commandPalette</Filter>
|
||||
</Midl>
|
||||
<Midl Include="CommandLinePaletteItem.idl">
|
||||
<Filter>commandPalette</Filter>
|
||||
</Midl>
|
||||
|
|
|
@ -980,11 +980,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 +997,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 +1062,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,17 +1122,17 @@ 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)
|
||||
void TerminalPage::_SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->MovePane(direction);
|
||||
terminalTab->SwapPane(direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1188,6 +1195,52 @@ 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.
|
||||
// Arguments:
|
||||
// - tabIdx: The target tab index.
|
||||
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.
|
||||
|
@ -1306,7 +1359,7 @@ namespace winrt::TerminalApp::implementation
|
|||
auto newControl = _InitControl(controlSettings, controlConnection);
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl, tab);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
|
@ -2077,29 +2130,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:
|
||||
|
|
|
@ -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,6 +188,7 @@ 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 _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
void _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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -30,14 +30,61 @@ namespace winrt::TerminalApp::implementation
|
|||
_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())
|
||||
|
@ -204,19 +262,6 @@ namespace winrt::TerminalApp::implementation
|
|||
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
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept
|
||||
{
|
||||
_AttachEventHandlersToPane(_rootPane);
|
||||
_AttachEventHandlersToControl(control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to update the settings of this tab's tree of panes.
|
||||
// Arguments:
|
||||
|
@ -426,10 +471,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 +485,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.
|
||||
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()
|
||||
{
|
||||
|
@ -521,7 +676,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// - direction: The direction to move the pane in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::MovePane(const FocusDirection& direction)
|
||||
void TerminalTab::SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
{
|
||||
|
@ -538,7 +693,7 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
_rootPane->MovePane(direction);
|
||||
_rootPane->SwapPane(direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,7 +706,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 +754,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 +789,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 +814,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 +833,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 +842,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 +861,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 +903,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 +992,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 +1012,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 +1026,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 +1051,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 +1073,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:
|
||||
|
|
|
@ -22,9 +22,10 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
public:
|
||||
TerminalTab(const GUID& 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;
|
||||
|
@ -33,6 +34,10 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
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,
|
||||
|
@ -54,7 +59,7 @@ 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);
|
||||
void 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);
|
||||
|
@ -83,6 +88,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void TogglePaneReadOnly();
|
||||
std::shared_ptr<Pane> GetActivePane() const;
|
||||
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
|
||||
|
||||
winrt::TerminalApp::TerminalTabStatus TabStatus()
|
||||
{
|
||||
|
@ -101,6 +107,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 +115,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 +140,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 +154,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);
|
||||
|
|
|
@ -66,6 +66,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
|||
#include <telemetry/ProjectTelemetry.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
|
||||
#include <msctf.h>
|
||||
#include <shellapi.h>
|
||||
#include <shobjidl_core.h>
|
||||
|
||||
|
|
|
@ -23,6 +23,16 @@ using namespace winrt::Windows::Graphics::Display;
|
|||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
// The minimum delay between updates to the scroll bar's values.
|
||||
// The updates are throttled to limit power usage.
|
||||
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the TSF input control.
|
||||
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100);
|
||||
|
||||
// The minimum delay between updating the locations of regex patterns
|
||||
constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500);
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
|
||||
|
@ -94,6 +104,61 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
auto pfnTerminalTaskbarProgressChanged = std::bind(&ControlCore::_terminalTaskbarProgressChanged, this);
|
||||
_terminal->TaskbarProgressChangedCallback(pfnTerminalTaskbarProgressChanged);
|
||||
|
||||
// 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
|
||||
// DispatcherQueue (on a new thread), so we can use that for throttled
|
||||
// functions.
|
||||
_dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
|
||||
if (!_dispatcher)
|
||||
{
|
||||
auto controller{ winrt::Windows::System::DispatcherQueueController::CreateOnDedicatedThread() };
|
||||
_dispatcher = controller.DispatcherQueue();
|
||||
}
|
||||
|
||||
// A few different events should be throttled, so they don't fire absolutely all the time:
|
||||
// * _tsfTryRedrawCanvas: When the cursor position moves, we need to
|
||||
// inform TSF, so it can move the canvas for the composition. We
|
||||
// throttle this so that we're not hopping across the process boundary
|
||||
// every time that the cursor moves.
|
||||
// * _updatePatternLocations: When there's new output, or we scroll the
|
||||
// viewport, we should re-check if there are any visible hyperlinks.
|
||||
// But we don't really need to do this every single time text is
|
||||
// output, we can limit this update to once every 500ms.
|
||||
// * _updateScrollBar: Same idea as the TSF update - we don't _really_
|
||||
// need to hop across the process boundary every time text is output.
|
||||
// We can throttle this to once every 8ms, which will get us out of
|
||||
// the way of the main output & rendering threads.
|
||||
_tsfTryRedrawCanvas = std::make_shared<ThrottledFuncTrailing<>>(
|
||||
_dispatcher,
|
||||
TsfRedrawInterval,
|
||||
[weakThis = get_weak()]() {
|
||||
if (auto core{ weakThis.get() }; !core->_IsClosing())
|
||||
{
|
||||
core->_CursorPositionChangedHandlers(*core, nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
_updatePatternLocations = std::make_shared<ThrottledFuncTrailing<>>(
|
||||
_dispatcher,
|
||||
UpdatePatternLocationsInterval,
|
||||
[weakThis = get_weak()]() {
|
||||
if (auto core{ weakThis.get() }; !core->_IsClosing())
|
||||
{
|
||||
core->UpdatePatternLocations();
|
||||
}
|
||||
});
|
||||
|
||||
_updateScrollBar = std::make_shared<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>>(
|
||||
_dispatcher,
|
||||
ScrollBarUpdateInterval,
|
||||
[weakThis = get_weak()](const auto& update) {
|
||||
if (auto core{ weakThis.get() }; !core->_IsClosing())
|
||||
{
|
||||
core->_ScrollPositionChangedHandlers(*core, update);
|
||||
}
|
||||
});
|
||||
|
||||
UpdateSettings(settings);
|
||||
}
|
||||
|
||||
|
@ -199,6 +264,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());
|
||||
|
||||
|
@ -535,6 +601,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
|
||||
_updateAntiAliasingMode(_renderEngine.get());
|
||||
|
||||
// Refresh our font with the renderer
|
||||
|
@ -564,6 +631,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();
|
||||
}
|
||||
}
|
||||
|
@ -739,6 +807,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.
|
||||
|
@ -747,11 +827,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() });
|
||||
|
@ -1103,15 +1178,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// TODO GH#9617: refine locking around pattern tree
|
||||
_terminal->ClearPatternTree();
|
||||
|
||||
_ScrollPositionChangedHandlers(*this,
|
||||
winrt::make<ScrollPositionChangedArgs>(viewTop,
|
||||
viewHeight,
|
||||
bufferSize));
|
||||
// Start the throttled update of our scrollbar.
|
||||
auto update{ winrt::make<ScrollPositionChangedArgs>(viewTop,
|
||||
viewHeight,
|
||||
bufferSize) };
|
||||
if (!_inUnitTests)
|
||||
{
|
||||
_updateScrollBar->Run(update);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ScrollPositionChangedHandlers(*this, update);
|
||||
}
|
||||
|
||||
// Additionally, start the throttled update of where our links are.
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
void ControlCore::_terminalCursorPositionChanged()
|
||||
{
|
||||
_CursorPositionChangedHandlers(*this, nullptr);
|
||||
// When the buffer's cursor moves, start the throttled func to
|
||||
// eventually dispatch a CursorPositionChanged event.
|
||||
_tsfTryRedrawCanvas->Run();
|
||||
}
|
||||
|
||||
void ControlCore::_terminalTaskbarProgressChanged()
|
||||
|
@ -1221,8 +1309,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
void ControlCore::Close()
|
||||
{
|
||||
if (!_closing.exchange(true))
|
||||
if (!_IsClosing())
|
||||
{
|
||||
_closing = true;
|
||||
|
||||
// Stop accepting new output and state changes before we disconnect everything.
|
||||
_connection.TerminalOutput(_connectionOutputEventToken);
|
||||
_connectionStateChangedRevoker.revoke();
|
||||
|
@ -1412,18 +1502,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
_terminal->Write(hstr);
|
||||
|
||||
// NOTE: We're raising an event here to inform the TermControl that
|
||||
// output has been received, so it can queue up a throttled
|
||||
// UpdatePatternLocations call. In the future, we should have the
|
||||
// _updatePatternLocations ThrottledFunc internal to this class, and
|
||||
// run on this object's dispatcher queue.
|
||||
//
|
||||
// We're not doing that quite yet, because the Core will eventually
|
||||
// be out-of-proc from the UI thread, and won't be able to just use
|
||||
// the UI thread as the dispatcher queue thread.
|
||||
//
|
||||
// See TODO: https://github.com/microsoft/terminal/projects/5#card-50760282
|
||||
_ReceivedOutputHandlers(*this, nullptr);
|
||||
// Start the throttled update of where our hyperlinks are.
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
private:
|
||||
bool _initializedTerminal{ false };
|
||||
std::atomic<bool> _closing{ false };
|
||||
bool _closing{ false };
|
||||
|
||||
TerminalConnection::ITerminalConnection _connection{ nullptr };
|
||||
event_token _connectionOutputEventToken;
|
||||
|
@ -207,6 +207,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
double _panelHeight{ 0 };
|
||||
double _compositionScale{ 0 };
|
||||
|
||||
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _updatePatternLocations;
|
||||
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> _updateScrollBar;
|
||||
|
||||
winrt::fire_and_forget _asyncCloseConnection();
|
||||
|
||||
void _setFontSize(int fontSize);
|
||||
|
@ -240,8 +245,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
void _connectionOutputHandler(const hstring& hstr);
|
||||
void _updateHoveredCell(const std::optional<til::point> terminalPosition);
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (_dispatcher)
|
||||
{
|
||||
// _closing isn't atomic and may only be accessed from the main thread.
|
||||
//
|
||||
// Though, the unit tests don't actually run in TAEF's main
|
||||
// thread, so we don't care when we're running in tests.
|
||||
assert(_inUnitTests || _dispatcher.HasThreadAccess());
|
||||
}
|
||||
#endif
|
||||
return _closing;
|
||||
}
|
||||
|
||||
friend class ControlUnitTests::ControlCoreTests;
|
||||
friend class ControlUnitTests::ControlInteractivityTests;
|
||||
bool _inUnitTests{ false };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9,5 +9,6 @@ namespace Microsoft.Terminal.Control
|
|||
interface IKeyBindings
|
||||
{
|
||||
Boolean TryKeyChord(KeyChord kc);
|
||||
Boolean IsKeyChordExplicitlyUnbound(KeyChord kc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,6 +30,14 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
VirtualKeyModifiers KeyChord::Modifiers() noexcept
|
||||
|
|
|
@ -33,7 +33,8 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
|||
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the TSF input control.
|
||||
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100);
|
||||
// This is already throttled primarily in the ControlCore, with a timeout of 100ms. We're adding another smaller one here, as the (potentially x-proc) call will come in off the UI thread
|
||||
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the locations of regex patterns
|
||||
constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500);
|
||||
|
@ -64,10 +65,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
|
||||
_core = _interactivity.Core();
|
||||
|
||||
// Use a manual revoker on the output event, so we can immediately stop
|
||||
// worrying about it on destruction.
|
||||
_coreOutputEventToken = _core.ReceivedOutput({ this, &TermControl::_coreReceivedOutput });
|
||||
|
||||
// These events might all be triggered by the connection, but that
|
||||
// should be drained and closed before we complete destruction. So these
|
||||
// are safe.
|
||||
|
@ -104,37 +101,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
}
|
||||
});
|
||||
|
||||
// Many of these ThrottledFunc's should be inside ControlCore. However,
|
||||
// currently they depend on the Dispatcher() of the UI thread, which the
|
||||
// Core eventually won't have access to. When we get to
|
||||
// https://github.com/microsoft/terminal/projects/5#card-50760282
|
||||
// then we'll move the applicable ones.
|
||||
//
|
||||
// These four throttled functions are triggered by terminal output and interact with the UI.
|
||||
// Get our dispatcher. This will get us the same dispatcher as
|
||||
// TermControl::Dispatcher().
|
||||
auto dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
|
||||
|
||||
// These three throttled functions are triggered by terminal output and interact with the UI.
|
||||
// Since Close() is the point after which we are removed from the UI, but before the
|
||||
// destructor has run, we MUST check control->_IsClosing() before actually doing anything.
|
||||
_tsfTryRedrawCanvas = std::make_shared<ThrottledFuncTrailing<>>(
|
||||
Dispatcher(),
|
||||
TsfRedrawInterval,
|
||||
[weakThis = get_weak()]() {
|
||||
if (auto control{ weakThis.get() }; !control->_IsClosing())
|
||||
{
|
||||
control->TSFInputControl().TryRedrawCanvas();
|
||||
}
|
||||
});
|
||||
|
||||
_updatePatternLocations = std::make_shared<ThrottledFuncTrailing<>>(
|
||||
Dispatcher(),
|
||||
UpdatePatternLocationsInterval,
|
||||
[weakThis = get_weak()]() {
|
||||
if (auto control{ weakThis.get() }; !control->_IsClosing())
|
||||
{
|
||||
control->_core.UpdatePatternLocations();
|
||||
}
|
||||
});
|
||||
|
||||
_playWarningBell = std::make_shared<ThrottledFuncLeading>(
|
||||
Dispatcher(),
|
||||
dispatcher,
|
||||
TerminalWarningBellInterval,
|
||||
[weakThis = get_weak()]() {
|
||||
if (auto control{ weakThis.get() }; !control->_IsClosing())
|
||||
|
@ -144,7 +119,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
});
|
||||
|
||||
_updateScrollBar = std::make_shared<ThrottledFuncTrailing<ScrollBarUpdate>>(
|
||||
Dispatcher(),
|
||||
dispatcher,
|
||||
ScrollBarUpdateInterval,
|
||||
[weakThis = get_weak()](const auto& update) {
|
||||
if (auto control{ weakThis.get() }; !control->_IsClosing())
|
||||
|
@ -540,9 +515,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
// 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())
|
||||
if (const auto& interactivityAutoPeer{ _interactivity.OnCreateAutomationPeer() })
|
||||
{
|
||||
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, interactivityAutoPeer);
|
||||
auto margins{ SwapChainPanel().Margin() };
|
||||
|
||||
Core::Padding padding{ margins.Left,
|
||||
margins.Top,
|
||||
margins.Right,
|
||||
margins.Bottom };
|
||||
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
|
||||
return _automationPeer;
|
||||
}
|
||||
}
|
||||
|
@ -786,28 +767,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;
|
||||
|
@ -1276,23 +1268,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
void TermControl::_coreReceivedOutput(const IInspectable& /*sender*/,
|
||||
const IInspectable& /*args*/)
|
||||
{
|
||||
// Queue up a throttled UpdatePatternLocations call. In the future, we
|
||||
// should have the _updatePatternLocations ThrottledFunc internal to
|
||||
// ControlCore, and run on that object's dispatcher queue.
|
||||
//
|
||||
// We're not doing that quite yet, because the Core will eventually
|
||||
// be out-of-proc from the UI thread, and won't be able to just use
|
||||
// the UI thread as the dispatcher queue thread.
|
||||
//
|
||||
// THIS IS CALLED ON EVERY STRING OF TEXT OUTPUT TO THE TERMINAL. Think
|
||||
// twice before adding anything here.
|
||||
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Reset the font size of the terminal to its default size.
|
||||
// Arguments:
|
||||
|
@ -1330,8 +1305,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_updateScrollBar->ModifyPending([](auto& update) {
|
||||
update.newValue.reset();
|
||||
});
|
||||
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1665,7 +1638,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
update.newValue = args.ViewTop();
|
||||
|
||||
_updateScrollBar->Run(update);
|
||||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1673,10 +1645,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// to be where the current cursor position is.
|
||||
// Arguments:
|
||||
// - N/A
|
||||
void TermControl::_CursorPositionChanged(const IInspectable& /*sender*/,
|
||||
const IInspectable& /*args*/)
|
||||
winrt::fire_and_forget TermControl::_CursorPositionChanged(const IInspectable& /*sender*/,
|
||||
const IInspectable& /*args*/)
|
||||
{
|
||||
_tsfTryRedrawCanvas->Run();
|
||||
// Prior to GH#10187, this fired a trailing throttled func to update the
|
||||
// TSF canvas only every 100ms. Now, the throttling occurs on the
|
||||
// ControlCore side. If we're told to update the cursor position, we can
|
||||
// just go ahead and do it.
|
||||
// This can come in off the COM thread - hop back to the UI thread.
|
||||
auto weakThis{ get_weak() };
|
||||
co_await resume_foreground(Dispatcher());
|
||||
if (auto control{ weakThis.get() }; !control->_IsClosing())
|
||||
{
|
||||
control->TSFInputControl().TryRedrawCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
hstring TermControl::Title()
|
||||
|
@ -1730,8 +1712,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
_closing = true;
|
||||
|
||||
_core.ReceivedOutput(_coreOutputEventToken);
|
||||
_RestorePointerCursorHandlers(*this, nullptr);
|
||||
|
||||
// Disconnect the TSF input control so it doesn't receive EditContext events.
|
||||
TSFInputControl().Close();
|
||||
_autoScrollTimer.Stop();
|
||||
|
|
|
@ -155,8 +155,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
bool _focused{ false };
|
||||
bool _initializedTerminal{ false };
|
||||
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _updatePatternLocations;
|
||||
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
|
||||
|
||||
struct ScrollBarUpdate
|
||||
|
@ -166,7 +164,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
double newMinimum;
|
||||
double newViewportSize;
|
||||
};
|
||||
|
||||
std::shared_ptr<ThrottledFuncTrailing<ScrollBarUpdate>> _updateScrollBar;
|
||||
|
||||
bool _isInternalScrollBarUpdate;
|
||||
|
||||
// Auto scroll occurs when user, while selecting, drags cursor outside
|
||||
|
@ -183,14 +183,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
std::optional<Windows::UI::Xaml::DispatcherTimer> _cursorTimer;
|
||||
std::optional<Windows::UI::Xaml::DispatcherTimer> _blinkTimer;
|
||||
|
||||
event_token _coreOutputEventToken;
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -235,7 +238,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
void _TerminalTabColorChanged(const std::optional<til::color> color);
|
||||
|
||||
void _ScrollPositionChanged(const IInspectable& sender, const Control::ScrollPositionChangedArgs& args);
|
||||
void _CursorPositionChanged(const IInspectable& sender, const IInspectable& args);
|
||||
winrt::fire_and_forget _CursorPositionChanged(const IInspectable& sender, const IInspectable& args);
|
||||
|
||||
bool _CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
bool _ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
|
@ -267,7 +270,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
const int fontHeight,
|
||||
const bool isInitialChange);
|
||||
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
|
||||
void _coreReceivedOutput(const IInspectable& sender, const IInspectable& args);
|
||||
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
|
||||
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -66,5 +66,6 @@ namespace Microsoft.Terminal.Core
|
|||
Microsoft.Terminal.Core.Color CursorColor;
|
||||
CursorStyle CursorShape;
|
||||
UInt32 CursorHeight;
|
||||
Boolean IntenseIsBright;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -283,6 +283,7 @@ private:
|
|||
bool _suppressApplicationTitle;
|
||||
bool _bracketedPasteMode;
|
||||
bool _trimBlockSelection;
|
||||
bool _intenseIsBright;
|
||||
|
||||
size_t _taskbarState;
|
||||
size_t _taskbarProgress;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -374,7 +374,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel MaxWidth="600"
|
||||
Spacing="8"
|
||||
Style="{StaticResource SettingsStackStyle}">
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<Button x:Uid="AddProfile_AddNewButton"
|
||||
AutomationProperties.AutomationId="AddProfile_AddNewButton"
|
||||
|
|
|
@ -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" });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Margin="{StaticResource StandardIndentMargin}"
|
||||
Spacing="24">
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<!-- Language -->
|
||||
<local:SettingContainer x:Uid="Globals_Language"
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<!-- Copy On Select -->
|
||||
<local:SettingContainer x:Uid="Globals_CopyOnSelect"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel>
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<!-- Default Profile -->
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
SelectionChanged="Pivot_SelectionChanged">
|
||||
<!-- General Tab -->
|
||||
<PivotItem x:Uid="Profile_General">
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Style="{StaticResource PivotStackStyle}">
|
||||
|
||||
<!-- Name -->
|
||||
|
@ -225,7 +225,7 @@
|
|||
|
||||
<!-- Appearance Tab -->
|
||||
<PivotItem x:Uid="Profile_Appearance">
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel>
|
||||
<!-- Control Preview -->
|
||||
<Border x:Name="ControlPreview"
|
||||
|
@ -397,7 +397,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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -182,7 +182,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<TextBlock x:Uid="Globals_KeybindingsDisclaimer"
|
||||
Style="{StaticResource DisclaimerStyle}" />
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<ScrollViewer ViewChanging="ViewChanging">
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<TextBlock x:Uid="Globals_RenderingDisclaimer"
|
||||
Style="{StaticResource DisclaimerStyle}" />
|
||||
|
|
|
@ -232,7 +232,7 @@
|
|||
<comment>Header for a control to toggle if the app should always show the tabs (similar to a website browser).</comment>
|
||||
</data>
|
||||
<data name="Globals_AlwaysShowTabs.HelpText" xml:space="preserve">
|
||||
<value>When unchecked, the tab bar will appear when a new tab is created.</value>
|
||||
<value>When disabled, the tab bar will appear when a new tab is created.</value>
|
||||
<comment>A description for what the "always show tabs" setting does. Presented near "Globals_AlwaysShowTabs.Header".</comment>
|
||||
</data>
|
||||
<data name="Globals_CopyOnSelect.Header" xml:space="preserve">
|
||||
|
@ -272,7 +272,7 @@
|
|||
<comment>Header for a control to toggle the "force full repaint" setting. When enabled, the app renders new content between screen frames.</comment>
|
||||
</data>
|
||||
<data name="Globals_ForceFullRepaint.HelpText" xml:space="preserve">
|
||||
<value>When unchecked, the terminal will render only the updates to the screen between frames.</value>
|
||||
<value>When disabled, the terminal will render only the updates to the screen between frames.</value>
|
||||
<comment>A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header".</comment>
|
||||
</data>
|
||||
<data name="Globals_InitialCols.Header" xml:space="preserve">
|
||||
|
@ -340,7 +340,7 @@
|
|||
<comment>Header for a control to toggle whether the title bar should be shown or not. Changing this setting requires the user to relaunch the app.</comment>
|
||||
</data>
|
||||
<data name="Globals_ShowTitlebar.HelpText" xml:space="preserve">
|
||||
<value>When unchecked, the title bar will appear above the tabs.</value>
|
||||
<value>When disabled, the title bar will appear above the tabs.</value>
|
||||
<comment>A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header".</comment>
|
||||
</data>
|
||||
<data name="Globals_ShowTitleInTitlebar.Header" xml:space="preserve">
|
||||
|
@ -348,7 +348,7 @@
|
|||
<comment>Header for a control to toggle whether the terminal's title is shown as the application title, or not.</comment>
|
||||
</data>
|
||||
<data name="Globals_ShowTitleInTitlebar.HelpText" xml:space="preserve">
|
||||
<value>When unchecked, the title bar will be 'Windows Terminal'.</value>
|
||||
<value>When disabled, the title bar will be 'Windows Terminal'.</value>
|
||||
<comment>A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"}</comment>
|
||||
</data>
|
||||
<data name="Globals_SnapToGridOnResize.Header" xml:space="preserve">
|
||||
|
@ -356,7 +356,7 @@
|
|||
<comment>Header for a control to toggle whether the terminal snaps the window to the character grid when resizing, or not.</comment>
|
||||
</data>
|
||||
<data name="Globals_SnapToGridOnResize.HelpText" xml:space="preserve">
|
||||
<value>When unchecked, the window will resize smoothly.</value>
|
||||
<value>When disabled, the window will resize smoothly.</value>
|
||||
<comment>A description for what the "snap to grid on resize" setting does. Presented near "Globals_SnapToGridOnResize.Header".</comment>
|
||||
</data>
|
||||
<data name="Globals_SoftwareRendering.Header" xml:space="preserve">
|
||||
|
@ -364,7 +364,7 @@
|
|||
<comment>Header for a control to toggle whether the terminal should use software to render content instead of the hardware.</comment>
|
||||
</data>
|
||||
<data name="Globals_SoftwareRendering.HelpText" xml:space="preserve">
|
||||
<value>When checked, the terminal will use the software renderer (a.k.a. WARP) instead of the hardware one.</value>
|
||||
<value>When enabled, the terminal will use the software renderer (a.k.a. WARP) instead of the hardware one.</value>
|
||||
<comment>A description for what the "software rendering" setting does. Presented near "Globals_SoftwareRendering.Header".</comment>
|
||||
</data>
|
||||
<data name="Globals_StartOnUserLogin.Header" xml:space="preserve">
|
||||
|
@ -372,7 +372,7 @@
|
|||
<comment>Header for a control to toggle whether the app should launch when the user's machine starts up, or not.</comment>
|
||||
</data>
|
||||
<data name="Globals_StartOnUserLogin.HelpText" xml:space="preserve">
|
||||
<value>When checked, this enables the launch of Windows Terminal at machine startup.</value>
|
||||
<value>When enabled, this enables the launch of Windows Terminal at machine startup.</value>
|
||||
<comment>A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header".</comment>
|
||||
</data>
|
||||
<data name="Globals_AlwaysOnTop.Header" xml:space="preserve">
|
||||
|
@ -480,7 +480,7 @@
|
|||
<comment>Header for a control to toggle whether the app treats ctrl+alt as the AltGr (also known as the Alt Graph) modifier key found on keyboards. {Locked="AltGr"}</comment>
|
||||
</data>
|
||||
<data name="Profile_AltGrAliasing.HelpText" xml:space="preserve">
|
||||
<value>By default Windows treats Ctrl+Alt as an alias for AltGr. When unchecked, this behavior will be disabled.</value>
|
||||
<value>By default Windows treats Ctrl+Alt as an alias for AltGr. When disabled, this behavior will be disabled.</value>
|
||||
<comment>A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header".</comment>
|
||||
</data>
|
||||
<data name="Profile_AntialiasingMode.Header" xml:space="preserve">
|
||||
|
@ -700,7 +700,7 @@
|
|||
<comment>Header for a control to toggle whether the profile is shown in a dropdown menu, or not.</comment>
|
||||
</data>
|
||||
<data name="Profile_Hidden.HelpText" xml:space="preserve">
|
||||
<value>If checked, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamically generated profiles, while leaving them in your settings file.</value>
|
||||
<value>If enabled, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamically generated profiles, while leaving them in your settings file.</value>
|
||||
<comment>A description for what the "hidden" setting does. Presented near "Profile_Hidden".</comment>
|
||||
</data>
|
||||
<data name="Profile_HistorySize.Header" xml:space="preserve">
|
||||
|
@ -736,7 +736,7 @@
|
|||
<comment>Header for a control to toggle classic CRT display effects, which gives the terminal a retro look.</comment>
|
||||
</data>
|
||||
<data name="Profile_RetroTerminalEffect.HelpText" xml:space="preserve">
|
||||
<value>When checked, enables retro terminal effects such as glowing text and scan lines.</value>
|
||||
<value>When enabled, enables retro terminal effects such as glowing text and scan lines.</value>
|
||||
<comment>A description for what the "retro terminal effects" setting does. Presented near "Profile_RetroTerminalEffect".</comment>
|
||||
</data>
|
||||
<data name="Profile_ScrollbarVisibility.Header" xml:space="preserve">
|
||||
|
@ -772,7 +772,7 @@
|
|||
<comment>A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process.</comment>
|
||||
</data>
|
||||
<data name="Profile_StartingDirectoryUseParentCheckbox.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>If checked, this profile will spawn in the directory from which Windows Terminal was launched.</value>
|
||||
<value>If enabled, this profile will spawn in the directory from which Windows Terminal was launched.</value>
|
||||
<comment>A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox".</comment>
|
||||
</data>
|
||||
<data name="Profile_SuppressApplicationTitle.Header" xml:space="preserve">
|
||||
|
@ -808,7 +808,7 @@
|
|||
<comment>A supplementary setting to the "background image" setting. When enabled, the OS desktop wallpaper is used as the background image. Presented near "Profile_BackgroundImage".</comment>
|
||||
</data>
|
||||
<data name="Profile_UseDesktopImage.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>When checked, use the desktop wallpaper image as the background image for the terminal.</value>
|
||||
<value>When enabled, use the desktop wallpaper image as the background image for the terminal.</value>
|
||||
<comment>A description for what the supplementary "use desktop image" setting does. Presented near "Profile_UseDesktopImage".</comment>
|
||||
</data>
|
||||
<data name="Settings_ResetSettingsButton.Content" xml:space="preserve">
|
||||
|
@ -1111,7 +1111,7 @@
|
|||
<comment>A supplementary setting to the "font face" setting. Toggling this control updates the font face control to show all of the fonts installed.</comment>
|
||||
</data>
|
||||
<data name="Profile_FontFaceShowAllFonts.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>If checked, show all installed fonts in the list above. Otherwise, only show the list of monospace fonts.</value>
|
||||
<value>If enabled, show all installed fonts in the list above. Otherwise, only show the list of monospace fonts.</value>
|
||||
<comment>A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts".</comment>
|
||||
</data>
|
||||
<data name="Profile_CreateUnfocusedAppearanceButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
|
@ -1186,4 +1186,28 @@
|
|||
<value>Shortcut</value>
|
||||
<comment>The label for a "key chord listener" control that sets the keys a key binding is bound to.</comment>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Appearance_TextFormattingHeader.Text" xml:space="preserve">
|
||||
<value>Text Formatting</value>
|
||||
<comment>Header for a control to how text is formatted</comment>
|
||||
</data>
|
||||
<data name="Appearance_IntenseTextStyle.Header" xml:space="preserve">
|
||||
<value>Intense Text Format</value>
|
||||
<comment>Header for a control to select how "intense" text is formatted (bold, bright, both or none)</comment>
|
||||
</data>
|
||||
<data name="Appearance_IntenseTextStyleNone.Content" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "intense text format" setting. When selected, "intense" text will not be rendered differently</comment>
|
||||
</data>
|
||||
<data name="Appearance_IntenseTextStyleBold.Content" xml:space="preserve">
|
||||
<value>Bold</value>
|
||||
<comment>An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as bold text</comment>
|
||||
</data>
|
||||
<data name="Appearance_IntenseTextStyleBright.Content" xml:space="preserve">
|
||||
<value>Bright</value>
|
||||
<comment>An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered in a brighter color</comment>
|
||||
</data>
|
||||
<data name="Appearance_IntenseTextStyleAll.Content" xml:space="preserve">
|
||||
<value>Both</value>
|
||||
<comment>An option to choose from for the "intense text format" setting. When selected, "intense" text will be rendered as both bold text and in a brighter color</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -138,3 +138,41 @@ namespace winrt::Microsoft::Terminal::Settings
|
|||
winrt::hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable);
|
||||
winrt::hstring LocalizedNameForEnumName(const std::wstring_view sectionAndType, const std::wstring_view enumValue, const std::wstring_view propertyType);
|
||||
}
|
||||
|
||||
// BODGY!
|
||||
//
|
||||
// The following function and struct are a workaround for GH#9320.
|
||||
//
|
||||
// DismissAllPopups can be used to dismiss all popups for a particular UI
|
||||
// element. However, we've got a bunch of pages with scroll viewers that may or
|
||||
// may not have popups in them. Rather than define the same exact body for all
|
||||
// their ViewChanging events, the HasScrollViewer struct will just do it for
|
||||
// you!
|
||||
inline void DismissAllPopups(winrt::Windows::UI::Xaml::XamlRoot const& xamlRoot)
|
||||
{
|
||||
const auto popups{ winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetOpenPopupsForXamlRoot(xamlRoot) };
|
||||
for (const auto& p : popups)
|
||||
{
|
||||
p.IsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct HasScrollViewer
|
||||
{
|
||||
// When the ScrollViewer scrolls, dismiss any popups we might have.
|
||||
void ViewChanging(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
const winrt::Windows::UI::Xaml::Controls::ScrollViewerViewChangingEventArgs& /*e*/)
|
||||
{
|
||||
// Inside this struct, we can't get at the XamlRoot() that our subclass
|
||||
// implements. I mean, _we_ can, but when XAML does it's code
|
||||
// generation, _XAML_ won't be able to figure it out.
|
||||
//
|
||||
// Fortunately for us, we don't need to! The sender is a UIElement, so
|
||||
// we can just get _their_ XamlRoot().
|
||||
if (const auto& uielem{ sender.try_as<winrt::Windows::UI::Xaml::UIElement>() })
|
||||
{
|
||||
DismissAllPopups(uielem.XamlRoot());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ static constexpr std::string_view ExecuteCommandlineKey{ "wt" };
|
|||
static constexpr std::string_view FindKey{ "find" };
|
||||
static constexpr std::string_view MoveFocusKey{ "moveFocus" };
|
||||
static constexpr std::string_view MovePaneKey{ "movePane" };
|
||||
static constexpr std::string_view SwapPaneKey{ "swapPane" };
|
||||
static constexpr std::string_view NewTabKey{ "newTab" };
|
||||
static constexpr std::string_view NextTabKey{ "nextTab" };
|
||||
static constexpr std::string_view OpenNewTabDropdownKey{ "openNewTabDropdown" };
|
||||
|
@ -323,6 +324,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{ ShortcutAction::Invalid, L"" },
|
||||
{ ShortcutAction::MoveFocus, RS_(L"MoveFocusCommandKey") },
|
||||
{ ShortcutAction::MovePane, RS_(L"MovePaneCommandKey") },
|
||||
{ ShortcutAction::SwapPane, RS_(L"SwapPaneCommandKey") },
|
||||
{ ShortcutAction::NewTab, RS_(L"NewTabCommandKey") },
|
||||
{ ShortcutAction::NextTab, RS_(L"NextTabCommandKey") },
|
||||
{ ShortcutAction::OpenNewTabDropdown, RS_(L"OpenNewTabDropdownCommandKey") },
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "ResizePaneArgs.g.cpp"
|
||||
#include "MoveFocusArgs.g.cpp"
|
||||
#include "MovePaneArgs.g.cpp"
|
||||
#include "SwapPaneArgs.g.cpp"
|
||||
#include "AdjustFontSizeArgs.g.cpp"
|
||||
#include "SendInputArgs.g.cpp"
|
||||
#include "SplitPaneArgs.g.cpp"
|
||||
|
@ -229,6 +230,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
};
|
||||
}
|
||||
|
||||
winrt::hstring MovePaneArgs::GenerateName() const
|
||||
{
|
||||
return winrt::hstring{
|
||||
fmt::format(L"{}, tab index:{}", RS_(L"MovePaneCommandKey"), TabIndex())
|
||||
};
|
||||
}
|
||||
|
||||
winrt::hstring SwitchToTabArgs::GenerateName() const
|
||||
{
|
||||
if (TabIndex() == UINT32_MAX)
|
||||
|
@ -291,7 +299,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
};
|
||||
}
|
||||
|
||||
winrt::hstring MovePaneArgs::GenerateName() const
|
||||
winrt::hstring SwapPaneArgs::GenerateName() const
|
||||
{
|
||||
winrt::hstring directionString;
|
||||
switch (Direction())
|
||||
|
@ -309,10 +317,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
directionString = RS_(L"DirectionDown");
|
||||
break;
|
||||
case FocusDirection::Previous:
|
||||
return RS_(L"MovePaneToLastUsedPane");
|
||||
return RS_(L"SwapPaneToLastUsedPane");
|
||||
}
|
||||
return winrt::hstring{
|
||||
fmt::format(std::wstring_view(RS_(L"MovePaneWithArgCommandKey")),
|
||||
fmt::format(std::wstring_view(RS_(L"SwapPaneWithArgCommandKey")),
|
||||
directionString)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "ResizePaneArgs.g.h"
|
||||
#include "MoveFocusArgs.g.h"
|
||||
#include "MovePaneArgs.g.h"
|
||||
#include "SwapPaneArgs.g.h"
|
||||
#include "AdjustFontSizeArgs.g.h"
|
||||
#include "SendInputArgs.g.h"
|
||||
#include "SplitPaneArgs.g.h"
|
||||
|
@ -286,6 +287,57 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
};
|
||||
|
||||
struct MovePaneArgs : public MovePaneArgsT<MovePaneArgs>
|
||||
{
|
||||
MovePaneArgs() = default;
|
||||
MovePaneArgs(uint32_t& tabIndex) :
|
||||
_TabIndex{ tabIndex } {};
|
||||
ACTION_ARG(uint32_t, TabIndex, 0);
|
||||
|
||||
static constexpr std::string_view TabIndexKey{ "index" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<MovePaneArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_TabIndex == _TabIndex;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static FromJsonResult FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MovePaneArgs>();
|
||||
JsonUtils::GetValueForKey(json, TabIndexKey, args->_TabIndex);
|
||||
return { *args, {} };
|
||||
}
|
||||
static Json::Value ToJson(const IActionArgs& val)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
const auto args{ get_self<MovePaneArgs>(val) };
|
||||
JsonUtils::SetValueForKey(json, TabIndexKey, args->_TabIndex);
|
||||
return json;
|
||||
}
|
||||
IActionArgs Copy() const
|
||||
{
|
||||
auto copy{ winrt::make_self<MovePaneArgs>() };
|
||||
copy->_TabIndex = _TabIndex;
|
||||
return *copy;
|
||||
}
|
||||
size_t Hash() const
|
||||
{
|
||||
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TabIndex());
|
||||
}
|
||||
};
|
||||
|
||||
struct SwitchToTabArgs : public SwitchToTabArgsT<SwitchToTabArgs>
|
||||
{
|
||||
SwitchToTabArgs() = default;
|
||||
|
@ -452,10 +504,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
};
|
||||
|
||||
struct MovePaneArgs : public MovePaneArgsT<MovePaneArgs>
|
||||
struct SwapPaneArgs : public SwapPaneArgsT<SwapPaneArgs>
|
||||
{
|
||||
MovePaneArgs() = default;
|
||||
MovePaneArgs(Model::FocusDirection direction) :
|
||||
SwapPaneArgs() = default;
|
||||
SwapPaneArgs(Model::FocusDirection direction) :
|
||||
_Direction{ direction } {};
|
||||
|
||||
ACTION_ARG(Model::FocusDirection, Direction, FocusDirection::None);
|
||||
|
@ -467,7 +519,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<MovePaneArgs>();
|
||||
auto otherAsUs = other.try_as<SwapPaneArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_Direction == _Direction;
|
||||
|
@ -477,7 +529,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
static FromJsonResult FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MovePaneArgs>();
|
||||
auto args = winrt::make_self<SwapPaneArgs>();
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->Direction() == FocusDirection::None)
|
||||
{
|
||||
|
@ -495,13 +547,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
return {};
|
||||
}
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
const auto args{ get_self<MovePaneArgs>(val) };
|
||||
const auto args{ get_self<SwapPaneArgs>(val) };
|
||||
JsonUtils::SetValueForKey(json, DirectionKey, args->_Direction);
|
||||
return json;
|
||||
}
|
||||
IActionArgs Copy() const
|
||||
{
|
||||
auto copy{ winrt::make_self<MovePaneArgs>() };
|
||||
auto copy{ winrt::make_self<SwapPaneArgs>() };
|
||||
copy->_Direction = _Direction;
|
||||
return *copy;
|
||||
}
|
||||
|
@ -1708,6 +1760,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
|||
BASIC_FACTORY(NewTabArgs);
|
||||
BASIC_FACTORY(MoveFocusArgs);
|
||||
BASIC_FACTORY(MovePaneArgs);
|
||||
BASIC_FACTORY(SwapPaneArgs);
|
||||
BASIC_FACTORY(SplitPaneArgs);
|
||||
BASIC_FACTORY(SetColorSchemeArgs);
|
||||
BASIC_FACTORY(ExecuteCommandlineArgs);
|
||||
|
|
|
@ -141,6 +141,12 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
NewTerminalArgs TerminalArgs { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass MovePaneArgs : IActionArgs
|
||||
{
|
||||
MovePaneArgs(UInt32 tabIndex);
|
||||
UInt32 TabIndex;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SwitchToTabArgs : IActionArgs
|
||||
{
|
||||
SwitchToTabArgs(UInt32 tabIndex);
|
||||
|
@ -158,9 +164,9 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
FocusDirection FocusDirection { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass MovePaneArgs : IActionArgs
|
||||
[default_interface] runtimeclass SwapPaneArgs : IActionArgs
|
||||
{
|
||||
MovePaneArgs(FocusDirection direction);
|
||||
SwapPaneArgs(FocusDirection direction);
|
||||
FocusDirection Direction { get; };
|
||||
};
|
||||
|
||||
|
|
|
@ -47,12 +47,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
return hashedAction ^ hashedArgs;
|
||||
}
|
||||
|
||||
ActionMap::ActionMap() :
|
||||
_NestedCommands{ single_threaded_map<hstring, Model::Command>() },
|
||||
_IterableCommands{ single_threaded_vector<Model::Command>() }
|
||||
{
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves the Command in the current layer, if it's valid
|
||||
// - We internally store invalid commands as full commands.
|
||||
|
@ -194,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
// Update NameMap with our parents.
|
||||
// Starting with this means we're doing a top-down approach.
|
||||
FAIL_FAST_IF(_parents.size() > 1);
|
||||
assert(_parents.size() <= 1);
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
parent->_PopulateNameMapWithSpecialCommands(nameMap);
|
||||
|
@ -272,7 +266,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
});
|
||||
|
||||
// Now, add the accumulated actions from our parents
|
||||
FAIL_FAST_IF(_parents.size() > 1);
|
||||
assert(_parents.size() <= 1);
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
const auto parentActions{ parent->_GetCumulativeActions() };
|
||||
|
@ -308,7 +302,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
// populate _KeyBindingMapCache
|
||||
std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality> keyBindingsMap;
|
||||
std::unordered_set<KeyChord, KeyChordHash, KeyChordEquality> unboundKeys;
|
||||
std::unordered_set<ActionMapKeyChord> unboundKeys;
|
||||
_PopulateKeyBindingMapWithStandardCommands(keyBindingsMap, unboundKeys);
|
||||
|
||||
_KeyBindingMapCache = single_threaded_map<KeyChord, Model::Command>(std::move(keyBindingsMap));
|
||||
|
@ -323,7 +317,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// Arguments:
|
||||
// - keyBindingsMap: the keyBindingsMap we're populating. This maps the key chord of a command to the command itself.
|
||||
// - unboundKeys: a set of keys that are explicitly unbound
|
||||
void ActionMap::_PopulateKeyBindingMapWithStandardCommands(std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality>& unboundKeys) const
|
||||
void ActionMap::_PopulateKeyBindingMapWithStandardCommands(std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<ActionMapKeyChord>& unboundKeys) const
|
||||
{
|
||||
// Update KeyBindingsMap with our current layer
|
||||
for (const auto& [keys, actionID] : _KeyMap)
|
||||
|
@ -358,7 +352,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
|
||||
// Update keyBindingsMap and unboundKeys with our parents
|
||||
FAIL_FAST_IF(_parents.size() > 1);
|
||||
assert(_parents.size() <= 1);
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
parent->_PopulateKeyBindingMapWithStandardCommands(keyBindingsMap, unboundKeys);
|
||||
|
@ -369,35 +363,38 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
auto actionMap{ make_self<ActionMap>() };
|
||||
|
||||
// copy _KeyMap (KeyChord --> ID)
|
||||
// KeyChord --> ID
|
||||
actionMap->_KeyMap = _KeyMap;
|
||||
|
||||
// copy _ActionMap (ID --> Command)
|
||||
// ID --> Command
|
||||
actionMap->_ActionMap.reserve(_ActionMap.size());
|
||||
for (const auto& [actionID, cmd] : _ActionMap)
|
||||
{
|
||||
actionMap->_ActionMap.emplace(actionID, *(get_self<Command>(cmd)->Copy()));
|
||||
actionMap->_ActionMap.emplace(actionID, *winrt::get_self<Command>(cmd)->Copy());
|
||||
}
|
||||
|
||||
// copy _MaskingActions (ID --> Command)
|
||||
// ID --> Command
|
||||
actionMap->_MaskingActions.reserve(_MaskingActions.size());
|
||||
for (const auto& [actionID, cmd] : _MaskingActions)
|
||||
{
|
||||
actionMap->_MaskingActions.emplace(actionID, *(get_self<Command>(cmd)->Copy()));
|
||||
actionMap->_MaskingActions.emplace(actionID, *winrt::get_self<Command>(cmd)->Copy());
|
||||
}
|
||||
|
||||
// copy _NestedCommands (Name --> Command)
|
||||
// Name --> Command
|
||||
actionMap->_NestedCommands.reserve(_NestedCommands.size());
|
||||
for (const auto& [name, cmd] : _NestedCommands)
|
||||
{
|
||||
actionMap->_NestedCommands.Insert(name, *(get_self<Command>(cmd)->Copy()));
|
||||
actionMap->_NestedCommands.emplace(name, *winrt::get_self<Command>(cmd)->Copy());
|
||||
}
|
||||
|
||||
// copy _IterableCommands
|
||||
actionMap->_IterableCommands.reserve(_IterableCommands.size());
|
||||
for (const auto& cmd : _IterableCommands)
|
||||
{
|
||||
actionMap->_IterableCommands.Append(*(get_self<Command>(cmd)->Copy()));
|
||||
actionMap->_IterableCommands.emplace_back(*winrt::get_self<Command>(cmd)->Copy());
|
||||
}
|
||||
|
||||
// repeat this for each of our parents
|
||||
FAIL_FAST_IF(_parents.size() > 1);
|
||||
assert(_parents.size() <= 1);
|
||||
actionMap->_parents.reserve(_parents.size());
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
actionMap->_parents.emplace_back(parent->Copy());
|
||||
|
@ -431,7 +428,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
const auto name{ cmd.Name() };
|
||||
if (!name.empty())
|
||||
{
|
||||
_NestedCommands.Insert(name, cmd);
|
||||
_NestedCommands.emplace(name, cmd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -439,7 +436,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// Handle iterable commands
|
||||
if (cmdImpl->IterateOn() != ExpandCommandType::None)
|
||||
{
|
||||
_IterableCommands.Append(cmd);
|
||||
_IterableCommands.emplace_back(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -582,7 +579,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
|
||||
// Handle a collision with NestedCommands
|
||||
_NestedCommands.TryRemove(newName);
|
||||
_NestedCommands.erase(newName);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -677,6 +674,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the given key chord is explicitly unbound
|
||||
// Arguments:
|
||||
// - keys: the key chord to check
|
||||
// Return value:
|
||||
// - true if the keychord is explicitly unbound
|
||||
// - false if either the keychord is bound, or not bound at all
|
||||
bool ActionMap::IsKeyChordExplicitlyUnbound(Control::KeyChord const& keys) const
|
||||
{
|
||||
// We use the fact that the ..Internal call returns nullptr for explicitly unbound
|
||||
// key chords, and nullopt for keychord that are not bound - it allows us to distinguish
|
||||
// between unbound and lack of binding.
|
||||
return _GetActionByKeyChordInternal(keys) == nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves the assigned command that can be invoked with the given key chord
|
||||
// Arguments:
|
||||
|
@ -686,21 +698,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// - nullptr if the key chord doesn't exist
|
||||
Model::Command ActionMap::GetActionByKeyChord(Control::KeyChord const& keys) const
|
||||
{
|
||||
const auto modifiers = keys.Modifiers();
|
||||
|
||||
// The "keys" given to us can contain both a Vkey, as well as a ScanCode.
|
||||
// For instance our UI code fills out a KeyChord with all available information.
|
||||
// But our _KeyMap only contains KeyChords that contain _either_ a Vkey or ScanCode.
|
||||
// Due to this we'll have to call _GetActionByKeyChordInternal twice.
|
||||
if (auto vkey = keys.Vkey())
|
||||
{
|
||||
if (auto command = _GetActionByKeyChordInternal({ modifiers, vkey, 0 }))
|
||||
{
|
||||
return *command;
|
||||
}
|
||||
}
|
||||
|
||||
return _GetActionByKeyChordInternal({ modifiers, 0, keys.ScanCode() }).value_or(nullptr);
|
||||
return _GetActionByKeyChordInternal(keys).value_or(nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -712,8 +710,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// - the command with the given key chord
|
||||
// - nullptr if the key chord is explicitly unbound
|
||||
// - nullopt if it was not bound in this layer
|
||||
std::optional<Model::Command> ActionMap::_GetActionByKeyChordInternal(Control::KeyChord const& keys) const
|
||||
std::optional<Model::Command> ActionMap::_GetActionByKeyChordInternal(const ActionMapKeyChord keys) const
|
||||
{
|
||||
// KeyChord's constructor ensures that Modifiers() & Vkey() is a valid value at a minimum.
|
||||
// This allows ActionMap 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.
|
||||
// As such we need to pretend as if ScanCode doesn't exist.
|
||||
assert(keys.vkey != 0);
|
||||
|
||||
// Check the current layer
|
||||
if (const auto actionIDPair = _KeyMap.find(keys); actionIDPair != _KeyMap.end())
|
||||
{
|
||||
|
@ -724,7 +729,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
// the command was not bound in this layer,
|
||||
// ask my parents
|
||||
FAIL_FAST_IF(_parents.size() > 1);
|
||||
assert(_parents.size() <= 1);
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
const auto& inheritedCmd{ parent->_GetActionByKeyChordInternal(keys) };
|
||||
|
@ -774,7 +779,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
|
||||
// Check our parents
|
||||
FAIL_FAST_IF(_parents.size() > 1);
|
||||
assert(_parents.size() <= 1);
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
if (const auto& keys{ parent->GetKeyBindingForAction(myAction, myArgs) })
|
||||
|
|
|
@ -28,6 +28,60 @@ namespace SettingsModelLocalTests
|
|||
class TerminalSettingsTests;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
union ActionMapKeyChord
|
||||
{
|
||||
uint16_t value = 0;
|
||||
struct
|
||||
{
|
||||
uint8_t modifiers;
|
||||
uint8_t vkey;
|
||||
};
|
||||
|
||||
constexpr ActionMapKeyChord() = default;
|
||||
ActionMapKeyChord(const Control::KeyChord& keys) :
|
||||
modifiers(gsl::narrow_cast<uint8_t>(keys.Modifiers())),
|
||||
vkey(gsl::narrow_cast<uint8_t>(keys.Vkey()))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool operator==(ActionMapKeyChord other) const noexcept
|
||||
{
|
||||
return value == other.value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<>
|
||||
struct std::hash<winrt::Microsoft::Terminal::Settings::Model::implementation::ActionMapKeyChord>
|
||||
{
|
||||
constexpr size_t operator()(winrt::Microsoft::Terminal::Settings::Model::implementation::ActionMapKeyChord keys) const noexcept
|
||||
{
|
||||
// I didn't like how std::hash uses the byte-wise FNV1a for integers.
|
||||
// So I built my own std::hash with murmurhash3.
|
||||
#if SIZE_MAX == UINT32_MAX
|
||||
size_t h = keys.value;
|
||||
h ^= h >> 16;
|
||||
h *= UINT32_C(0x85ebca6b);
|
||||
h ^= h >> 13;
|
||||
h *= UINT32_C(0xc2b2ae35);
|
||||
h ^= h >> 16;
|
||||
return h;
|
||||
#elif SIZE_MAX == UINT64_MAX
|
||||
size_t h = keys.value;
|
||||
h ^= h >> 33;
|
||||
h *= UINT64_C(0xff51afd7ed558ccd);
|
||||
h ^= h >> 33;
|
||||
h *= UINT64_C(0xc4ceb9fe1a85ec53);
|
||||
h ^= h >> 33;
|
||||
return h;
|
||||
#else
|
||||
return std::hash<uint16_t>{}(keys.value);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
using InternalActionID = size_t;
|
||||
|
@ -36,7 +90,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
std::size_t operator()(const Control::KeyChord& key) const
|
||||
{
|
||||
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(key.Modifiers(), key.Vkey(), key.ScanCode());
|
||||
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(key.Modifiers(), key.Vkey());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -44,14 +98,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
bool operator()(const Control::KeyChord& lhs, const Control::KeyChord& rhs) const
|
||||
{
|
||||
return lhs.Modifiers() == rhs.Modifiers() && lhs.Vkey() == rhs.Vkey() && lhs.ScanCode() == rhs.ScanCode();
|
||||
return lhs.Modifiers() == rhs.Modifiers() && lhs.Vkey() == rhs.Vkey();
|
||||
}
|
||||
};
|
||||
|
||||
struct ActionMap : ActionMapT<ActionMap>, IInheritable<ActionMap>
|
||||
{
|
||||
ActionMap();
|
||||
|
||||
// views
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::ActionAndArgs> AvailableActions();
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::Command> NameMap();
|
||||
|
@ -61,6 +113,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
// queries
|
||||
Model::Command GetActionByKeyChord(Control::KeyChord const& keys) const;
|
||||
bool IsKeyChordExplicitlyUnbound(Control::KeyChord const& keys) const;
|
||||
Control::KeyChord GetKeyBindingForAction(ShortcutAction const& action) const;
|
||||
Control::KeyChord GetKeyBindingForAction(ShortcutAction const& action, IActionArgs const& actionArgs) const;
|
||||
|
||||
|
@ -79,12 +132,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
private:
|
||||
std::optional<Model::Command> _GetActionByID(const InternalActionID actionID) const;
|
||||
std::optional<Model::Command> _GetActionByKeyChordInternal(const Control::KeyChord& keys) const;
|
||||
std::optional<Model::Command> _GetActionByKeyChordInternal(const ActionMapKeyChord keys) const;
|
||||
|
||||
void _PopulateAvailableActionsWithStandardCommands(std::unordered_map<hstring, Model::ActionAndArgs>& availableActions, std::unordered_set<InternalActionID>& visitedActionIDs) const;
|
||||
void _PopulateNameMapWithSpecialCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
|
||||
void _PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
|
||||
void _PopulateKeyBindingMapWithStandardCommands(std::unordered_map<Control::KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality>& unboundKeys) const;
|
||||
void _PopulateKeyBindingMapWithStandardCommands(std::unordered_map<Control::KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<ActionMapKeyChord>& unboundKeys) const;
|
||||
std::vector<Model::Command> _GetCumulativeActions() const noexcept;
|
||||
|
||||
void _TryUpdateActionMap(const Model::Command& cmd, Model::Command& oldCmd, Model::Command& consolidatedCmd);
|
||||
|
@ -95,9 +148,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _GlobalHotkeysCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _KeyBindingMapCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NestedCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<Model::Command> _IterableCommands{ nullptr };
|
||||
std::unordered_map<Control::KeyChord, InternalActionID, KeyChordHash, KeyChordEquality> _KeyMap;
|
||||
|
||||
std::unordered_map<winrt::hstring, Model::Command> _NestedCommands;
|
||||
std::vector<Model::Command> _IterableCommands;
|
||||
std::unordered_map<ActionMapKeyChord, InternalActionID> _KeyMap;
|
||||
std::unordered_map<InternalActionID, Model::Command> _ActionMap;
|
||||
|
||||
// Masking Actions:
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
// This interface ensures that no changes are made to ActionMap
|
||||
interface IActionMapView
|
||||
{
|
||||
Boolean IsKeyChordExplicitlyUnbound(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
ON_ALL_ACTIONS(ResizePane) \
|
||||
ON_ALL_ACTIONS(MoveFocus) \
|
||||
ON_ALL_ACTIONS(MovePane) \
|
||||
ON_ALL_ACTIONS(SwapPane) \
|
||||
ON_ALL_ACTIONS(Find) \
|
||||
ON_ALL_ACTIONS(ToggleShaderEffects) \
|
||||
ON_ALL_ACTIONS(ToggleFocusMode) \
|
||||
|
@ -91,6 +92,7 @@
|
|||
ON_ALL_ACTIONS_WITH_ARGS(GlobalSummon) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(MoveFocus) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(MovePane) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(SwapPane) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(MoveTab) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(NewTab) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(NewWindow) \
|
||||
|
|
|
@ -25,6 +25,7 @@ static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImag
|
|||
static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" };
|
||||
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
|
||||
static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" };
|
||||
static constexpr std::string_view IntenseTextStyleKey{ "intenseTextStyle" };
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::implementation::AppearanceConfig::AppearanceConfig(const winrt::weak_ref<Profile> sourceProfile) :
|
||||
_sourceProfile(sourceProfile)
|
||||
|
@ -48,6 +49,7 @@ winrt::com_ptr<AppearanceConfig> AppearanceConfig::CopyAppearance(const winrt::c
|
|||
appearance->_BackgroundImageAlignment = sourceAppearance->_BackgroundImageAlignment;
|
||||
appearance->_RetroTerminalEffect = sourceAppearance->_RetroTerminalEffect;
|
||||
appearance->_PixelShaderPath = sourceAppearance->_PixelShaderPath;
|
||||
appearance->_IntenseTextStyle = sourceAppearance->_IntenseTextStyle;
|
||||
return appearance;
|
||||
}
|
||||
|
||||
|
@ -68,6 +70,7 @@ Json::Value AppearanceConfig::ToJson() const
|
|||
JsonUtils::SetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment);
|
||||
JsonUtils::SetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect);
|
||||
JsonUtils::SetValueForKey(json, PixelShaderPathKey, _PixelShaderPath);
|
||||
JsonUtils::SetValueForKey(json, IntenseTextStyleKey, _IntenseTextStyle);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
@ -98,6 +101,7 @@ void AppearanceConfig::LayerJson(const Json::Value& json)
|
|||
JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment);
|
||||
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect);
|
||||
JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath);
|
||||
JsonUtils::GetValueForKey(json, IntenseTextStyleKey, _IntenseTextStyle);
|
||||
}
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile AppearanceConfig::SourceProfile()
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, bool, RetroTerminalEffect, false);
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, PixelShaderPath, L"");
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, Model::IntenseStyle, IntenseTextStyle, Model::IntenseStyle::All);
|
||||
|
||||
private:
|
||||
winrt::weak_ref<Profile> _sourceProfile;
|
||||
|
|
|
@ -348,6 +348,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::Duplicate
|
|||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, SelectionBackground);
|
||||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, CursorColor);
|
||||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, PixelShaderPath);
|
||||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, IntenseTextStyle);
|
||||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, BackgroundImagePath);
|
||||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, BackgroundImageOpacity);
|
||||
DUPLICATE_SETTING_MACRO_SUB(appearance, target, BackgroundImageStretchMode);
|
||||
|
|
|
@ -155,15 +155,14 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
|
|||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
std::optional<std::string> fileData = _ReadUserSettings();
|
||||
const bool foundFile = fileData.has_value();
|
||||
|
||||
// Make sure the file isn't totally empty. If it is, we'll treat the file
|
||||
// like it doesn't exist at all.
|
||||
const bool fileHasData = foundFile && !fileData.value().empty();
|
||||
const bool fileHasData = fileData && !fileData->empty();
|
||||
bool needToWriteFile = false;
|
||||
if (fileHasData)
|
||||
{
|
||||
resultPtr->_ParseJsonString(fileData.value(), false);
|
||||
resultPtr->_ParseJsonString(*fileData, false);
|
||||
}
|
||||
|
||||
// Load profiles from dynamic profile generators. _userSettings should be
|
||||
|
@ -204,6 +203,35 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
|
|||
_CatchRethrowSerializationExceptionWithLocationInfo(resultPtr->_userSettingsString);
|
||||
}
|
||||
|
||||
// Let's say a user doesn't know that they need to write `"hidden": true` in
|
||||
// order to prevent a profile from showing up (and a settings UI doesn't exist).
|
||||
// Naturally they would open settings.json and try to remove the profile object.
|
||||
// This section of code recognizes if a profile was seen before and marks it as
|
||||
// `"hidden": true` by default and thus ensures the behavior the user expects:
|
||||
// Profiles won't show up again after they've been removed from settings.json.
|
||||
{
|
||||
const auto state = winrt::get_self<implementation::ApplicationState>(ApplicationState::SharedInstance());
|
||||
auto generatedProfiles = state->GeneratedProfiles();
|
||||
bool generatedProfilesChanged = false;
|
||||
|
||||
for (auto profile : resultPtr->_allProfiles)
|
||||
{
|
||||
if (generatedProfiles.emplace(profile.Guid()).second)
|
||||
{
|
||||
generatedProfilesChanged = true;
|
||||
}
|
||||
else if (profile.Origin() != OriginTag::User)
|
||||
{
|
||||
profile.Hidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (generatedProfilesChanged)
|
||||
{
|
||||
state->GeneratedProfiles(generatedProfiles);
|
||||
}
|
||||
}
|
||||
|
||||
// After layering the user settings, check if there are any new profiles
|
||||
// that need to be inserted into their user settings file.
|
||||
needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
|
||||
|
@ -352,7 +380,6 @@ void CascadiaSettings::_LoadDynamicProfiles()
|
|||
}
|
||||
}
|
||||
|
||||
const GUID nullGuid{ 0 };
|
||||
for (auto& generator : _profileGenerators)
|
||||
{
|
||||
const std::wstring generatorNamespace{ generator->GetNamespace() };
|
||||
|
@ -711,7 +738,7 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
|||
wbuilder.settings_["indentation"] = " ";
|
||||
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
|
||||
|
||||
auto isInJsonObj = [](const auto& profile, const auto& json) {
|
||||
static const auto isInJsonObj = [](const auto& profile, const auto& json) {
|
||||
for (auto profileJson : _GetProfilesJsonObject(json))
|
||||
{
|
||||
if (profileJson.isObject())
|
||||
|
@ -745,8 +772,16 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
|||
|
||||
for (const auto& profile : _allProfiles)
|
||||
{
|
||||
// Skip profiles that are in the user settings or the default settings.
|
||||
if (isInJsonObj(profile, _userSettings) || isInJsonObj(profile, _defaultSettings))
|
||||
// Skip profiles that are:
|
||||
// * hidden
|
||||
// Because when a user manually removes profiles from settings.json,
|
||||
// we mark them as hidden in LoadAll(). Adding those profiles right
|
||||
// back into settings.json would feel confusing, while the
|
||||
// profile that was just erased is added right back.
|
||||
// * in the user settings or the default settings
|
||||
// Because we don't want to add profiles which are already
|
||||
// in the settings.json (explicitly or implicitly).
|
||||
if (profile.Hidden() || isInJsonObj(profile, _userSettings) || isInJsonObj(profile, _defaultSettings))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
DEFINE_ENUM_MAP(Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::TextAntialiasingMode, TextAntialiasingMode);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Core::CursorStyle, CursorStyle);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::IntenseStyle, IntenseTextStyle);
|
||||
|
||||
// FontWeight is special because the JsonUtils::ConversionTrait for it
|
||||
// creates a FontWeight object, but we need to use the uint16_t value.
|
||||
|
|
|
@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::TextAntialiasingMode> TextAntialiasingMode();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Core::CursorStyle> CursorStyle();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint16_t> FontWeight();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle> IntenseTextStyle();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -22,5 +22,6 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.TextAntialiasingMode> TextAntialiasingMode { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Core.CursorStyle> CursorStyle { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, UInt16> FontWeight { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.IntenseStyle> IntenseTextStyle { get; };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,27 @@ namespace Microsoft::Terminal::Settings::Model
|
|||
|
||||
void WriteUTF8FileAtomic(const std::filesystem::path& path, const std::string_view content)
|
||||
{
|
||||
auto tmpPath = path;
|
||||
// GH#10787: rename() will replace symbolic links themselves and not the path they point at.
|
||||
// It's thus important that we first resolve them before generating temporary path.
|
||||
std::error_code ec;
|
||||
const auto resolvedPath = std::filesystem::is_symlink(path) ? std::filesystem::canonical(path, ec) : path;
|
||||
if (ec)
|
||||
{
|
||||
if (ec.value() != ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
THROW_WIN32_MSG(ec.value(), "failed to compute canonical path");
|
||||
}
|
||||
|
||||
// The original file is a symbolic link, but the target doesn't exist.
|
||||
// Consider two fall-backs:
|
||||
// * resolve the link manually, which might be less accurate and more prone to race conditions
|
||||
// * write to the file directly, which lets the system resolve the symbolic link but leaves the write non-atomic
|
||||
// The latter is chosen, as this is an edge case and our 'atomic' writes are only best-effort.
|
||||
WriteUTF8File(path, content);
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmpPath = resolvedPath;
|
||||
tmpPath += L".tmp";
|
||||
|
||||
// Writing to a file isn't atomic, but...
|
||||
|
@ -132,6 +152,6 @@ namespace Microsoft::Terminal::Settings::Model
|
|||
// renaming one is (supposed to be) atomic.
|
||||
// Wait... "supposed to be"!? Well it's technically not always atomic,
|
||||
// but it's pretty darn close to it, so... better than nothing.
|
||||
std::filesystem::rename(tmpPath, path);
|
||||
std::filesystem::rename(tmpPath, resolvedPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ static constexpr std::string_view StartupActionsKey{ "startupActions" };
|
|||
static constexpr std::string_view FocusFollowMouseKey{ "focusFollowMouse" };
|
||||
static constexpr std::string_view WindowingBehaviorKey{ "windowingBehavior" };
|
||||
static constexpr std::string_view TrimBlockSelectionKey{ "trimBlockSelection" };
|
||||
static constexpr std::string_view AlwaysShowTrayIconKey{ "alwaysShowTrayIcon" };
|
||||
static constexpr std::string_view MinimizeToTrayKey{ "minimizeToTray" };
|
||||
|
||||
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
|
||||
|
||||
|
@ -129,6 +131,8 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
|
|||
globals->_WindowingBehavior = _WindowingBehavior;
|
||||
globals->_TrimBlockSelection = _TrimBlockSelection;
|
||||
globals->_DetectURLs = _DetectURLs;
|
||||
globals->_MinimizeToTray = _MinimizeToTray;
|
||||
globals->_AlwaysShowTrayIcon = _AlwaysShowTrayIcon;
|
||||
|
||||
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
|
||||
globals->_validDefaultProfile = _validDefaultProfile;
|
||||
|
@ -319,6 +323,10 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
|||
|
||||
JsonUtils::GetValueForKey(json, DetectURLsKey, _DetectURLs);
|
||||
|
||||
JsonUtils::GetValueForKey(json, MinimizeToTrayKey, _MinimizeToTray);
|
||||
|
||||
JsonUtils::GetValueForKey(json, AlwaysShowTrayIconKey, _AlwaysShowTrayIcon);
|
||||
|
||||
// This is a helper lambda to get the keybindings and commands out of both
|
||||
// and array of objects. We'll use this twice, once on the legacy
|
||||
// `keybindings` key, and again on the newer `bindings` key.
|
||||
|
@ -414,6 +422,8 @@ Json::Value GlobalAppSettings::ToJson() const
|
|||
JsonUtils::SetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior);
|
||||
JsonUtils::SetValueForKey(json, TrimBlockSelectionKey, _TrimBlockSelection);
|
||||
JsonUtils::SetValueForKey(json, DetectURLsKey, _DetectURLs);
|
||||
JsonUtils::SetValueForKey(json, MinimizeToTrayKey, _MinimizeToTray);
|
||||
JsonUtils::SetValueForKey(json, AlwaysShowTrayIconKey, _AlwaysShowTrayIcon);
|
||||
// clang-format on
|
||||
|
||||
json[JsonKey(ActionsKey)] = _actionMap->ToJson();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue