Merge remote-tracking branch 'origin/main' into feature/gh10733-select-subtree-panes

Conflicts:
	src/cascadia/TerminalApp/Pane.cpp
	src/cascadia/TerminalApp/TerminalTab.cpp
This commit is contained in:
Schuyler Rosefield 2021-09-15 16:31:20 -04:00
commit 259281e06b
108 changed files with 2396 additions and 795 deletions

View file

@ -536,6 +536,7 @@ DECRC
DECREQTPARM
DECRLM
DECRQM
DECRQSS
DECRST
DECSASD
DECSC

View file

@ -279,6 +279,7 @@
"paste",
"prevTab",
"renameTab",
"openSystemMenu",
"openTabRenamer",
"quakeMode",
"resetFontSize",
@ -304,6 +305,7 @@
"toggleReadOnlyMode",
"toggleShaderEffects",
"wt",
"quit",
"unbound"
],
"type": "string"
@ -346,11 +348,13 @@
],
"type": "string"
},
"SplitState": {
"SplitDirection": {
"enum": [
"vertical",
"horizontal",
"auto"
"auto",
"up",
"right",
"down",
"left"
],
"type": "string"
},
@ -629,9 +633,9 @@
"properties": {
"action": { "type": "string", "pattern": "splitPane" },
"split": {
"$ref": "#/definitions/SplitState",
"$ref": "#/definitions/SplitDirection",
"default": "auto",
"description": "The orientation to split the pane in. Possible values:\n -\"auto\" (splits pane based on remaining space)\n -\"horizontal\" (think [-])\n -\"vertical\" (think [|])"
"description": "The orientation to split the pane in. Possible values:\n -\"auto\" (splits pane based on remaining space)\n -\"up\" (think [-] and above)\n -\"down\" (think [-] and below)\n -\"left\" (think [|] and to the left)\n -\"right\"(think [|] and to the right)"
},
"splitMode": {
"default": "duplicate",
@ -1233,6 +1237,15 @@
"description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
"type": "boolean"
},
"firstWindowPreference": {
"default": "defaultProfile",
"description": "Defines what behavior the terminal takes when it starts. \"defaultProfile\" will have the terminal launch with one tab of the default profile, and \"persistedWindowLayout\" will cause the terminal to save its layout on close and reload it on open.",
"enum": [
"defaultProfile",
"persistedWindowLayout"
],
"type": "string"
},
"launchMode": {
"default": "default",
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window. Setting this to \"focus\" is equivalent to launching the terminal in the \"default\" mode, but with the focus mode enabled. Similar, setting this to \"maximizedFocus\" will result in launching the terminal in a maximized window with the focus mode enabled.",
@ -1253,14 +1266,14 @@
"type": [ "integer", "string" ],
"deprecated": true
},
"minimizeToTray": {
"minimizeToNotificationArea": {
"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.",
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the notification area through which the user can access their windows.",
"type": "boolean"
},
"alwaysShowTrayIcon": {
"alwaysShowNotificationIcon": {
"default": "false",
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
"description": "When set to true, the Terminal's notification icon will always be shown in the notification area.",
"type": "boolean"
},
"useAcrylicInTabRow": {

View file

@ -2,5 +2,5 @@
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

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

View file

@ -152,6 +152,10 @@ namespace SettingsModelLocalTests
{ "name": "command4", "command": { "action": "splitPane" } },
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
{ "name": "command6", "command": { "action": "splitPane", "size": 0.25 } },
{ "name": "command7", "command": { "action": "splitPane", "split": "right" } },
{ "name": "command8", "command": { "action": "splitPane", "split": "left" } },
{ "name": "command9", "command": { "action": "splitPane", "split": "up" } },
{ "name": "command10", "command": { "action": "splitPane", "split": "down" } },
])" };
const auto commands0Json = VerifyParseSucceeded(commands0String);
@ -160,7 +164,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(0u, commands.Size());
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
VERIFY_ARE_EQUAL(0u, warnings.size());
VERIFY_ARE_EQUAL(5u, commands.Size());
VERIFY_ARE_EQUAL(9u, commands.Size());
{
auto command = commands.Lookup(L"command1");
@ -170,7 +174,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -181,7 +185,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -192,7 +196,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -203,7 +207,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
@ -214,9 +218,53 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command7");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command8");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command9");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
{
auto command = commands.Lookup(L"command10");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize());
}
}
void CommandTests::TestSplitPaneBadSize()
@ -244,7 +292,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize());
}
}
@ -286,8 +334,10 @@ namespace SettingsModelLocalTests
const std::string commands0String{ R"([
{ "command": { "action": "splitPane", "split": null } },
{ "command": { "action": "splitPane", "split": "vertical" } },
{ "command": { "action": "splitPane", "split": "horizontal" } },
{ "command": { "action": "splitPane", "split": "left" } },
{ "command": { "action": "splitPane", "split": "right" } },
{ "command": { "action": "splitPane", "split": "up" } },
{ "command": { "action": "splitPane", "split": "down" } },
{ "command": { "action": "splitPane", "split": "none" } },
{ "command": { "action": "splitPane" } },
{ "command": { "action": "splitPane", "split": "auto" } },
@ -301,10 +351,10 @@ namespace SettingsModelLocalTests
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
VERIFY_ARE_EQUAL(0u, warnings.size());
// There are only 3 commands here: all of the `"none"`, `"auto"`,
// There are only 5 commands here: all of the `"none"`, `"auto"`,
// `"foo"`, `null`, and <no args> bindings all generate the same action,
// which will generate just a single name for all of them.
VERIFY_ARE_EQUAL(3u, commands.Size());
VERIFY_ARE_EQUAL(5u, commands.Size());
{
auto command = commands.Lookup(L"Split pane");
@ -314,27 +364,47 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: vertical");
auto command = commands.Lookup(L"Split pane, split: left");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Left, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: horizontal");
auto command = commands.Lookup(L"Split pane, split: right");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: up");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Up, realArgs.SplitDirection());
}
{
auto command = commands.Lookup(L"Split pane, split: down");
VERIFY_IS_NOT_NULL(command);
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.ActionAndArgs().Action());
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
}
}
void CommandTests::TestLayerOnAutogeneratedName()
@ -360,7 +430,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = command.ActionAndArgs().Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
}
}

View file

@ -2130,7 +2130,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2147,7 +2147,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2161,7 +2161,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2175,7 +2175,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2189,7 +2189,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -2218,7 +2218,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());

View file

@ -432,7 +432,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('E'), 0 };
@ -440,7 +440,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('G'), 0 };
@ -448,7 +448,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('H'), 0 };
@ -456,7 +456,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().as<SplitPaneArgs>();
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
}
}

View file

@ -256,10 +256,14 @@ namespace SettingsModelLocalTests
])" };
// complex command with key chords
const std::string actionsString4{ R"([
const std::string actionsString4A{ R"([
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
])" };
const std::string actionsString4B{ R"([
{ "command": { "action": "findMatch", "direction": "next" }, "keys": "ctrl+shift+s" },
{ "command": { "action": "findMatch", "direction": "prev" }, "keys": "ctrl+shift+r" }
])" };
// command with name and icon and multiple key chords
const std::string actionsString5{ R"([
@ -372,7 +376,8 @@ namespace SettingsModelLocalTests
RoundtripTest<implementation::ActionMap>(actionsString3);
Log::Comment(L"complex commands with key chords");
RoundtripTest<implementation::ActionMap>(actionsString4);
RoundtripTest<implementation::ActionMap>(actionsString4A);
RoundtripTest<implementation::ActionMap>(actionsString4B);
Log::Comment(L"command with name and icon and multiple key chords");
RoundtripTest<implementation::ActionMap>(actionsString5);

View file

@ -124,7 +124,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -145,7 +145,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -167,7 +167,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -189,7 +189,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -211,7 +211,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -242,7 +242,7 @@ namespace SettingsModelLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_FALSE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());

View file

@ -715,7 +715,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -735,7 +735,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Horizontal, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -757,7 +757,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Vertical, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(SplitType::Manual, myArgs.SplitMode());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -799,7 +799,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
@ -828,7 +828,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Horizontal, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, myArgs.SplitDirection());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
@ -857,7 +857,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty());
@ -1779,7 +1779,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1799,7 +1799,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1820,7 +1820,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1830,7 +1830,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.5f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1852,7 +1852,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.3f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}
@ -1862,7 +1862,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
auto myArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(myArgs);
VERIFY_ARE_EQUAL(SplitState::Automatic, myArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, myArgs.SplitDirection());
VERIFY_ARE_EQUAL(0.7f, myArgs.SplitSize());
VERIFY_IS_NOT_NULL(myArgs.TerminalArgs());
}

View file

@ -132,7 +132,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -156,7 +156,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -174,7 +174,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -192,7 +192,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -259,7 +259,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -283,7 +283,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -301,7 +301,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -319,7 +319,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -388,7 +388,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -412,7 +412,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -430,7 +430,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -448,7 +448,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -680,16 +680,16 @@ namespace TerminalAppLocalTests
// <Command Palette>
// ├─ profile0...
// | ├─ Split pane, profile: profile0
// | ├─ Split pane, direction: vertical, profile: profile0
// | └─ Split pane, direction: horizontal, profile: profile0
// | ├─ Split pane, direction: right, profile: profile0
// | └─ Split pane, direction: down, profile: profile0
// ├─ profile1...
// | ├─Split pane, profile: profile1
// | ├─Split pane, direction: vertical, profile: profile1
// | └─Split pane, direction: horizontal, profile: profile1
// | ├─Split pane, direction: right, profile: profile1
// | └─Split pane, direction: down, profile: profile1
// └─ profile2...
// ├─ Split pane, profile: profile2
// ├─ Split pane, direction: vertical, profile: profile2
// └─ Split pane, direction: horizontal, profile: profile2
// ├─ Split pane, direction: right, profile: profile2
// └─ Split pane, direction: down, profile: profile2
const std::string settingsJson{ R"(
{
@ -719,8 +719,8 @@ namespace TerminalAppLocalTests
"name": "${profile.name}...",
"commands": [
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "auto" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" } }
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "right" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
]
}
],
@ -762,7 +762,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -773,7 +773,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: down, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -783,7 +783,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -794,7 +794,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: right, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -804,7 +804,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -915,16 +915,16 @@ namespace TerminalAppLocalTests
// └─ New Pane...
// ├─ profile0...
// | ├─ Split automatically
// | ├─ Split vertically
// | └─ Split horizontally
// | ├─ Split right
// | └─ Split down
// ├─ profile1...
// | ├─ Split automatically
// | ├─ Split vertically
// | └─ Split horizontally
// | ├─ Split right
// | └─ Split down
// └─ profile2...
// ├─ Split automatically
// ├─ Split vertically
// └─ Split horizontally
// ├─ Split right
// └─ Split down
const std::string settingsJson{ R"(
{
@ -957,8 +957,8 @@ namespace TerminalAppLocalTests
"name": "${profile.name}...",
"commands": [
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "auto" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" } }
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "right" } },
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
]
}
]
@ -1010,7 +1010,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1021,7 +1021,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: horizontal, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: down, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -1031,7 +1031,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Down, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1042,7 +1042,7 @@ namespace TerminalAppLocalTests
VERIFY_IS_FALSE(childCommand.HasNestedCommands());
}
{
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: vertical, profile: {}", name) };
winrt::hstring childCommandName{ fmt::format(L"Split pane, split: right, profile: {}", name) };
auto childCommand = command.NestedCommands().Lookup(childCommandName);
VERIFY_IS_NOT_NULL(childCommand);
auto childActionAndArgs = childCommand.ActionAndArgs();
@ -1052,7 +1052,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = childActionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Right, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1127,7 +1127,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1156,7 +1156,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1174,7 +1174,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
@ -1192,7 +1192,7 @@ namespace TerminalAppLocalTests
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
VERIFY_ARE_EQUAL(SplitDirection::Automatic, realArgs.SplitDirection());
VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());

View file

@ -508,7 +508,7 @@ namespace TerminalAppLocalTests
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@ -526,7 +526,7 @@ namespace TerminalAppLocalTests
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
result = RunOnUIThread([&page]() {
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
@ -844,7 +844,7 @@ namespace TerminalAppLocalTests
// | 1 | 2 |
// | | |
// -------------------
page->_SplitPane(SplitState::Vertical, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Right, SplitType::Duplicate, 0.5f, nullptr);
secondId = tab->_activePane->Id().value();
});
Sleep(250);
@ -862,7 +862,7 @@ namespace TerminalAppLocalTests
// | 3 | |
// | | |
// -------------------
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr);
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
// Split again to make the 3rd tab
thirdId = tab->_activePane->Id().value();
@ -882,7 +882,7 @@ namespace TerminalAppLocalTests
// | 3 | 4 |
// | | |
// -------------------
page->_SplitPane(SplitState::Horizontal, SplitType::Duplicate, 0.5f, nullptr);
page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr);
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
fourthId = tab->_activePane->Id().value();
});

View file

@ -80,8 +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); });
peasant.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
peasant.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
_peasants[newPeasantsId] = peasant;
@ -91,6 +92,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
_WindowCreatedHandlers(nullptr, nullptr);
return newPeasantsId;
}
catch (...)
@ -107,6 +110,75 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
}
// Method Description:
// - Gives the host process an opportunity to run any pre-close logic then
// requests all peasants to close.
// Arguments:
// - <none> used
// Return Value:
// - <none>
void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
// Let the process hosting the monarch run any needed logic before
// closing all windows.
_QuitAllRequestedHandlers(*this, nullptr);
// Tell all peasants to exit.
const auto callback = [&](const auto& /*id*/, const auto& p) {
p.Quit();
};
const auto onError = [&](const auto& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_handleQuitAll_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not close"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forEachPeasant(callback, onError);
}
// Method Description:
// - Tells the monarch that a peasant is being closed.
// Arguments:
// - peasantId: the id of the peasant
// Return Value:
// - <none>
void Monarch::SignalClose(const uint64_t peasantId)
{
_clearOldMruEntries(peasantId);
_peasants.erase(peasantId);
_WindowClosedHandlers(nullptr, nullptr);
}
// Method Description:
// - Counts the number of living peasants.
// Arguments:
// - <none>
// Return Value:
// - the number of active peasants.
uint64_t Monarch::GetNumberOfPeasants()
{
auto num = 0;
auto callback = [&](const auto& /*id*/, const auto& p) {
// Check that the peasant is alive, and if so increment the count
p.GetID();
num += 1;
};
auto onError = [](const auto& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_GetNumberOfPeasants_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not enumerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forEachPeasant(callback, onError);
return num;
}
// Method Description:
// - Event handler for the Peasant::WindowActivated event. Used as an
// opportunity for us to update our internal stack of the "most recent

View file

@ -47,6 +47,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
uint64_t GetPID();
uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant);
void SignalClose(const uint64_t peasantId);
uint64_t GetNumberOfPeasants();
winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
@ -57,8 +60,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
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);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
uint64_t _ourPID;
@ -90,6 +96,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
// Method Description:
// - Helper for doing something on each and every peasant.
// - We'll try calling func on every peasant.

View file

@ -43,16 +43,21 @@ namespace Microsoft.Terminal.Remoting
UInt64 GetPID();
UInt64 AddPeasant(IPeasant peasant);
UInt64 GetNumberOfPeasants();
ProposeCommandlineResult ProposeCommandline(CommandlineArgs args);
void HandleActivatePeasant(WindowActivatedArgs args);
void SummonWindow(SummonWindowSelectionArgs args);
void SignalClose(UInt64 peasantId);
void SummonAllWindows();
Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
};
}

View file

@ -226,34 +226,66 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestShowTrayIcon()
void Peasant::RequestShowNotificationIcon()
{
try
{
_ShowTrayIconRequestedHandlers(*this, nullptr);
_ShowNotificationIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestShowTrayIcon",
"Peasant_RequestShowNotificationIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestHideTrayIcon()
void Peasant::RequestHideNotificationIcon()
{
try
{
_HideTrayIconRequestedHandlers(*this, nullptr);
_HideNotificationIconRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestHideTrayIcon",
"Peasant_RequestHideNotificationIcon",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestQuitAll()
{
try
{
_QuitAllRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestQuit",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::Quit()
{
try
{
_QuitRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_Quit",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}

View file

@ -28,8 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestIdentifyWindows();
void DisplayWindowId();
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
@ -43,8 +45,10 @@ 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);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
Peasant(const uint64_t testPID);

View file

@ -65,8 +65,10 @@ 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();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
@ -74,8 +76,10 @@ 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;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View file

@ -54,6 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// monarch!
CoRevokeClassObject(_registrationHostClass);
_registrationHostClass = 0;
SignalClose();
_monarchWaitInterrupt.SetEvent();
// A thread is joinable once it's been started. Basically this just
@ -64,6 +65,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
}
void WindowManager::SignalClose()
{
if (_monarch)
{
try
{
_monarch.SignalClose(_peasant.GetID());
}
CATCH_LOG()
}
}
void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args)
{
// If we're the king, we _definitely_ want to process the arguments, we were
@ -250,12 +263,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// Here, we're the king!
//
// This is where you should do any additional setup that might need to be
// done when we become the king. THis will be called both for the first
// done when we become the king. This will be called both for the first
// window, and when the current monarch dies.
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
_monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
_monarch.QuitAllRequested([this](auto&&, auto&&) { _QuitAllRequestedHandlers(*this, nullptr); });
_BecameMonarchHandlers(*this, nullptr);
}
@ -513,7 +529,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void WindowManager::SummonAllWindows()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
_monarch.SummonAllWindows();
}
@ -526,29 +542,55 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return _monarch.GetPeasantInfos();
}
// Method Description:
// - Ask the monarch to show a tray icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestShowTrayIcon()
uint64_t WindowManager::GetNumberOfPeasants()
{
co_await winrt::resume_background();
_peasant.RequestShowTrayIcon();
if (_monarch)
{
try
{
return _monarch.GetNumberOfPeasants();
}
CATCH_LOG()
}
return 0;
}
// Method Description:
// - Ask the monarch to hide its tray icon.
// - Ask the monarch to show a notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestHideTrayIcon()
winrt::fire_and_forget WindowManager::RequestShowNotificationIcon()
{
co_await winrt::resume_background();
_peasant.RequestShowNotificationIcon();
}
// Method Description:
// - Ask the monarch to hide its notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestHideNotificationIcon()
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestHideTrayIcon();
_peasant.RequestHideNotificationIcon();
}
// Method Description:
// - Ask the monarch to quit all windows.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestQuitAll()
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestQuitAll();
}
bool WindowManager::DoesQuakeWindowExist()

View file

@ -39,19 +39,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
bool IsMonarch();
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
void SignalClose();
void SummonAllWindows();
uint64_t GetNumberOfPeasants();
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
winrt::fire_and_forget RequestShowTrayIcon();
winrt::fire_and_forget RequestHideTrayIcon();
winrt::fire_and_forget RequestShowNotificationIcon();
winrt::fire_and_forget RequestHideNotificationIcon();
winrt::fire_and_forget RequestQuitAll();
bool DoesQuakeWindowExist();
void UpdateActiveTabTitle(winrt::hstring title);
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);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
bool _shouldCreateWindow{ false };

View file

@ -8,19 +8,25 @@ namespace Microsoft.Terminal.Remoting
{
WindowManager();
void ProposeCommandline(CommandlineArgs args);
void SignalClose();
Boolean ShouldCreateWindow { get; };
IPeasant CurrentWindow();
Boolean IsMonarch { get; };
void SummonWindow(SummonWindowSelectionArgs args);
void SummonAllWindows();
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
UInt64 GetNumberOfPeasants();
void RequestQuitAll();
void UpdateActiveTabTitle(String title);
Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
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;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
};
}

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -78,7 +78,14 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
CloseWindow();
CloseWindow(false);
args.Handled(true);
}
void TerminalPage::_HandleQuit(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
RequestQuit();
args.Handled(true);
}
@ -166,7 +173,7 @@ namespace winrt::TerminalApp::implementation
}
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
{
_SplitPane(realArgs.SplitStyle(),
_SplitPane(realArgs.SplitDirection(),
realArgs.SplitMode(),
// This is safe, we're already filtering so the value is (0, 1)
::base::saturated_cast<float>(realArgs.SplitSize()),
@ -875,6 +882,13 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleOpenSystemMenu(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_OpenSystemMenuHandlers(*this, nullptr);
args.Handled(true);
}
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View file

@ -274,18 +274,18 @@ void AppCommandlineArgs::_buildSplitPaneParser()
// _getNewTerminalArgs MUST be called before parsing any other options,
// as it might clear those options while finding the commandline
auto terminalArgs{ _getNewTerminalArgs(subcommand) };
auto style{ SplitState::Automatic };
auto style{ SplitDirection::Automatic };
// Make sure to use the `Option`s here to check if they were set -
// _getNewTerminalArgs might reset them while parsing a commandline
if ((*subcommand._horizontalOption || *subcommand._verticalOption))
{
if (_splitHorizontal)
{
style = SplitState::Horizontal;
style = SplitDirection::Down;
}
else if (_splitVertical)
{
style = SplitState::Vertical;
style = SplitDirection::Right;
}
}
const auto splitMode{ subcommand._duplicateOption && _splitDuplicate ? SplitType::Duplicate : SplitType::Manual };

View file

@ -329,6 +329,14 @@ namespace winrt::TerminalApp::implementation
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
void AppLogic::Quit()
{
if (_root)
{
_root->CloseWindow(true);
}
}
// Method Description:
// - Show a ContentDialog with buttons to take further action. Uses the
// FrameworkElements provided as the title and content of this dialog, and
@ -596,12 +604,30 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
// Use the default profile to determine how big of a window we need.
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) };
auto proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi);
winrt::Windows::Foundation::Size proposedSize{};
const float scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
if (_root->ShouldUsePersistedLayout(_settings))
{
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialSize())
{
proposedSize = layouts.GetAt(0).InitialSize().Value();
// The size is saved as a non-scaled real pixel size,
// so we need to scale it appropriately.
proposedSize.Height = proposedSize.Height * scale;
proposedSize.Width = proposedSize.Width * scale;
}
}
if (proposedSize.Width == 0 && proposedSize.Height == 0)
{
// Use the default profile to determine how big of a window we need.
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) };
proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi);
}
// GH#2061 - If the global setting "Always show tab bar" is
// set or if "Show tabs in title bar" is set, then we'll need to add
@ -683,7 +709,18 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
const auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
if (_root->ShouldUsePersistedLayout(_settings))
{
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialPosition())
{
initialPosition = layouts.GetAt(0).InitialPosition().Value();
}
}
return {
initialPosition.X ? initialPosition.X.Value() : defaultInitialX,
initialPosition.Y ? initialPosition.Y.Value() : defaultInitialY
@ -1125,7 +1162,7 @@ namespace winrt::TerminalApp::implementation
{
if (_root)
{
_root->CloseWindow();
_root->CloseWindow(false);
}
}
@ -1429,6 +1466,14 @@ namespace winrt::TerminalApp::implementation
}
}
void AppLogic::SetNumberOfOpenWindows(const uint64_t num)
{
if (_root)
{
_root->SetNumberOfOpenWindows(num);
}
}
void AppLogic::RenameFailed()
{
if (_root)
@ -1442,9 +1487,9 @@ namespace winrt::TerminalApp::implementation
return _root->IsQuakeWindow();
}
bool AppLogic::GetMinimizeToTray()
bool AppLogic::GetMinimizeToNotificationArea()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (!_loadedInitialSettings)
{
@ -1452,7 +1497,7 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
return _settings.GlobalSettings().MinimizeToTray();
return _settings.GlobalSettings().MinimizeToNotificationArea();
}
else
{
@ -1460,9 +1505,9 @@ namespace winrt::TerminalApp::implementation
}
}
bool AppLogic::GetAlwaysShowTrayIcon()
bool AppLogic::GetAlwaysShowNotificationIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (!_loadedInitialSettings)
{
@ -1470,7 +1515,7 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
return _settings.GlobalSettings().AlwaysShowTrayIcon();
return _settings.GlobalSettings().AlwaysShowNotificationIcon();
}
else
{

View file

@ -53,6 +53,8 @@ namespace winrt::TerminalApp::implementation
void LoadSettings();
[[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
void Quit();
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions);
@ -69,6 +71,7 @@ namespace winrt::TerminalApp::implementation
void WindowName(const winrt::hstring& name);
uint64_t WindowId();
void WindowId(const uint64_t& id);
void SetNumberOfOpenWindows(const uint64_t num);
bool IsQuakeWindow() const noexcept;
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
@ -92,8 +95,8 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TaskbarState TaskbarState();
bool GetMinimizeToTray();
bool GetAlwaysShowTrayIcon();
bool GetMinimizeToNotificationArea();
bool GetAlwaysShowNotificationIcon();
bool GetShowTitleInTitlebar();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
@ -172,6 +175,8 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;

View file

@ -39,6 +39,8 @@ namespace TerminalApp
String ParseCommandlineMessage { get; };
Boolean ShouldExitEarly { get; };
void Quit();
void LoadSettings();
Windows.UI.Xaml.UIElement GetRoot();
@ -53,6 +55,7 @@ namespace TerminalApp
void IdentifyWindow();
String WindowName;
UInt64 WindowId;
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed();
Boolean IsQuakeWindow();
@ -70,8 +73,8 @@ namespace TerminalApp
TaskbarState TaskbarState{ get; };
Boolean GetMinimizeToTray();
Boolean GetAlwaysShowTrayIcon();
Boolean GetMinimizeToNotificationArea();
Boolean GetAlwaysShowNotificationIcon();
Boolean GetShowTitleInTitlebar();
FindTargetWindowResult FindTargetWindow(String[] args);
@ -96,5 +99,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
}
}

View file

@ -407,7 +407,7 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Windows10version1903:Shadow="{StaticResource CommandPaletteShadow}"
CornerRadius="{ThemeResource ControlCornerRadius}"
CornerRadius="{ThemeResource OverlayCornerRadius}"
PointerPressed="_backdropPointerPressed"
Style="{ThemeResource CommandPaletteBackground}">

View file

@ -116,6 +116,155 @@ Pane::Pane(std::shared_ptr<Pane> first,
});
}
// Method Description:
// - Extract the terminal settings from the current (leaf) pane's control
// to be used to create an equivalent control
// Arguments:
// - <none>
// Return Value:
// - Arguments appropriate for a SplitPane or NewTab action
NewTerminalArgs Pane::GetTerminalArgsForPane() const
{
// Leaves are the only things that have controls
assert(_IsLeaf());
NewTerminalArgs args{};
auto controlSettings = _control.Settings().as<TerminalSettings>();
args.Profile(controlSettings.ProfileName());
args.StartingDirectory(controlSettings.StartingDirectory());
args.TabTitle(controlSettings.StartingTitle());
args.Commandline(controlSettings.Commandline());
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
if (controlSettings.TabColor() || controlSettings.StartingTabColor())
{
til::color c;
// StartingTabColor is prioritized over other colors
if (const auto color = controlSettings.StartingTabColor())
{
c = til::color(color.Value());
}
else
{
c = til::color(controlSettings.TabColor().Value());
}
args.TabColor(winrt::Windows::Foundation::IReference<winrt::Windows::UI::Color>(c));
}
if (controlSettings.AppliedColorScheme())
{
auto name = controlSettings.AppliedColorScheme().Name();
args.ColorScheme(name);
}
return args;
}
// Method Description:
// - Serializes the state of this tab as a series of commands that can be
// executed to recreate it.
// - This will always result in the right-most child being the focus
// after the commands finish executing.
// Arguments:
// - currentId: the id to use for the current/first pane
// - nextId: the id to use for a new pane if we split
// Return Value:
// - The state from building the startup actions, includes a vector of commands,
// the original root pane, the id of the focused pane, and the number of panes
// created.
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId)
{
// if we are a leaf then all there is to do is defer to the parent.
if (_IsLeaf())
{
if (_lastActive)
{
return { {}, shared_from_this(), currentId, 0 };
}
return { {}, shared_from_this(), std::nullopt, 0 };
}
auto buildSplitPane = [&](auto newPane) {
ActionAndArgs actionAndArgs;
actionAndArgs.Action(ShortcutAction::SplitPane);
const auto terminalArgs{ newPane->GetTerminalArgsForPane() };
// When creating a pane the split size is the size of the new pane
// and not position.
const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right;
SplitPaneArgs args{ SplitType::Manual, splitDirection, 1. - _desiredSplitPosition, terminalArgs };
actionAndArgs.Args(args);
return actionAndArgs;
};
auto buildMoveFocus = [](auto direction) {
MoveFocusArgs args{ direction };
ActionAndArgs actionAndArgs{};
actionAndArgs.Action(ShortcutAction::MoveFocus);
actionAndArgs.Args(args);
return actionAndArgs;
};
// Handle simple case of a single split (a minor optimization for clarity)
// Here we just create the second child (by splitting) and return the first
// child for the parent to deal with.
if (_firstChild->_IsLeaf() && _secondChild->_IsLeaf())
{
auto actionAndArgs = buildSplitPane(_secondChild);
std::optional<uint32_t> focusedPaneId = std::nullopt;
if (_firstChild->_lastActive)
{
focusedPaneId = currentId;
}
else if (_secondChild->_lastActive)
{
focusedPaneId = nextId;
}
return { { actionAndArgs }, _firstChild, focusedPaneId, 1 };
}
// We now need to execute the commands for each side of the tree
// We've done one split, so the first-most child will have currentId, and the
// one after it will be incremented.
auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1);
// the next id for the second branch depends on how many splits were in the
// first child.
auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1);
std::vector<ActionAndArgs> actions{};
actions.reserve(firstState.args.size() + secondState.args.size() + 3);
// first we make our split
const auto newSplit = buildSplitPane(secondState.firstPane);
actions.emplace_back(std::move(newSplit));
if (firstState.args.size() > 0)
{
// Then move to the first child and execute any actions on the left branch
// then move back
actions.emplace_back(buildMoveFocus(FocusDirection::PreviousInOrder));
actions.insert(actions.end(), std::make_move_iterator(std::begin(firstState.args)), std::make_move_iterator(std::end(firstState.args)));
actions.emplace_back(buildMoveFocus(FocusDirection::NextInOrder));
}
// And if there are any commands to run on the right branch do so
if (secondState.args.size() > 0)
{
actions.insert(actions.end(), std::make_move_iterator(secondState.args.begin()), std::make_move_iterator(secondState.args.end()));
}
// if the tree is well-formed then f1.has_value and f2.has_value are
// mutually exclusive.
const auto focusedPaneId = firstState.focusedPaneId.has_value() ? firstState.focusedPaneId : secondState.focusedPaneId;
return { actions, firstState.firstPane, focusedPaneId, firstState.panesCreated + secondState.panesCreated + 1 };
}
// Method Description:
// - Update the size of this pane. Resizes each of our columns so they have the
// same relative sizes, given the newSize.
@ -1315,7 +1464,7 @@ void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Pr
// - 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)
std::shared_ptr<Pane> Pane::AttachPane(std::shared_ptr<Pane> pane, SplitDirection splitType)
{
// Splice the new pane into the tree
const auto [first, _] = _Split(splitType, .5, pane);
@ -2056,7 +2205,7 @@ void Pane::_SetupEntranceAnimation()
// Note:
// - This method is highly similar to Pane::PreCalculateAutoSplit
std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> target,
SplitState splitType,
SplitDirection splitType,
const float splitSize,
const winrt::Windows::Foundation::Size availableSpace) const
{
@ -2068,12 +2217,7 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
// the available space to calculate which direction to split in.
const Size minSize = _GetMinSize();
if (splitType == SplitState::None)
{
return { false };
}
else if (splitType == SplitState::Vertical)
if (splitType == SplitDirection::Left || splitType == SplitDirection::Right)
{
const auto widthMinusSeparator = availableSpace.Width - CombinedPaneBorderSize;
const auto newFirstWidth = widthMinusSeparator * firstPrecent;
@ -2082,7 +2226,7 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
return { newFirstWidth > minSize.Width && newSecondWidth > minSize.Width };
}
else if (splitType == SplitState::Horizontal)
else if (splitType == SplitDirection::Up || splitType == SplitDirection::Down)
{
const auto heightMinusSeparator = availableSpace.Height - CombinedPaneBorderSize;
const auto newFirstHeight = heightMinusSeparator * firstPrecent;
@ -2134,8 +2278,8 @@ std::optional<bool> Pane::PreCalculateCanSplit(const std::shared_ptr<Pane> targe
// - profile: The profile to associate with the newly created pane.
// - control: A TermControl to use in the new pane.
// Return Value:
// - The two newly created Panes
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState splitType,
// - The two newly created Panes, with the original pane first
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitDirection splitType,
const float splitSize,
const Profile& profile,
const TermControl& control)
@ -2198,18 +2342,18 @@ bool Pane::ToggleSplitOrientation()
// Method Description:
// - Converts an "automatic" split type into either Vertical or Horizontal,
// based upon the current dimensions of the Pane.
// - If any of the other SplitState values are passed in, they're returned
// unmodified.
// - Similarly, if Up/Down or Left/Right are provided a Horizontal or Vertical
// split type will be returned.
// Arguments:
// - splitType: The SplitState to attempt to convert
// - splitType: The SplitDirection to attempt to convert
// Return Value:
// - None if splitType was None, otherwise one of Horizontal or Vertical
SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
// - One of Horizontal or Vertical
SplitState Pane::_convertAutomaticOrDirectionalSplitState(const SplitDirection& splitType) const
{
// Careful here! If the pane doesn't yet have a size, these dimensions will
// be 0, and we'll always return Vertical.
if (splitType == SplitState::Automatic)
if (splitType == SplitDirection::Automatic)
{
// If the requested split type was "auto", determine which direction to
// split based on our current dimensions
@ -2217,7 +2361,12 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
gsl::narrow_cast<float>(_root.ActualHeight()) };
return actualSize.Width >= actualSize.Height ? SplitState::Vertical : SplitState::Horizontal;
}
return splitType;
if (splitType == SplitDirection::Up || splitType == SplitDirection::Down)
{
return SplitState::Horizontal;
}
// All that is left is Left / Right which are vertical splits
return SplitState::Vertical;
}
// Method Description:
@ -2228,17 +2377,12 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const
// - 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,
// - The two newly created Panes, with the original pane as the first pane.
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane)
{
if (splitType == SplitState::None)
{
return { nullptr, nullptr };
}
auto actualSplitType = _convertAutomaticSplitState(splitType);
auto actualSplitType = _convertAutomaticOrDirectionalSplitState(splitType);
// Lock the create/close lock so that another operation won't concurrently
// modify our tree
@ -2289,6 +2433,11 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
_splitState = actualSplitType;
_desiredSplitPosition = 1.0f - splitSize;
_secondChild = newPane;
// If we want the new pane to be the first child, swap the children
if (splitType == SplitDirection::Up || splitType == SplitDirection::Left)
{
std::swap(_firstChild, _secondChild);
}
_root.ColumnDefinitions().Clear();
_root.RowDefinitions().Clear();
@ -2312,6 +2461,12 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
// Clear out our ID, only leaves should have IDs
_id = {};
// Regardless of which child the new child is, we want to return the
// original one first.
if (splitType == SplitDirection::Up || splitType == SplitDirection::Left)
{
return { _secondChild, _firstChild };
}
return { _firstChild, _secondChild };
}
@ -2966,16 +3121,16 @@ int Pane::GetLeafPaneCount() const noexcept
// - availableSpace: The theoretical space that's available for this pane to be able to split.
// Return Value:
// - nullopt if `target` is not this pane or a child of this pane, otherwise the
// SplitState that `target` would use for an `Automatic` split given
// SplitDirection that `target` would use for an `Automatic` split given
// `availableSpace`
std::optional<SplitState> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size availableSpace) const
std::optional<SplitDirection> Pane::PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size availableSpace) const
{
if (target.get() == this)
{
// If this pane is the pane we are looking for, use the available space
// to calculate which direction to split in.
return availableSpace.Width > availableSpace.Height ? SplitState::Vertical : SplitState::Horizontal;
return availableSpace.Width > availableSpace.Height ? SplitDirection::Right : SplitDirection::Down;
}
else if (_IsLeaf())
{

View file

@ -45,6 +45,13 @@ enum class Borders : int
};
DEFINE_ENUM_FLAG_OPERATORS(Borders);
enum class SplitState : int
{
None = 0,
Horizontal = 1,
Vertical = 2
};
class Pane : public std::enable_shared_from_this<Pane>
{
public:
@ -54,7 +61,7 @@ public:
Pane(std::shared_ptr<Pane> first,
std::shared_ptr<Pane> second,
const winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
const SplitState splitType,
const float splitPosition,
const bool lastFocused = false);
@ -78,6 +85,16 @@ public:
void ClearActive();
void SetActive();
struct BuildStartupState
{
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> args;
std::shared_ptr<Pane> firstPane;
std::optional<uint32_t> focusedPaneId;
uint32_t panesCreated;
};
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId);
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane() const;
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
@ -91,23 +108,23 @@ public:
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
std::shared_ptr<Pane> PreviousPane(const std::shared_ptr<Pane> pane);
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Control::TermControl& control);
bool ToggleSplitOrientation();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size parentSize) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
const winrt::Windows::Foundation::Size parentSize) const;
std::optional<bool> PreCalculateCanSplit(const std::shared_ptr<Pane> target,
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
const winrt::Windows::Foundation::Size availableSpace) const;
void Shutdown();
void Close();
std::shared_ptr<Pane> AttachPane(std::shared_ptr<Pane> pane,
winrt::Microsoft::Terminal::Settings::Model::SplitState splitType);
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType);
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
int GetLeafPaneCount() const noexcept;
@ -176,7 +193,7 @@ private:
std::shared_ptr<Pane> _firstChild{ nullptr };
std::shared_ptr<Pane> _secondChild{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::SplitState _splitState{ winrt::Microsoft::Terminal::Settings::Model::SplitState::None };
SplitState _splitState{ SplitState::None };
float _desiredSplitPosition;
std::optional<uint32_t> _id;
@ -203,7 +220,7 @@ private:
void _SetupChildCloseHandlers();
bool _HasChild(const std::shared_ptr<Pane> child);
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
@ -247,9 +264,9 @@ private:
LayoutSizeNode _CreateMinSizeTree(const bool widthOrHeight) const;
float _ClampSplitPosition(const bool widthOrHeight, const float requestedValue, const float totalSize) const;
winrt::Microsoft::Terminal::Settings::Model::SplitState _convertAutomaticSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitState& splitType) const;
SplitState _convertAutomaticOrDirectionalSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitDirection& splitType) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> _preCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> _preCalculateAutoSplit(const std::shared_ptr<Pane> target, const winrt::Windows::Foundation::Size parentSize) const;
// Function Description:
// - Returns true if the given direction can be used with the given split
@ -260,24 +277,24 @@ private:
// - Also used for moving focus between panes, which again happens _across_ a separator.
// Arguments:
// - direction: The Direction to compare
// - splitType: The winrt::TerminalApp::SplitState to compare
// - splitType: The SplitState to compare
// Return Value:
// - true iff the direction is perpendicular to the splitType. False for
// winrt::TerminalApp::SplitState::None.
// SplitState::None.
template<typename T>
static constexpr bool DirectionMatchesSplit(const T& direction,
const winrt::Microsoft::Terminal::Settings::Model::SplitState& splitType)
const SplitState& splitType)
{
if (splitType == winrt::Microsoft::Terminal::Settings::Model::SplitState::None)
if (splitType == SplitState::None)
{
return false;
}
else if (splitType == winrt::Microsoft::Terminal::Settings::Model::SplitState::Horizontal)
else if (splitType == SplitState::Horizontal)
{
return direction == T::Up ||
direction == T::Down;
}
else if (splitType == winrt::Microsoft::Terminal::Settings::Model::SplitState::Vertical)
else if (splitType == SplitState::Vertical)
{
return direction == T::Left ||
direction == T::Right;

View file

@ -184,6 +184,9 @@
<data name="CloseAll" xml:space="preserve">
<value>Close all</value>
</data>
<data name="Quit" xml:space="preserve">
<value>Quit</value>
</data>
<data name="CloseWindowWarningTitle" xml:space="preserve">
<value>Do you want to close all tabs?</value>
</data>
@ -444,6 +447,15 @@
<value>Third-Party Notices</value>
<comment>A hyperlink name for the Terminal's third-party notices</comment>
</data>
<data name="QuitDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="QuitDialog.PrimaryButtonText" xml:space="preserve">
<value>Close all</value>
</data>
<data name="QuitDialog.Title" xml:space="preserve">
<value>Do you want to close all windows?</value>
</data>
<data name="CloseAllDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
@ -656,11 +668,11 @@
<data name="CommandPaletteMenuItem" xml:space="preserve">
<value>Command Palette</value>
</data>
<data name="TrayIconFocusTerminal" xml:space="preserve">
<data name="NotificationIconFocusTerminal" 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">
<data name="NotificationIconWindowSubmenu" 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>
@ -700,4 +712,10 @@
<data name="PlainText" xml:space="preserve">
<value>Plain Text</value>
</data>
<data name="CloseOnExitInfoBar.Message" xml:space="preserve">
<value>Termination behavior can be configured in advanced profile settings.</value>
</data>
<data name="InfoBarDismissButton.Content" xml:space="preserve">
<value>Don't show again</value>
</data>
</root>

View file

@ -289,7 +289,7 @@ namespace winrt::TerminalApp::implementation
auto newControl = _InitControl(settings, debugConnection);
_RegisterTerminalEvents(newControl);
// Split (auto) with the debug tap.
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profile, newControl);
newTabImpl->SplitPane(SplitDirection::Automatic, 0.5f, profile, newControl);
}
}
@ -399,7 +399,7 @@ namespace winrt::TerminalApp::implementation
try
{
_SetFocusedTab(tab);
_SplitPane(tab, SplitState::Automatic, SplitType::Duplicate);
_SplitPane(tab, SplitDirection::Automatic, SplitType::Duplicate);
}
CATCH_LOG();
}
@ -499,6 +499,14 @@ namespace winrt::TerminalApp::implementation
// To close the window here, we need to close the hosting window.
if (_tabs.Size() == 0)
{
// If we are supposed to save state, make sure we clear it out
// if the user manually closed all tabs.
if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings))
{
auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(nullptr);
}
_LastTabClosedHandlers(*this, nullptr);
}
else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast<uint32_t>(tabIndex))
@ -599,9 +607,13 @@ namespace winrt::TerminalApp::implementation
tabIndex = std::clamp(tabIndex, 0u, _tabs.Size() - 1);
auto tab{ _tabs.GetAt(tabIndex) };
// GH#11107 - Always just set the item directly first so that if
// tab movement is done as part of multiple actions following calls
// to _GetFocusedTab will return the correct tab.
_tabView.SelectedItem(tab.TabViewItem());
if (_startupState == StartupState::InStartup)
{
_tabView.SelectedItem(tab.TabViewItem());
_UpdatedSelectedTab(tab);
}
else

View file

@ -276,6 +276,21 @@ namespace winrt::TerminalApp::implementation
CATCH_LOG();
}
// Method Description;
// - Checks if the current terminal window should load or save its layout information.
// Arguments:
// - settings: The settings to use as this may be called before the page is
// fully initialized.
// Return Value:
// - true if the ApplicationState should be used.
bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const
{
// If the setting is enabled, and we are the only window.
return Feature_PersistedWindowLayout::IsEnabled() &&
settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout &&
_numOpenWindows == 1;
}
winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e)
{
Windows::Foundation::Collections::IVectorView<Windows::Storage::IStorageItem> items;
@ -358,6 +373,34 @@ namespace winrt::TerminalApp::implementation
if (_startupState == StartupState::NotInitialized)
{
_startupState = StartupState::InStartup;
// If the user selected to save their tab layout, we are the first
// window opened, and wt was not run with any other arguments, then
// we should use the saved settings.
auto firstActionIsDefault = [](ActionAndArgs action) {
if (action.Action() != ShortcutAction::NewTab)
{
return false;
}
// If no commands were given, we will have default args
if (const auto args = action.Args().try_as<NewTabArgs>())
{
NewTerminalArgs defaultArgs{};
return args.TerminalArgs() == nullptr || args.TerminalArgs().Equals(defaultArgs);
}
return false;
};
if (ShouldUsePersistedLayout(_settings) && _startupActions.Size() == 1 && firstActionIsDefault(_startupActions.GetAt(0)))
{
auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).TabLayout() && layouts.GetAt(0).TabLayout().Size() > 0)
{
_startupActions = layouts.GetAt(0).TabLayout();
}
}
ProcessStartupActions(_startupActions, true);
// If we were told that the COM server needs to be started to listen for incoming
@ -515,6 +558,21 @@ namespace winrt::TerminalApp::implementation
ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW);
}
// Method Description:
// - Displays a dialog to warn the user that they are about to close all open windows.
// Once the user clicks the OK button, shut down the application.
// If cancel is clicked, the dialog will close.
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. See _ShowDialog for details
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowQuitDialog()
{
if (auto presenter{ _dialogPresenter.get() })
{
co_return co_await presenter.ShowDialog(FindName(L"QuitDialog").try_as<WUX::Controls::ContentDialog>());
}
co_return ContentDialogResult::None;
}
// Method Description:
// - Displays a dialog for warnings found while closing the terminal app using
// key binding with multiple tabs opened. Display messages to warn user
@ -769,7 +827,7 @@ namespace winrt::TerminalApp::implementation
if (altPressed && !debugTap)
{
this->_SplitPane(SplitState::Automatic,
this->_SplitPane(SplitDirection::Automatic,
SplitType::Manual,
0.5f,
newTerminalArgs);
@ -1050,6 +1108,8 @@ namespace winrt::TerminalApp::implementation
// 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 });
term.ConnectionStateChanged({ get_weak(), &TerminalPage::_ConnectionStateChangedHandler });
}
// Method Description:
@ -1194,13 +1254,116 @@ namespace winrt::TerminalApp::implementation
}
return nullptr;
}
// Method Description:
// - Warn the user that they are about to close all open windows, then
// signal that we want to close everything.
fire_and_forget TerminalPage::RequestQuit()
{
if (!_displayingCloseDialog)
{
_displayingCloseDialog = true;
ContentDialogResult warningResult = co_await _ShowQuitDialog();
_displayingCloseDialog = false;
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}
_QuitRequestedHandlers(nullptr, nullptr);
}
}
// Method Description:
// - Saves the window position and tab layout to the application state
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::PersistWindowLayout()
{
std::vector<ActionAndArgs> actions;
for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
auto tabActions = terminalTab->BuildStartupActions();
actions.insert(actions.end(), tabActions.begin(), tabActions.end());
}
else if (tab.try_as<SettingsTab>())
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);
actions.push_back(action);
}
}
// if the focused tab was not the last tab, restore that
auto idx = _GetFocusedTabIndex();
if (idx && idx != _tabs.Size() - 1)
{
ActionAndArgs action;
action.Action(ShortcutAction::SwitchToTab);
SwitchToTabArgs switchToTabArgs{ idx.value() };
action.Args(switchToTabArgs);
actions.push_back(action);
}
WindowLayout layout{};
layout.TabLayout(winrt::single_threaded_vector<ActionAndArgs>(std::move(actions)));
// Only save the content size because the tab size will be added on load.
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size windowSize{ contentWidth, contentHeight };
layout.InitialSize(windowSize);
if (_hostingHwnd)
{
// Get the position of the current window. This includes the
// non-client already.
RECT window{};
GetWindowRect(_hostingHwnd.value(), &window);
// We want to remove the non-client area so calculate that.
// We don't have access to the (NonClient)IslandWindow directly so
// just replicate the logic.
const auto windowStyle = static_cast<DWORD>(GetWindowLong(_hostingHwnd.value(), GWL_STYLE));
auto dpi = GetDpiForWindow(_hostingHwnd.value());
RECT nonClientArea{};
LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&nonClientArea, windowStyle, false, 0, dpi));
// The nonClientArea adjustment is negative, so subtract that out.
// This way we save the user-visible location of the terminal.
LaunchPosition pos{};
pos.X = window.left - nonClientArea.left;
pos.Y = window.top;
layout.InitialPosition(pos);
}
auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(winrt::single_threaded_vector<WindowLayout>({ layout }));
}
// Method Description:
// - Close the terminal app. If there is more
// than one tab opened, show a warning dialog.
fire_and_forget TerminalPage::CloseWindow()
// Arguments:
// - bypassDialog: if true a dialog won't be shown even if the user would
// normally get confirmation. This is used in the case where the user
// has already been prompted by the Quit action.
fire_and_forget TerminalPage::CloseWindow(bool bypassDialog)
{
if (_HasMultipleTabs() &&
if (!bypassDialog &&
_HasMultipleTabs() &&
_settings.GlobalSettings().ConfirmCloseAllTabs() &&
!_displayingCloseDialog)
{
@ -1214,6 +1377,13 @@ namespace winrt::TerminalApp::implementation
}
}
if (ShouldUsePersistedLayout(_settings))
{
PersistWindowLayout();
// don't delete the ApplicationState when all of the tabs are removed.
_maintainStateOnTabClose = true;
}
_RemoveAllTabs();
}
@ -1298,25 +1468,18 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given TermControl into the newly created pane.
// - If splitType == SplitState::None, this method does nothing.
// Arguments:
// - splitType: one value from the TerminalApp::SplitState enum, indicating how the
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
// new pane should be split from its parent.
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
// - newTerminalArgs: An object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
void TerminalPage::_SplitPane(const SplitState splitType,
void TerminalPage::_SplitPane(const SplitDirection splitDirection,
const SplitType splitMode,
const float splitSize,
const NewTerminalArgs& newTerminalArgs)
{
// Do nothing if we're requesting no split.
if (splitType == SplitState::None)
{
return;
}
const auto focusedTab{ _GetFocusedTabImpl() };
// Do nothing if no TerminalTab is focused
@ -1325,33 +1488,26 @@ namespace winrt::TerminalApp::implementation
return;
}
_SplitPane(*focusedTab, splitType, splitMode, splitSize, newTerminalArgs);
_SplitPane(*focusedTab, splitDirection, splitMode, splitSize, newTerminalArgs);
}
// Method Description:
// - Split the focused pane of the given tab, either horizontally or vertically, and place the
// given TermControl into the newly created pane.
// - If splitType == SplitState::None, this method does nothing.
// Arguments:
// - tab: The tab that is going to be split.
// - splitType: one value from the TerminalApp::SplitState enum, indicating how the
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
// new pane should be split from its parent.
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
// - newTerminalArgs: An object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
void TerminalPage::_SplitPane(TerminalTab& tab,
const SplitState splitType,
const SplitDirection splitDirection,
const SplitType splitMode,
const float splitSize,
const NewTerminalArgs& newTerminalArgs)
{
// Do nothing if we're requesting no split.
if (splitType == SplitState::None)
{
return;
}
try
{
TerminalSettingsCreateResult controlSettings{ nullptr };
@ -1397,8 +1553,8 @@ namespace winrt::TerminalApp::implementation
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
auto realSplitType = splitType;
if (realSplitType == SplitState::Automatic)
auto realSplitType = splitDirection;
if (realSplitType == SplitDirection::Automatic)
{
realSplitType = tab.PreCalculateAutoSplit(availableSpace);
}
@ -2708,11 +2864,14 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Displays a dialog stating the "Touch Keyboard and Handwriting Panel
// Service" is disabled.
void TerminalPage::ShowKeyboardServiceWarning()
void TerminalPage::ShowKeyboardServiceWarning() const
{
if (auto keyboardWarningInfoBar = FindName(L"KeyboardWarningInfoBar").try_as<MUX::Controls::InfoBar>())
if (!_IsMessageDismissed(InfoBarMessage::KeyboardServiceWarning))
{
keyboardWarningInfoBar.IsOpen(true);
if (const auto keyboardServiceWarningInfoBar = FindName(L"KeyboardServiceWarningInfoBar").try_as<MUX::Controls::InfoBar>())
{
keyboardServiceWarningInfoBar.IsOpen(true);
}
}
}
@ -2932,6 +3091,11 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::SetNumberOfOpenWindows(const uint64_t num)
{
_numOpenWindows = num;
}
// Method Description:
// - Returns a label like "Window: 1234" for the ID of this window
// Arguments:
@ -3069,4 +3233,99 @@ namespace winrt::TerminalApp::implementation
}
return profile;
}
// Method Description:
// - Handles the change of connection state.
// If the connection state is failure show information bar suggesting to configure termination behavior
// (unless user asked not to show this message again)
// Arguments:
// - sender: the ICoreState instance containing the connection state
// Return Value:
// - <none>
winrt::fire_and_forget TerminalPage::_ConnectionStateChangedHandler(const IInspectable& sender, const IInspectable& /*args*/) const
{
if (const auto coreState{ sender.try_as<winrt::Microsoft::Terminal::Control::ICoreState>() })
{
const auto newConnectionState = coreState.ConnectionState();
if (newConnectionState == ConnectionState::Failed && !_IsMessageDismissed(InfoBarMessage::CloseOnExitInfo))
{
co_await winrt::resume_foreground(Dispatcher());
if (const auto infoBar = FindName(L"CloseOnExitInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(true);
}
}
}
}
// Method Description:
// - Persists the user's choice not to show information bar guiding to configure termination behavior.
// Then hides this information buffer.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_CloseOnExitInfoDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) const
{
_DismissMessage(InfoBarMessage::CloseOnExitInfo);
if (const auto infoBar = FindName(L"CloseOnExitInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(false);
}
}
// Method Description:
// - Persists the user's choice not to show information bar warning about "Touch keyboard and Handwriting Panel Service" disabled
// Then hides this information buffer.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_KeyboardServiceWarningInfoDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) const
{
_DismissMessage(InfoBarMessage::KeyboardServiceWarning);
if (const auto infoBar = FindName(L"KeyboardServiceWarningInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(false);
}
}
// Method Description:
// - Checks whether information bar message was dismissed earlier (in the application state)
// Arguments:
// - message: message to look for in the state
// Return Value:
// - true, if the message was dismissed
bool TerminalPage::_IsMessageDismissed(const InfoBarMessage& message)
{
if (const auto dismissedMessages{ ApplicationState::SharedInstance().DismissedMessages() })
{
for (const auto& dismissedMessage : dismissedMessages)
{
if (dismissedMessage == message)
{
return true;
}
}
}
return false;
}
// Method Description:
// - Persists the user's choice to dismiss information bar message (in application state)
// Arguments:
// - message: message to dismiss
// Return Value:
// - <none>
void TerminalPage::_DismissMessage(const InfoBarMessage& message)
{
auto dismissedMessages = ApplicationState::SharedInstance().DismissedMessages();
if (!dismissedMessages)
{
dismissedMessages = winrt::single_threaded_vector<InfoBarMessage>();
}
dismissedMessages.Append(message);
ApplicationState::SharedInstance().DismissedMessages(dismissedMessages);
}
}

View file

@ -58,6 +58,8 @@ namespace winrt::TerminalApp::implementation
void Create();
bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e);
hstring Title();
@ -69,7 +71,8 @@ namespace winrt::TerminalApp::implementation
winrt::hstring ApplicationDisplayName();
winrt::hstring ApplicationVersion();
winrt::fire_and_forget CloseWindow();
winrt::fire_and_forget RequestQuit();
winrt::fire_and_forget CloseWindow(bool bypassDialog);
void ToggleFocusMode();
void ToggleFullscreen();
@ -79,6 +82,8 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const;
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
void PersistWindowLayout();
void SetInboundListener(bool isEmbedding);
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
@ -87,7 +92,7 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TaskbarState TaskbarState() const;
void ShowKeyboardServiceWarning();
void ShowKeyboardServiceWarning() const;
winrt::hstring KeyboardServiceDisabledText();
winrt::fire_and_forget IdentifyWindow();
@ -104,6 +109,9 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget WindowName(const winrt::hstring& value);
uint64_t WindowId() const noexcept;
void WindowId(const uint64_t& value);
void SetNumberOfOpenWindows(const uint64_t value);
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;
@ -124,6 +132,8 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable);
TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
@ -155,10 +165,12 @@ namespace winrt::TerminalApp::implementation
bool _isAlwaysOnTop{ false };
winrt::hstring _WindowName{};
uint64_t _WindowId{ 0 };
uint64_t _numOpenWindows{ 0 };
bool _rearranging;
std::optional<int> _rearrangeFrom;
std::optional<int> _rearrangeTo;
bool _maintainStateOnTabClose{ false };
bool _rearranging{ false };
std::optional<int> _rearrangeFrom{};
std::optional<int> _rearrangeTo{};
bool _removing{ false };
uint32_t _systemRowsToScroll{ DefaultRowsToScroll };
@ -180,6 +192,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Toast> _windowRenameFailedToast{ nullptr };
void _ShowAboutDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowQuitDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseWarningDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseReadOnlyDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowMultiLinePasteWarningDialog();
@ -256,12 +269,12 @@ namespace winrt::TerminalApp::implementation
void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference<uint32_t>& rowsToScroll);
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitState splitType,
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
const float splitSize = 0.5f,
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
void _SplitPane(TerminalTab& tab,
const Microsoft::Terminal::Settings::Model::SplitState splitType,
const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
const float splitSize = 0.5f,
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
@ -361,6 +374,12 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Settings::Model::Profile GetClosestProfileForDuplicationOfProfile(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile) const noexcept;
winrt::fire_and_forget _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);

View file

@ -33,6 +33,7 @@ namespace TerminalApp
UInt64 WindowId;
String WindowNameForDisplay { get; };
String WindowIdForDisplay { get; };
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed();
Boolean IsQuakeWindow();
@ -57,5 +58,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
}
}

View file

@ -47,6 +47,11 @@
</StackPanel>
</ContentDialog>
<ContentDialog x:Name="QuitDialog"
x:Uid="QuitDialog"
x:Load="False"
DefaultButton="Primary" />
<ContentDialog x:Name="CloseAllDialog"
x:Uid="CloseAllDialog"
x:Load="False"
@ -109,13 +114,33 @@
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
<mux:InfoBar x:Name="KeyboardWarningInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"
Severity="Warning" />
<StackPanel>
<mux:InfoBar x:Name="KeyboardServiceWarningInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"
Severity="Warning">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_KeyboardServiceWarningInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
<mux:InfoBar x:Name="CloseOnExitInfoBar"
x:Uid="CloseOnExitInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_CloseOnExitInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
</StackPanel>
<!--
A TeachingTip with IsLightDismissEnabled="True" will immediately

View file

@ -444,6 +444,50 @@ namespace winrt::TerminalApp::implementation
control.ScrollViewport(::base::ClampAdd(currentOffset, delta));
}
// Method Description:
// - Serializes the state of this tab as a series of commands that can be
// executed to recreate it.
// Arguments:
// - <none>
// Return Value:
// - A vector of commands
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions() const
{
// Give initial ids (0 for the child created with this tab,
// 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1);
ActionAndArgs newTabAction{};
newTabAction.Action(ShortcutAction::NewTab);
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() };
newTabAction.Args(newTabArgs);
state.args.emplace(state.args.begin(), std::move(newTabAction));
// If we only have one arg, we only have 1 pane so we don't need any
// special focus logic
if (state.args.size() > 1 && state.focusedPaneId.has_value())
{
ActionAndArgs focusPaneAction{};
focusPaneAction.Action(ShortcutAction::FocusPane);
FocusPaneArgs focusArgs{ state.focusedPaneId.value() };
focusPaneAction.Args(focusArgs);
state.args.emplace_back(std::move(focusPaneAction));
}
if (_zoomedPane)
{
// we start without any panes zoomed so toggle zoom will enable zoom.
ActionAndArgs zoomPaneAction{};
zoomPaneAction.Action(ShortcutAction::TogglePaneZoom);
state.args.emplace_back(std::move(zoomPaneAction));
}
return state.args;
}
// Method Description:
// - Split the focused pane in our tree of panes, and place the
// given TermControl into the newly created pane.
@ -453,39 +497,38 @@ namespace winrt::TerminalApp::implementation
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void TerminalTab::SplitPane(SplitState splitType,
void TerminalTab::SplitPane(SplitDirection splitType,
const float splitSize,
const Profile& profile,
TermControl& control)
{
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
const auto activePaneId = _activePane->Id();
auto [first, second] = _activePane->Split(splitType, splitSize, profile, control);
// Depending on which direction will be split, the new pane can be
// either the first or second child, but this will always return the
// original pane first.
auto [original, newPane] = _activePane->Split(splitType, splitSize, profile, control);
// The active pane has an id if it is a leaf
if (activePaneId)
{
first->Id(activePaneId.value());
second->Id(_nextPaneId);
++_nextPaneId;
original->Id(activePaneId.value());
}
else
{
second->Id(_nextPaneId);
++_nextPaneId;
}
_activePane = first;
newPane->Id(_nextPaneId);
++_nextPaneId;
_activePane = original;
// 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);
_AttachEventHandlersToControl(newPane->Id().value(), control);
_AttachEventHandlersToPane(original);
_AttachEventHandlersToPane(newPane);
// Immediately update our tracker of the focused pane now. If we're
// splitting panes during startup (from a commandline), then it's
// possible that the focus events won't propagate immediately. Updating
// the focus here will give the same effect though.
_UpdateActivePane(second);
_UpdateActivePane(newPane);
}
// Method Description:
@ -571,7 +614,7 @@ namespace winrt::TerminalApp::implementation
const auto previousId = _activePane->Id();
// Add the new pane as an automatic split on the active pane.
auto first = _activePane->AttachPane(pane, SplitState::Automatic);
auto first = _activePane->AttachPane(pane, SplitDirection::Automatic);
// This will be true if the original _activePane is a leaf pane.
// If it is a parent pane then we don't want to set an ID on it.
@ -1501,14 +1544,14 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - availableSpace: The theoretical space that's available for this Tab's content
// Return Value:
// - The SplitState that we should use for an `Automatic` split given
// - The SplitDirection that we should use for an `Automatic` split given
// `availableSpace`
SplitState TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
SplitDirection TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
{
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitDirection::Right);
}
bool TerminalTab::PreCalculateCanSplit(SplitState splitType,
bool TerminalTab::PreCalculateCanSplit(SplitDirection splitType,
const float splitSize,
winrt::Windows::Foundation::Size availableSpace) const
{

View file

@ -38,7 +38,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> DetachPane();
void AttachPane(std::shared_ptr<Pane> pane);
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
winrt::Microsoft::Terminal::Control::TermControl& control);
@ -51,8 +51,8 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget ActivateBellIndicatorTimer();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
winrt::Microsoft::Terminal::Settings::Model::SplitState PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType,
winrt::Microsoft::Terminal::Settings::Model::SplitDirection PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const;
bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
winrt::Windows::Foundation::Size availableSpace) const;
@ -85,6 +85,8 @@ namespace winrt::TerminalApp::implementation
void EnterZoom();
void ExitZoom();
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const;
int GetLeafPaneCount() const noexcept;
void TogglePaneReadOnly();

View file

@ -2,5 +2,5 @@
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
<package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
</packages>

View file

@ -16,18 +16,23 @@
<ResourceDictionary>
<Style x:Key="ToggleButtonStyle"
TargetType="ToggleButton">
<Setter Property="Width" Value="25" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="1" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="2,0" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
</Style>
<Style x:Key="ButtonStyle"
TargetType="Button">
<Setter Property="Width" Value="25" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="6,0,0,0" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
</Style>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
@ -227,18 +232,20 @@
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Padding="5"
CornerRadius="0,0,2,2"
<StackPanel Margin="8"
Padding="8"
CornerRadius="{ThemeResource OverlayCornerRadius}"
Orientation="Horizontal"
Style="{ThemeResource SearchBoxBackground}">
<TextBox x:Name="TextBox"
x:Uid="SearchBox_TextBox"
Width="160"
Margin="5"
Margin="0,0,6,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
CornerRadius="2"
FontSize="15"
IsSpellCheckEnabled="False"
KeyDown="TextBoxKeyDown"
PlaceholderForeground="{ThemeResource TextBoxPlaceholderTextThemeBrush}" />
@ -269,7 +276,6 @@
<Button x:Name="CloseButton"
x:Uid="SearchBox_Close"
Padding="0"
Click="CloseClick"
Style="{ThemeResource ButtonStyle}">
<FontIcon FontFamily="Segoe MDL2 Assets"

View file

@ -904,24 +904,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
if (vkey == VK_SPACE && modifiers.IsAltPressed())
{
if (const auto bindings = _settings.KeyBindings())
{
if (!bindings.IsKeyChordExplicitlyUnbound({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), modifiers.IsWinPressed(), vkey, scanCode }))
{
// If we get here, it means that
// 1. we do not have a command bound to alt+space
// 2. alt+space was not explicitly unbound
// That means that XAML handled the alt+space to open up the context menu, and
// so we don't want to send anything to the terminal
// TODO GH#11018: Add a new "openSystemMenu" keybinding
e.Handled(true);
return;
}
}
}
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
{
e.Handled(true);

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -206,6 +206,7 @@
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox IsEnabled="{x:Bind local:Converters.StringsAreNotEqual('desktopWallpaper', Appearance.BackgroundImagePath), Mode=OneWay}"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind local:Converters.StringFallBackToEmptyString('desktopWallpaper', Appearance.BackgroundImagePath), Mode=TwoWay, BindBack=Appearance.SetBackgroundImagePath}" />
<Button x:Uid="Profile_BackgroundImageBrowse"

View file

@ -199,8 +199,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
bool GlobalAppearance::FeatureTrayIconEnabled() const noexcept
bool GlobalAppearance::FeatureNotificationIconEnabled() const noexcept
{
return Feature_TrayIcon::IsEnabled();
return Feature_NotificationIcon::IsEnabled();
}
}

View file

@ -25,7 +25,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
bool FeatureTrayIconEnabled() const noexcept;
bool FeatureNotificationIconEnabled() const noexcept;
WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals, Theme);

View file

@ -26,6 +26,6 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentTabWidthMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
Boolean FeatureTrayIconEnabled { get; };
Boolean FeatureNotificationIconEnabled { get; };
}
}

View file

@ -85,16 +85,16 @@
<ToggleSwitch IsOn="{x:Bind local:Converters.InvertBoolean(State.Globals.DisableAnimations), BindBack=State.Globals.SetInvertedDisableAnimationsValue, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Always Show Tray Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowTrayIcon"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowTrayIcon, Mode=TwoWay}" />
<!-- Always Show Notification Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowNotificationIcon"
Visibility="{x:Bind FeatureNotificationIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowNotificationIcon, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Minimize To Tray -->
<local:SettingContainer x:Uid="Globals_MinimizeToTray"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToTray, Mode=TwoWay}" />
<!-- Minimize To Notification Area -->
<local:SettingContainer x:Uid="Globals_MinimizeToNotificationArea"
Visibility="{x:Bind FeatureNotificationIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToNotificationArea, Mode=TwoWay}" />
</local:SettingContainer>
</StackPanel>
</ScrollViewer>

View file

@ -46,7 +46,8 @@
<!-- Word Delimiters -->
<local:SettingContainer x:Uid="Globals_WordDelimiters">
<TextBox Style="{StaticResource TextBoxSettingStyle}"
<TextBox IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Globals.WordDelimiters, Mode=TwoWay}" />
</local:SettingContainer>

View file

@ -17,6 +17,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
InitializeComponent();
INITIALIZE_BINDABLE_ENUM_SETTING(FirstWindowPreference, FirstWindowPreference, FirstWindowPreference, L"Globals_FirstWindowPreference", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(LaunchMode, LaunchMode, LaunchMode, L"Globals_LaunchMode", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(WindowingBehavior, WindowingMode, WindowingMode, L"Globals_WindowingBehavior", L"Content");
@ -68,4 +69,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
return winrt::single_threaded_observable_vector(std::move(profiles));
}
bool Launch::ShowFirstWindowPreference() const noexcept
{
return Feature_PersistedWindowLayout::IsEnabled();
}
}

View file

@ -29,8 +29,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void CurrentDefaultProfile(const IInspectable& value);
winrt::Windows::Foundation::Collections::IObservableVector<IInspectable> DefaultProfiles() const;
bool ShowFirstWindowPreference() const noexcept;
WINRT_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(FirstWindowPreference, Model::FirstWindowPreference, State().Settings().GlobalSettings, FirstWindowPreference);
GETSET_BINDABLE_ENUM_SETTING(LaunchMode, Model::LaunchMode, State().Settings().GlobalSettings, LaunchMode);
GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, State().Settings().GlobalSettings, WindowingBehavior);
};

View file

@ -20,6 +20,12 @@ namespace Microsoft.Terminal.Settings.Editor
// https://github.com/microsoft/microsoft-ui-xaml/issues/5395
IObservableVector<IInspectable> DefaultProfiles { get; };
Boolean ShowFirstWindowPreference { get; };
IInspectable CurrentFirstWindowPreference;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> FirstWindowPreferenceList { get; };
IInspectable CurrentLaunchMode;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };

View file

@ -133,6 +133,15 @@
<ToggleSwitch IsOn="{x:Bind State.Settings.GlobalSettings.StartOnUserLogin, Mode=TwoWay}" />
</local:SettingContainer>
<!-- First Window Behavior -->
<local:SettingContainer x:Uid="Globals_FirstWindowPreference"
Visibility="{x:Bind ShowFirstWindowPreference}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind FirstWindowPreferenceList}"
SelectedItem="{x:Bind CurrentFirstWindowPreference, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Launch Mode -->
<local:SettingContainer x:Uid="Globals_LaunchMode">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}"

View file

@ -81,7 +81,8 @@
SettingOverrideSource="{x:Bind State.Profile.CommandlineOverrideSource, Mode=OneWay}"
Visibility="{x:Bind local:Converters.InvertedBooleanToVisibility(State.Profile.IsBaseLayer), Mode=OneWay}">
<StackPanel Orientation="Horizontal">
<TextBox Style="{StaticResource TextBoxSettingStyle}"
<TextBox IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Profile.Commandline, Mode=TwoWay}" />
<Button x:Uid="Profile_CommandlineBrowse"
Click="Commandline_Click"
@ -99,6 +100,7 @@
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox IsEnabled="{x:Bind State.Profile.UseCustomStartingDirectory, Mode=OneWay}"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Profile.StartingDirectory, Mode=TwoWay}" />
<Button x:Name="StartingDirectoryBrowse"
@ -120,6 +122,7 @@
SettingOverrideSource="{x:Bind State.Profile.IconOverrideSource, Mode=OneWay}">
<StackPanel Orientation="Horizontal">
<TextBox FontFamily="Segoe UI, Segoe MDL2 Assets"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind State.Profile.Icon, Mode=TwoWay}" />
<Button x:Uid="Profile_IconBrowse"

View file

@ -291,6 +291,22 @@
<value>The number of rows displayed in the window upon first load. Measured in characters.</value>
<comment>A description for what the "rows" setting does. Presented near "Globals_InitialRows.Header".</comment>
</data>
<data name="Globals_FirstWindowPreference.Header" xml:space="preserve">
<value>When Terminal starts</value>
<comment>Header for a control to select how the terminal should load its first window.</comment>
</data>
<data name="Globals_FirstWindowPreference.HelpText" xml:space="preserve">
<value>What should be shown when the first terminal is created.</value>
<comment></comment>
</data>
<data name="Globals_FirstWindowPreferenceDefaultProfile.Content" xml:space="preserve">
<value>Open a tab with the default profile</value>
<comment>An option to choose from for the "First window preference" setting. Open the default profile.</comment>
</data>
<data name="Globals_FirstWindowPreferencePersistedWindowLayout.Content" xml:space="preserve">
<value>Open tabs from a previous session</value>
<comment>An option to choose from for the "First window preference" setting. Reopen the layout from the last session.</comment>
</data>
<data name="Globals_LaunchMode.Header" xml:space="preserve">
<value>Launch mode</value>
<comment>Header for a control to select what mode to launch the terminal in.</comment>
@ -1090,13 +1106,13 @@
<value>Pane animations</value>
<comment>Header for a control to toggle animations on panes. "Enabled" value enables the animations.</comment>
</data>
<data name="Globals_AlwaysShowTrayIcon.Header" xml:space="preserve">
<data name="Globals_AlwaysShowNotificationIcon.Header" xml:space="preserve">
<value>Always display an icon in the notification area</value>
<comment>Header for a control to toggle whether the tray icon should always be shown.</comment>
<comment>Header for a control to toggle whether the notification icon should always be shown.</comment>
</data>
<data name="Globals_MinimizeToTray.Header" xml:space="preserve">
<data name="Globals_MinimizeToNotificationArea.Header" xml:space="preserve">
<value>Hide Terminal in the notification area when it is minimized</value>
<comment>Header for a control to toggle whether the terminal should hide itself in the tray instead of the taskbar when minimized.</comment>
<comment>Header for a control to toggle whether the terminal should hide itself in the notification area instead of the taskbar when minimized.</comment>
</data>
<data name="SettingContainer_OverrideMessageBaseLayer" xml:space="preserve">
<value>Reset to inherited value.</value>
@ -1218,4 +1234,4 @@
<value>Bold font with bright colors</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>
</root>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -65,8 +65,10 @@ static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
static constexpr std::string_view OpenSystemMenuKey{ "openSystemMenu" };
static constexpr std::string_view ClearBufferKey{ "clearBuffer" };
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
static constexpr std::string_view QuitKey{ "quit" };
static constexpr std::string_view ActionKey{ "action" };
@ -368,8 +370,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
{ ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::Quit, RS_(L"QuitCommandKey") },
};
}();

View file

@ -403,13 +403,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// This text is intentionally _not_ localized, to attempt to mirror the
// exact syntax that the property would have in JSON.
switch (SplitStyle())
switch (SplitDirection())
{
case SplitState::Vertical:
ss << L"split: vertical, ";
case SplitDirection::Up:
ss << L"split: up, ";
break;
case SplitState::Horizontal:
ss << L"split: horizontal, ";
case SplitDirection::Right:
ss << L"split: right, ";
break;
case SplitDirection::Down:
ss << L"split: down, ";
break;
case SplitDirection::Left:
ss << L"split: left, ";
break;
}

View file

@ -126,14 +126,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
bool Equals(const Model::NewTerminalArgs& other)
{
return other.Commandline() == _Commandline &&
other.StartingDirectory() == _StartingDirectory &&
other.TabTitle() == _TabTitle &&
other.TabColor() == _TabColor &&
other.ProfileIndex() == _ProfileIndex &&
other.Profile() == _Profile &&
other.SuppressApplicationTitle() == _SuppressApplicationTitle &&
other.ColorScheme() == _ColorScheme;
auto otherAsUs = other.try_as<NewTerminalArgs>();
if (otherAsUs)
{
return otherAsUs->_Commandline == _Commandline &&
otherAsUs->_StartingDirectory == _StartingDirectory &&
otherAsUs->_TabTitle == _TabTitle &&
otherAsUs->_TabColor == _TabColor &&
otherAsUs->_ProfileIndex == _ProfileIndex &&
otherAsUs->_Profile == _Profile &&
otherAsUs->_SuppressApplicationTitle == _SuppressApplicationTitle &&
otherAsUs->_ColorScheme == _ColorScheme;
}
return false;
};
static Model::NewTerminalArgs FromJson(const Json::Value& json)
{
@ -669,21 +674,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
{
SplitPaneArgs() = default;
SplitPaneArgs(SplitType splitMode, SplitState style, double size, const Model::NewTerminalArgs& terminalArgs) :
SplitPaneArgs(SplitType splitMode, SplitDirection direction, double size, const Model::NewTerminalArgs& terminalArgs) :
_SplitMode{ splitMode },
_SplitStyle{ style },
_SplitDirection{ direction },
_SplitSize{ size },
_TerminalArgs{ terminalArgs } {};
SplitPaneArgs(SplitState style, double size, const Model::NewTerminalArgs& terminalArgs) :
_SplitStyle{ style },
SplitPaneArgs(SplitDirection direction, double size, const Model::NewTerminalArgs& terminalArgs) :
_SplitDirection{ direction },
_SplitSize{ size },
_TerminalArgs{ terminalArgs } {};
SplitPaneArgs(SplitState style, const Model::NewTerminalArgs& terminalArgs) :
_SplitStyle{ style },
SplitPaneArgs(SplitDirection direction, const Model::NewTerminalArgs& terminalArgs) :
_SplitDirection{ direction },
_TerminalArgs{ terminalArgs } {};
SplitPaneArgs(SplitType splitMode) :
_SplitMode{ splitMode } {};
ACTION_ARG(SplitState, SplitStyle, SplitState::Automatic);
ACTION_ARG(Model::SplitDirection, SplitDirection, SplitDirection::Automatic);
WINRT_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr);
ACTION_ARG(SplitType, SplitMode, SplitType::Manual);
ACTION_ARG(double, SplitSize, .5);
@ -700,7 +705,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto otherAsUs = other.try_as<SplitPaneArgs>();
if (otherAsUs)
{
return otherAsUs->_SplitStyle == _SplitStyle &&
return otherAsUs->_SplitDirection == _SplitDirection &&
(otherAsUs->_TerminalArgs ? otherAsUs->_TerminalArgs.Equals(_TerminalArgs) :
otherAsUs->_TerminalArgs == _TerminalArgs) &&
otherAsUs->_SplitSize == _SplitSize &&
@ -713,7 +718,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<SplitPaneArgs>();
args->_TerminalArgs = NewTerminalArgs::FromJson(json);
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle);
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitDirection);
JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode);
JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize);
if (args->SplitSize() >= 1 || args->SplitSize() <= 0)
@ -730,7 +735,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
const auto args{ get_self<SplitPaneArgs>(val) };
auto json{ NewTerminalArgs::ToJson(args->_TerminalArgs) };
JsonUtils::SetValueForKey(json, SplitKey, args->_SplitStyle);
JsonUtils::SetValueForKey(json, SplitKey, args->_SplitDirection);
JsonUtils::SetValueForKey(json, SplitModeKey, args->_SplitMode);
JsonUtils::SetValueForKey(json, SplitSizeKey, args->_SplitSize);
return json;
@ -738,7 +743,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
IActionArgs Copy() const
{
auto copy{ winrt::make_self<SplitPaneArgs>() };
copy->_SplitStyle = _SplitStyle;
copy->_SplitDirection = _SplitDirection;
copy->_TerminalArgs = _TerminalArgs.Copy();
copy->_SplitMode = _SplitMode;
copy->_SplitSize = _SplitSize;
@ -746,7 +751,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SplitStyle(), TerminalArgs(), SplitMode(), SplitSize());
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SplitDirection(), TerminalArgs(), SplitMode(), SplitSize());
}
};
@ -1412,7 +1417,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
Json::Value json{ Json::ValueType::objectValue };
const auto args{ get_self<FindMatchArgs>(val) };
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
JsonUtils::SetValueForKey(json, DirectionKey, args->_Direction);
return json;
}
IActionArgs Copy() const

View file

@ -43,12 +43,13 @@ namespace Microsoft.Terminal.Settings.Model
Child
};
enum SplitState
enum SplitDirection
{
Automatic = -1,
None = 0,
Vertical = 1,
Horizontal = 2
Automatic = 0,
Up,
Right,
Down,
Left
};
enum SplitType
@ -189,12 +190,12 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass SplitPaneArgs : IActionArgs
{
SplitPaneArgs(SplitType splitMode, SplitState split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitState split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitState split, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitType splitMode, SplitDirection split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitDirection split, Double size, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitDirection split, NewTerminalArgs terminalArgs);
SplitPaneArgs(SplitType splitMode);
SplitState SplitStyle { get; };
SplitDirection SplitDirection { get; };
NewTerminalArgs TerminalArgs { get; };
SplitType SplitMode { get; };
Double SplitSize { get; };

View file

@ -79,8 +79,10 @@
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(OpenSystemMenu) \
ON_ALL_ACTIONS(ClearBuffer) \
ON_ALL_ACTIONS(MultipleActions)
ON_ALL_ACTIONS(MultipleActions) \
ON_ALL_ACTIONS(Quit)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \

View file

@ -5,54 +5,53 @@
#include "ApplicationState.h"
#include "CascadiaSettings.h"
#include "ApplicationState.g.cpp"
#include "WindowLayout.g.cpp"
#include "ActionAndArgs.h"
#include "JsonUtils.h"
#include "FileUtils.h"
constexpr std::wstring_view stateFileName{ L"state.json" };
static constexpr std::wstring_view stateFileName{ L"state.json" };
static constexpr std::string_view TabLayoutKey{ "tabLayout" };
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
static constexpr std::string_view InitialSizeKey{ "initialSize" };
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
// This trait exists in order to serialize the std::unordered_set for GeneratedProfiles.
template<typename T>
struct ConversionTrait<std::unordered_set<T>>
using namespace winrt::Microsoft::Terminal::Settings::Model;
template<>
struct ConversionTrait<WindowLayout>
{
std::unordered_set<T> FromJson(const Json::Value& json) const
WindowLayout FromJson(const Json::Value& json)
{
ConversionTrait<T> trait;
std::unordered_set<T> val;
val.reserve(json.size());
auto layout = winrt::make_self<implementation::WindowLayout>();
for (const auto& element : json)
{
val.emplace(trait.FromJson(element));
}
GetValueForKey(json, TabLayoutKey, layout->_TabLayout);
GetValueForKey(json, InitialPositionKey, layout->_InitialPosition);
GetValueForKey(json, InitialSizeKey, layout->_InitialSize);
return val;
return *layout;
}
bool CanConvert(const Json::Value& json) const
bool CanConvert(const Json::Value& json)
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
return json.isObject();
}
Json::Value ToJson(const std::unordered_set<T>& val)
Json::Value ToJson(const WindowLayout& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
Json::Value json{ Json::objectValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
SetValueForKey(json, TabLayoutKey, val.TabLayout());
SetValueForKey(json, InitialPositionKey, val.InitialPosition());
SetValueForKey(json, InitialSizeKey, val.InitialSize());
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
return "WindowLayout";
}
};
}

View file

@ -13,20 +13,33 @@ Abstract:
#pragma once
#include "ApplicationState.g.h"
#include "WindowLayout.g.h"
#include <inc/cppwinrt_utils.h>
#include <til/mutex.h>
#include <til/throttled_func.h>
#include <JsonUtils.h>
// This macro generates all getters and setters for ApplicationState.
// It provides X with the following arguments:
// (type, function name, JSON key, ...variadic construction arguments)
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands")
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
X(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages")
struct WindowLayout : WindowLayoutT<WindowLayout>
{
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Model::ActionAndArgs>, TabLayout, nullptr);
WINRT_PROPERTY(winrt::Windows::Foundation::IReference<Model::LaunchPosition>, InitialPosition, nullptr);
WINRT_PROPERTY(winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::Size>, InitialSize, nullptr);
friend ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<Model::WindowLayout>;
};
struct ApplicationState : ApplicationStateT<ApplicationState>
{
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
@ -66,5 +79,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(WindowLayout)
BASIC_FACTORY(ApplicationState);
}

View file

@ -1,8 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "Command.idl";
import "GlobalAppSettings.idl";
namespace Microsoft.Terminal.Settings.Model
{
enum InfoBarMessage
{
CloseOnExitInfo = 0,
KeyboardServiceWarning
};
runtimeclass WindowLayout
{
WindowLayout();
Windows.Foundation.Collections.IVector<ActionAndArgs> TabLayout;
Windows.Foundation.IReference<LaunchPosition> InitialPosition;
Windows.Foundation.IReference<Windows.Foundation.Size> InitialSize;
};
[default_interface] runtimeclass ApplicationState {
static ApplicationState SharedInstance();
@ -10,6 +27,10 @@ namespace Microsoft.Terminal.Settings.Model
String FilePath { get; };
Windows.Foundation.Collections.IVector<WindowLayout> PersistedWindowLayouts { get; set; };
Windows.Foundation.Collections.IVector<String> RecentCommands { get; set; };
Windows.Foundation.Collections.IVector<InfoBarMessage> DismissedMessages { get; set; };
}
}

View file

@ -4,6 +4,7 @@
namespace Microsoft.Terminal.Settings.Model
{
[default_interface] runtimeclass ColorScheme : Windows.Foundation.IStringable {
ColorScheme();
ColorScheme(String name);
String Name;

View file

@ -32,6 +32,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Global Settings
DEFINE_ENUM_MAP(winrt::Windows::UI::Xaml::ElementTheme, ElementTheme);
DEFINE_ENUM_MAP(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabViewWidthMode);
DEFINE_ENUM_MAP(Model::FirstWindowPreference, FirstWindowPreference);
DEFINE_ENUM_MAP(Model::LaunchMode, LaunchMode);
DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::CopyFormat, CopyFormat);

View file

@ -28,6 +28,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Global Settings
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Windows::UI::Xaml::ElementTheme> ElementTheme();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode> TabViewWidthMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, FirstWindowPreference> FirstWindowPreference();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, LaunchMode> LaunchMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, TabSwitcherMode> TabSwitcherMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::CopyFormat> CopyFormat();

View file

@ -10,6 +10,7 @@ namespace Microsoft.Terminal.Settings.Model
// Global Settings
static Windows.Foundation.Collections.IMap<String, Windows.UI.Xaml.ElementTheme> ElementTheme { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.UI.Xaml.Controls.TabViewWidthMode> TabViewWidthMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.FirstWindowPreference> FirstWindowPreference { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.LaunchMode> LaunchMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.TabSwitcherMode> TabSwitcherMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.CopyFormat> CopyFormat { get; };

View file

@ -41,6 +41,7 @@ static constexpr std::string_view LaunchModeKey{ "launchMode" };
static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" };
static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
static constexpr std::string_view EnableStartupTaskKey{ "startOnUserLogin" };
static constexpr std::string_view FirstWindowPreferenceKey{ "firstWindowPreference" };
static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" };
static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" };
static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" };
@ -49,8 +50,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 AlwaysShowNotificationIconKey{ "alwaysShowNotificationIcon" };
static constexpr std::string_view MinimizeToNotificationAreaKey{ "minimizeToNotificationArea" };
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
@ -125,6 +126,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_ForceVTInput = _ForceVTInput;
globals->_DebugFeaturesEnabled = _DebugFeaturesEnabled;
globals->_StartOnUserLogin = _StartOnUserLogin;
globals->_FirstWindowPreference = _FirstWindowPreference;
globals->_AlwaysOnTop = _AlwaysOnTop;
globals->_TabSwitcherMode = _TabSwitcherMode;
globals->_DisableAnimations = _DisableAnimations;
@ -133,8 +135,8 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_WindowingBehavior = _WindowingBehavior;
globals->_TrimBlockSelection = _TrimBlockSelection;
globals->_DetectURLs = _DetectURLs;
globals->_MinimizeToTray = _MinimizeToTray;
globals->_AlwaysShowTrayIcon = _AlwaysShowTrayIcon;
globals->_MinimizeToNotificationArea = _MinimizeToNotificationArea;
globals->_AlwaysShowNotificationIcon = _AlwaysShowNotificationIcon;
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_validDefaultProfile = _validDefaultProfile;
@ -285,6 +287,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::GetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::GetValueForKey(json, LanguageKey, _Language);
@ -327,9 +331,9 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, DetectURLsKey, _DetectURLs);
JsonUtils::GetValueForKey(json, MinimizeToTrayKey, _MinimizeToTray);
JsonUtils::GetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea);
JsonUtils::GetValueForKey(json, AlwaysShowTrayIconKey, _AlwaysShowTrayIcon);
JsonUtils::GetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
// 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
@ -408,6 +412,7 @@ Json::Value GlobalAppSettings::ToJson() const
JsonUtils::SetValueForKey(json, CopyFormattingKey, _CopyFormatting);
JsonUtils::SetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
JsonUtils::SetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::SetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::SetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::SetValueForKey(json, LanguageKey, _Language);
JsonUtils::SetValueForKey(json, ThemeKey, _Theme);
@ -427,8 +432,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);
JsonUtils::SetValueForKey(json, MinimizeToNotificationAreaKey, _MinimizeToNotificationArea);
JsonUtils::SetValueForKey(json, AlwaysShowNotificationIconKey, _AlwaysShowNotificationIcon);
// clang-format on
json[JsonKey(ActionsKey)] = _actionMap->ToJson();

View file

@ -84,6 +84,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, WarnAboutMultiLinePaste, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchPosition, InitialPosition, nullptr, nullptr);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, CenterOnLaunch, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::FirstWindowPreference, FirstWindowPreference, FirstWindowPreference::DefaultProfile);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchMode, LaunchMode, LaunchMode::DefaultMode);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, SnapToGridOnResize, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ForceFullRepaintRendering, false);
@ -99,8 +100,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::WindowingMode, WindowingBehavior, Model::WindowingMode::UseNew);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, TrimBlockSelection, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, DetectURLs, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, MinimizeToTray, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowTrayIcon, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, MinimizeToNotificationArea, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowNotificationIcon, false);
private:
guid _defaultProfile;

View file

@ -34,6 +34,12 @@ namespace Microsoft.Terminal.Settings.Model
UseExisting,
};
enum FirstWindowPreference
{
DefaultProfile,
PersistedWindowLayout,
};
[default_interface] runtimeclass GlobalAppSettings {
Guid DefaultProfile;
@ -59,6 +65,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, WarnAboutMultiLinePaste);
INHERITABLE_SETTING(LaunchPosition, InitialPosition);
INHERITABLE_SETTING(Boolean, CenterOnLaunch);
INHERITABLE_SETTING(FirstWindowPreference, FirstWindowPreference);
INHERITABLE_SETTING(LaunchMode, LaunchMode);
INHERITABLE_SETTING(Boolean, SnapToGridOnResize);
INHERITABLE_SETTING(Boolean, ForceFullRepaintRendering);
@ -74,8 +81,8 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(WindowingMode, WindowingBehavior);
INHERITABLE_SETTING(Boolean, TrimBlockSelection);
INHERITABLE_SETTING(Boolean, DetectURLs);
INHERITABLE_SETTING(Boolean, MinimizeToTray);
INHERITABLE_SETTING(Boolean, AlwaysShowTrayIcon);
INHERITABLE_SETTING(Boolean, MinimizeToNotificationArea);
INHERITABLE_SETTING(Boolean, AlwaysShowNotificationIcon);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);

View file

@ -117,6 +117,127 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
std::string expectedType;
};
// Method Description:
// - Helper that will populate a reference with a value converted from a json object.
// Arguments:
// - json: the json object to convert
// - target: the value to populate with the converted result
// Return Value:
// - a boolean indicating whether the value existed (in this case, was non-null)
//
// GetValue, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
{
if (!conv.CanConvert(json))
{
DeserializationError e{ json };
e.expectedType = conv.TypeDescription();
throw e;
}
target = conv.FromJson(json);
return true;
}
// GetValue, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
{
std::decay_t<T> local{};
GetValue(json, local, std::forward<Converter>(conv));
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
{
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
{
try
{
return GetValue(*found, target, std::forward<Converter>(conv));
}
catch (DeserializationError& e)
{
e.SetKey(key);
throw; // rethrow now that it has a key
}
}
return false;
}
// GetValueForKey, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
{
std::decay_t<T> local{};
GetValueForKey(json, key, local, std::forward<Converter>(conv));
return local; // returns zero-initialized?
}
// GetValue, type-deduced, with automatic converter
template<typename T>
bool GetValue(const Json::Value& json, T& target)
{
return GetValue(json, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValue, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValue(const Json::Value& json)
{
std::decay_t<T> local{};
GetValue(json, local, ConversionTrait<typename std::decay<T>::type>{});
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, with automatic converter
template<typename T>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
{
return GetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValueForKey, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
{
return GetValueForKey<T>(json, key, ConversionTrait<typename std::decay<T>::type>{});
}
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
// Uses the default converter for each v.
// Careful: this can cause a template explosion.
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
template<typename T, typename... Args>
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
{
GetValueForKey(json, key1, val1);
GetValuesForKeys(json, std::forward<Args>(args)...);
}
// SetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target, Converter&& conv)
{
// We don't want to write any empty optionals into JSON (right now).
if (OptionOracle<T>::HasValue(target))
{
// demand guarantees that it will return a value or throw an exception
*json.demand(&*key.cbegin(), (&*key.cbegin()) + key.size()) = conv.ToJson(target);
}
}
// SetValueForKey, type-deduced, with automatic converter
template<typename T>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target)
{
SetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
template<typename T>
struct ConversionTrait
{
@ -219,6 +340,49 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
};
template<typename T>
struct ConversionTrait<std::unordered_set<T>>
{
std::unordered_set<T> FromJson(const Json::Value& json) const
{
ConversionTrait<T> trait;
std::unordered_set<T> val;
val.reserve(json.size());
for (const auto& element : json)
{
val.emplace(trait.FromJson(element));
}
return val;
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) mutable -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const std::unordered_set<T>& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<std::unordered_map<std::string, T>>
{
std::unordered_map<std::string, T> FromJson(const Json::Value& json) const
@ -595,6 +759,46 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};
#ifdef WINRT_Windows_Foundation_H
template<>
struct ConversionTrait<winrt::Windows::Foundation::Size>
{
winrt::Windows::Foundation::Size FromJson(const Json::Value& json)
{
winrt::Windows::Foundation::Size size{};
GetValueForKey(json, std::string_view("width"), size.Width);
GetValueForKey(json, std::string_view("height"), size.Height);
return size;
}
bool CanConvert(const Json::Value& json)
{
if (!json.isObject())
{
return false;
}
return json.isMember("width") && json.isMember("height");
}
Json::Value ToJson(const winrt::Windows::Foundation::Size& val)
{
Json::Value json{ Json::objectValue };
SetValueForKey(json, std::string_view("width"), val.Width);
SetValueForKey(json, std::string_view("height"), val.Height);
return json;
}
std::string TypeDescription() const
{
return "size { width, height }";
}
};
#endif
#ifdef WINRT_Windows_UI_H
template<>
struct ConversionTrait<winrt::Windows::UI::Color>
@ -852,127 +1056,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return "any";
}
};
// Method Description:
// - Helper that will populate a reference with a value converted from a json object.
// Arguments:
// - json: the json object to convert
// - target: the value to populate with the converted result
// Return Value:
// - a boolean indicating whether the value existed (in this case, was non-null)
//
// GetValue, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
{
if (!conv.CanConvert(json))
{
DeserializationError e{ json };
e.expectedType = conv.TypeDescription();
throw e;
}
target = conv.FromJson(json);
return true;
}
// GetValue, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
{
std::decay_t<T> local{};
GetValue(json, local, std::forward<Converter>(conv));
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
{
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
{
try
{
return GetValue(*found, target, std::forward<Converter>(conv));
}
catch (DeserializationError& e)
{
e.SetKey(key);
throw; // rethrow now that it has a key
}
}
return false;
}
// GetValueForKey, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
{
std::decay_t<T> local{};
GetValueForKey(json, key, local, std::forward<Converter>(conv));
return local; // returns zero-initialized?
}
// GetValue, type-deduced, with automatic converter
template<typename T>
bool GetValue(const Json::Value& json, T& target)
{
return GetValue(json, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValue, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValue(const Json::Value& json)
{
std::decay_t<T> local{};
GetValue(json, local, ConversionTrait<typename std::decay<T>::type>{});
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, with automatic converter
template<typename T>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
{
return GetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValueForKey, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
{
return GetValueForKey<T>(json, key, ConversionTrait<typename std::decay<T>::type>{});
}
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
// Uses the default converter for each v.
// Careful: this can cause a template explosion.
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
template<typename T, typename... Args>
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
{
GetValueForKey(json, key1, val1);
GetValuesForKeys(json, std::forward<Args>(args)...);
}
// SetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target, Converter&& conv)
{
// We don't want to write any empty optionals into JSON (right now).
if (OptionOracle<T>::HasValue(target))
{
// demand guarantees that it will return a value or throw an exception
*json.demand(&*key.cbegin(), (&*key.cbegin()) + key.size()) = conv.ToJson(target);
}
}
// SetValueForKey, type-deduced, with automatic converter
template<typename T>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target)
{
SetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
};
#define JSON_ENUM_MAPPER(...) \

View file

@ -178,6 +178,9 @@
<data name="CloseWindowCommandKey" xml:space="preserve">
<value>Close window</value>
</data>
<data name="OpenSystemMenuCommandKey" xml:space="preserve">
<value>Open system menu</value>
</data>
<data name="CommandPromptDisplayName" xml:space="preserve">
<value>Command Prompt</value>
<comment>This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt"</comment>
@ -472,7 +475,7 @@
<value>Windows Console Host</value>
<comment>Name describing the usage of the classic windows console as the terminal UI. (`conhost.exe`)</comment>
</data>
<data name="MinimizeToTrayCommandKey" xml:space="preserve">
<value>Minimize current window to tray</value>
<data name="QuitCommandKey" xml:space="preserve">
<value>Quit the Terminal</value>
</data>
</root>

View file

@ -340,6 +340,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// settings.
if (scheme == nullptr)
{
ClearAppliedColorScheme();
ClearDefaultForeground();
ClearDefaultBackground();
ClearSelectionBackground();
@ -348,6 +349,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
else
{
AppliedColorScheme(scheme);
_DefaultForeground = til::color{ scheme.Foreground() };
_DefaultBackground = til::color{ scheme.Background() };
_SelectionBackground = til::color{ scheme.SelectionBackground() };

View file

@ -127,6 +127,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes);
INHERITABLE_SETTING(Model::TerminalSettings, IFontFeatureMap, FontFeatures);
INHERITABLE_SETTING(Model::TerminalSettings, Model::ColorScheme, AppliedColorScheme);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage);
INHERITABLE_SETTING(Model::TerminalSettings, double, BackgroundImageOpacity, 1.0);

View file

@ -33,5 +33,7 @@ namespace Microsoft.Terminal.Settings.Model
void SetParent(TerminalSettings parent);
TerminalSettings GetParent();
void ApplyColorScheme(ColorScheme scheme);
ColorScheme AppliedColorScheme;
};
}

View file

@ -209,6 +209,14 @@ JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::ElementTheme)
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FirstWindowPreference)
{
JSON_MAPPINGS(2) = {
pair_type{ "defaultProfile", ValueType::DefaultProfile },
pair_type{ "persistedWindowLayout", ValueType::PersistedWindowLayout },
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::LaunchMode)
{
JSON_MAPPINGS(5) = {
@ -363,19 +371,24 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::ResizeDirection)
};
// Possible SplitState values
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitState)
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitDirection)
{
JSON_MAPPINGS(3) = {
pair_type{ "vertical", ValueType::Vertical },
pair_type{ "horizontal", ValueType::Horizontal },
JSON_MAPPINGS(7) = {
pair_type{ "auto", ValueType::Automatic },
pair_type{ "up", ValueType::Up },
pair_type{ "right", ValueType::Right },
pair_type{ "down", ValueType::Down },
pair_type{ "left", ValueType::Left },
pair_type{ "vertical", ValueType::Right },
pair_type{ "horizontal", ValueType::Down },
};
};
// Possible SplitType values
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitType)
{
JSON_MAPPINGS(1) = {
JSON_MAPPINGS(2) = {
pair_type{ "manual", ValueType::Manual },
pair_type{ "duplicate", ValueType::Duplicate },
};
};
@ -493,3 +506,11 @@ JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::IntenseStyle)
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage)
{
JSON_MAPPINGS(2) = {
pair_type{ "closeOnExitInfo", ValueType::CloseOnExitInfo },
pair_type{ "keyboardServiceWarning", ValueType::KeyboardServiceWarning },
};
};

View file

@ -29,8 +29,8 @@
"disableAnimations": false,
"startupActions": "",
"focusFollowMouse": false,
"minimizeToTray": false,
"alwaysShowTrayIcon": false,
"minimizeToNotificationArea": false,
"alwaysShowNotificationIcon": false,
"profiles":
[
@ -301,6 +301,8 @@
{ "command": "identifyWindow" },
{ "command": "openWindowRenamer" },
{ "command": "quakeMode", "keys":"win+sc(41)" },
{ "command": "openSystemMenu", "keys": "alt+space" },
{ "command": "quit" },
// Tab Management
// "command": "closeTab" is unbound by default.
@ -335,8 +337,10 @@
// Pane Management
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": { "action": "splitPane", "split": "horizontal" }, "keys": "alt+shift+-" },
{ "command": { "action": "splitPane", "split": "vertical" }, "keys": "alt+shift+plus" },
{ "command": { "action": "splitPane", "split": "up" } },
{ "command": { "action": "splitPane", "split": "down" }, "keys": "alt+shift+-" },
{ "command": { "action": "splitPane", "split": "left" } },
{ "command": { "action": "splitPane", "split": "right" }, "keys": "alt+shift+plus" },
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
@ -431,10 +435,16 @@
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "auto" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" }
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "up" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" }
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "left" }
},
{
"command": { "action": "splitPane", "profile": "${profile.name}", "split": "right" }
}
]
}

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -73,16 +73,20 @@ namespace RemotingUnitTests
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; }
void Summon(const Remoting::SummonWindowBehavior& /*args*/) { throw winrt::hresult_error{}; };
void RequestShowTrayIcon() { throw winrt::hresult_error{}; };
void RequestHideTrayIcon() { throw winrt::hresult_error{}; };
void RequestShowNotificationIcon() { throw winrt::hresult_error{}; };
void RequestHideNotificationIcon() { throw winrt::hresult_error{}; };
void RequestQuitAll() { throw winrt::hresult_error{}; };
void Quit() { throw winrt::hresult_error{}; };
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs);
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
};
class RemotingTests
@ -136,11 +140,16 @@ namespace RemotingUnitTests
TEST_METHOD(TestSummonMostRecentIsQuake);
TEST_METHOD(TestSummonAfterWindowClose);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
}
static void _closePeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
static void _killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
@ -160,6 +169,19 @@ namespace RemotingUnitTests
}
};
// Helper to tell the monarch that a peasant is closing, this emulates when
// a peasant is closed normally instead of when it crashes.
void RemotingTests::_closePeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID)
{
if (peasantID <= 0)
{
return;
}
m->SignalClose(peasantID);
}
// Helper to replace the specified peasant in a monarch with a
// "DeadPeasant", which will emulate what happens when the peasant process
// dies.
@ -2374,4 +2396,127 @@ namespace RemotingUnitTests
}
}
void RemotingTests::TestSummonAfterWindowClose()
{
Log::Comment(L"Test that we can summon a window on the current desktop,"
L" when the MRU window on that desktop closes normally.");
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
constexpr auto monarch0PID = 12345u;
constexpr auto peasant1PID = 23456u;
constexpr auto peasant2PID = 34567u;
constexpr auto peasant3PID = 45678u;
auto m0 = make_private<Remoting::implementation::Monarch>(monarch0PID);
auto p1 = make_private<Remoting::implementation::Peasant>(peasant1PID);
auto p2 = make_private<Remoting::implementation::Peasant>(peasant2PID);
auto p3 = make_private<Remoting::implementation::Peasant>(peasant3PID);
p1->WindowName(L"one");
p2->WindowName(L"two");
p3->WindowName(L"three");
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
VERIFY_ARE_EQUAL(0, p3->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
m0->AddPeasant(*p3);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(3, p3->GetID());
VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
bool p1ExpectedToBeSummoned = false;
bool p2ExpectedToBeSummoned = false;
bool p3ExpectedToBeSummoned = false;
p1->SummonRequested([&](auto&&, auto&&) {
Log::Comment(L"p1 summoned");
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
});
p2->SummonRequested([&](auto&&, auto&&) {
Log::Comment(L"p2 summoned");
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
});
p3->SummonRequested([&](auto&&, auto&&) {
Log::Comment(L"p3 summoned");
VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
});
{
Log::Comment(L"Activate the first peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
guid1,
winrt::clock().now() };
p1->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the second peasant, second desktop");
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
guid2,
winrt::clock().now() };
p2->ActivateWindow(activatedArgs);
}
{
Log::Comment(L"Activate the third peasant, first desktop");
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
guid1,
winrt::clock().now() };
p3->ActivateWindow(activatedArgs);
}
Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
auto manager = winrt::make_self<MockDesktopManager>();
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
if (hwnd == peasant1PID || hwnd == peasant3PID)
{
*result = true;
}
else if (hwnd == peasant2PID)
{
*result = false;
}
else
{
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
}
return S_OK;
};
manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
Remoting::SummonWindowSelectionArgs args;
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
p3ExpectedToBeSummoned = true;
args.OnCurrentDesktop(true);
m0->SummonWindow(args);
VERIFY_IS_TRUE(args.FoundMatch());
Log::Comment(L"Close window 3. Window 1 is now the MRU on desktop 1.");
RemotingTests::_closePeasant(m0, p3->GetID());
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
p1ExpectedToBeSummoned = true;
p2ExpectedToBeSummoned = false;
p3ExpectedToBeSummoned = false;
args.FoundMatch(false);
args.OnCurrentDesktop(true);
m0->SummonWindow(args);
VERIFY_IS_TRUE(args.FoundMatch());
}
}

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -63,7 +63,7 @@ AppHost::AppHost() noexcept :
// Update our own internal state tracking if we're in quake mode or not.
_IsQuakeWindowChanged(nullptr, nullptr);
_window->SetMinimizeToTrayBehavior(_logic.GetMinimizeToTray());
_window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea());
// Tell the window to callback to us when it's about to handle a WM_CREATE
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
@ -213,7 +213,15 @@ void AppHost::_HandleCommandlineArgs()
peasant.SummonRequested({ this, &AppHost::_HandleSummon });
peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId });
peasant.QuitRequested({ this, &AppHost::_QuitRequested });
// We need this property to be set before we get the InitialSize/Position
// and BecameMonarch which normally sets it is only run after the window
// is created.
if (_windowManager.IsMonarch())
{
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants());
}
_logic.WindowName(peasant.WindowName());
_logic.WindowId(peasant.GetID());
}
@ -271,6 +279,8 @@ void AppHost::Initialize()
_logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged });
_logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged });
_logic.SummonWindowRequested({ this, &AppHost::_SummonWindowRequested });
_logic.OpenSystemMenu({ this, &AppHost::_OpenSystemMenu });
_logic.QuitRequested({ this, &AppHost::_RequestQuitAll });
_window->UpdateTitle(_logic.Title());
@ -325,13 +335,13 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*
// - <none>
void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/)
{
if (_windowManager.IsMonarch() && _trayIcon)
if (_windowManager.IsMonarch() && _notificationIcon)
{
_DestroyTrayIcon();
_DestroyNotificationIcon();
}
else if (_window->IsQuakeWindow())
{
_HideTrayIconRequested();
_HideNotificationIconRequested();
}
_window->Close();
@ -668,14 +678,24 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s
if (_windowManager.DoesQuakeWindowExist() ||
_window->IsQuakeWindow() ||
(_logic.GetAlwaysShowTrayIcon() || _logic.GetMinimizeToTray()))
(_logic.GetAlwaysShowNotificationIcon() || _logic.GetMinimizeToNotificationArea()))
{
_CreateTrayIcon();
_CreateNotificationIcon();
}
// Set the number of open windows (so we know if we are the last window)
// and subscribe for updates if there are any changes to that number.
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants());
_windowManager.WindowCreated([this](auto&&, auto&&) { _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); });
_windowManager.WindowClosed([this](auto&&, auto&&) { _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); });
// These events are coming from peasants that become or un-become quake windows.
_windowManager.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequested(); });
_windowManager.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequested(); });
_windowManager.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequested(); });
_windowManager.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequested(); });
// If the monarch receives a QuitAll event it will signal this event to be
// ran before each peasant is closed.
_windowManager.QuitAllRequested({ this, &AppHost::_QuitAllRequested });
}
void AppHost::_listenForInboundConnections()
@ -962,12 +982,12 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
{
_setupGlobalHotkeys();
// If we're monarch, we need to check some conditions to show the tray icon.
// If there's a Quake window somewhere, we'll want to keep the tray icon.
// There's two settings - MinimizeToTray and AlwaysShowTrayIcon. If either
// one of them are true, we want to make sure there's a tray icon.
// If both are false, we want to remove our icon from the tray.
// When we remove our icon from the tray, we'll also want to re-summon
// If we're monarch, we need to check some conditions to show the notification icon.
// If there's a Quake window somewhere, we'll want to keep the notification icon.
// There's two settings - MinimizeToNotificationArea and AlwaysShowNotificationIcon. If either
// one of them are true, we want to make sure there's a notification icon.
// If both are false, we want to remove our icon from the notification area.
// When we remove our icon from the notification area, we'll also want to re-summon
// any hidden windows, but right now we're not keeping track of who's hidden,
// so just summon them all. Tracking the work to do a "summon all minimized" in
// GH#10448
@ -975,41 +995,63 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
{
if (!_windowManager.DoesQuakeWindowExist())
{
if (!_trayIcon && (_logic.GetMinimizeToTray() || _logic.GetAlwaysShowTrayIcon()))
if (!_notificationIcon && (_logic.GetMinimizeToNotificationArea() || _logic.GetAlwaysShowNotificationIcon()))
{
_CreateTrayIcon();
_CreateNotificationIcon();
}
else if (_trayIcon && !_logic.GetMinimizeToTray() && !_logic.GetAlwaysShowTrayIcon())
else if (_notificationIcon && !_logic.GetMinimizeToNotificationArea() && !_logic.GetAlwaysShowNotificationIcon())
{
_windowManager.SummonAllWindows();
_DestroyTrayIcon();
_DestroyNotificationIcon();
}
}
}
_window->SetMinimizeToTrayBehavior(_logic.GetMinimizeToTray());
_window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea());
}
void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
// We want the quake window to be accessible through the tray icon.
// This means if there's a quake window _somewhere_, we want the tray icon
// to show regardless of the tray icon settings.
// This also means we'll need to destroy the tray icon if it was created
// We want the quake window to be accessible through the notification icon.
// This means if there's a quake window _somewhere_, we want the notification icon
// to show regardless of the notification icon settings.
// This also means we'll need to destroy the notification icon if it was created
// specifically for the quake window. If not, it should not be destroyed.
if (!_window->IsQuakeWindow() && _logic.IsQuakeWindow())
{
_ShowTrayIconRequested();
_ShowNotificationIconRequested();
}
else if (_window->IsQuakeWindow() && !_logic.IsQuakeWindow())
{
_HideTrayIconRequested();
_HideNotificationIconRequested();
}
_window->IsQuakeWindow(_logic.IsQuakeWindow());
}
winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
// Need to be on the main thread to close out all of the tabs.
co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher());
_logic.Quit();
}
void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_windowManager.RequestQuitAll();
}
void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
// TODO: GH#9800: For now, nothing needs to be done before the monarch closes all windows.
// Later when we have state saving that should go here.
}
void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable&)
{
@ -1021,82 +1063,88 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta
_HandleSummon(sender, summonArgs);
}
void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->OpenSystemMenu(std::nullopt, std::nullopt);
}
// Method Description:
// - Creates a Tray Icon and hooks up its handlers
// - Creates a Notification Icon and hooks up its handlers
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::_CreateTrayIcon()
void AppHost::_CreateNotificationIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
_trayIcon = std::make_unique<TrayIcon>(_window->GetHandle());
_notificationIcon = std::make_unique<NotificationIcon>(_window->GetHandle());
// Hookup the handlers, save the tokens for revoking if settings change.
_ReAddTrayIconToken = _window->NotifyReAddTrayIcon([this]() { _trayIcon->ReAddTrayIcon(); });
_TrayIconPressedToken = _window->NotifyTrayIconPressed([this]() { _trayIcon->TrayIconPressed(); });
_ShowTrayContextMenuToken = _window->NotifyShowTrayContextMenu([this](til::point coord) { _trayIcon->ShowTrayContextMenu(coord, _windowManager.GetPeasantInfos()); });
_TrayMenuItemSelectedToken = _window->NotifyTrayMenuItemSelected([this](HMENU hm, UINT idx) { _trayIcon->TrayMenuItemSelected(hm, idx); });
_trayIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); });
_ReAddNotificationIconToken = _window->NotifyReAddNotificationIcon([this]() { _notificationIcon->ReAddNotificationIcon(); });
_NotificationIconPressedToken = _window->NotifyNotificationIconPressed([this]() { _notificationIcon->NotificationIconPressed(); });
_ShowNotificationIconContextMenuToken = _window->NotifyShowNotificationIconContextMenu([this](til::point coord) { _notificationIcon->ShowContextMenu(coord, _windowManager.GetPeasantInfos()); });
_NotificationIconMenuItemSelectedToken = _window->NotifyNotificationIconMenuItemSelected([this](HMENU hm, UINT idx) { _notificationIcon->MenuItemSelected(hm, idx); });
_notificationIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); });
}
}
// Method Description:
// - Deletes our tray icon if we have one.
// - Deletes our notification icon if we have one.
// Arguments:
// - <none>
// Return Value:
// - <none>
void AppHost::_DestroyTrayIcon()
void AppHost::_DestroyNotificationIcon()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
_window->NotifyReAddTrayIcon(_ReAddTrayIconToken);
_window->NotifyTrayIconPressed(_TrayIconPressedToken);
_window->NotifyShowTrayContextMenu(_ShowTrayContextMenuToken);
_window->NotifyTrayMenuItemSelected(_TrayMenuItemSelectedToken);
_window->NotifyReAddNotificationIcon(_ReAddNotificationIconToken);
_window->NotifyNotificationIconPressed(_NotificationIconPressedToken);
_window->NotifyShowNotificationIconContextMenu(_ShowNotificationIconContextMenuToken);
_window->NotifyNotificationIconMenuItemSelected(_NotificationIconMenuItemSelectedToken);
_trayIcon->RemoveIconFromTray();
_trayIcon = nullptr;
_notificationIcon->RemoveIconFromNotificationArea();
_notificationIcon = nullptr;
}
}
void AppHost::_ShowTrayIconRequested()
void AppHost::_ShowNotificationIconRequested()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (_windowManager.IsMonarch())
{
if (!_trayIcon)
if (!_notificationIcon)
{
_CreateTrayIcon();
_CreateNotificationIcon();
}
}
else
{
_windowManager.RequestShowTrayIcon();
_windowManager.RequestShowNotificationIcon();
}
}
}
void AppHost::_HideTrayIconRequested()
void AppHost::_HideNotificationIconRequested()
{
if constexpr (Feature_TrayIcon::IsEnabled())
if constexpr (Feature_NotificationIcon::IsEnabled())
{
if (_windowManager.IsMonarch())
{
// Destroy it only if our settings allow it
if (_trayIcon &&
!_logic.GetAlwaysShowTrayIcon() &&
!_logic.GetMinimizeToTray())
if (_notificationIcon &&
!_logic.GetAlwaysShowNotificationIcon() &&
!_logic.GetMinimizeToNotificationArea())
{
_DestroyTrayIcon();
_DestroyNotificationIcon();
}
}
else
{
_windowManager.RequestHideTrayIcon();
_windowManager.RequestHideNotificationIcon();
}
}
}

View file

@ -3,7 +3,7 @@
#include "pch.h"
#include "NonClientIslandWindow.h"
#include "TrayIcon.h"
#include "NotificationIcon.h"
class AppHost
{
@ -85,13 +85,25 @@ private:
void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _CreateTrayIcon();
void _DestroyTrayIcon();
void _ShowTrayIconRequested();
void _HideTrayIconRequested();
std::unique_ptr<TrayIcon> _trayIcon;
winrt::event_token _ReAddTrayIconToken;
winrt::event_token _TrayIconPressedToken;
winrt::event_token _ShowTrayContextMenuToken;
winrt::event_token _TrayMenuItemSelectedToken;
void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
winrt::fire_and_forget _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _RequestQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _QuitAllRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _CreateNotificationIcon();
void _DestroyNotificationIcon();
void _ShowNotificationIconRequested();
void _HideNotificationIconRequested();
std::unique_ptr<NotificationIcon> _notificationIcon;
winrt::event_token _ReAddNotificationIconToken;
winrt::event_token _NotificationIconPressedToken;
winrt::event_token _ShowNotificationIconContextMenuToken;
winrt::event_token _NotificationIconMenuItemSelectedToken;
};

View file

@ -5,4 +5,4 @@
// Custom window messages
#define CM_UPDATE_TITLE (WM_USER)
#define CM_NOTIFY_FROM_TRAY (WM_USER + 1)
#define CM_NOTIFY_FROM_NOTIFICATION_AREA (WM_USER + 1)

View file

@ -6,7 +6,7 @@
#include "../types/inc/Viewport.hpp"
#include "resource.h"
#include "icon.h"
#include "TrayIcon.h"
#include "NotificationIcon.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
@ -570,20 +570,20 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
}
}
}
case CM_NOTIFY_FROM_TRAY:
case CM_NOTIFY_FROM_NOTIFICATION_AREA:
{
switch (LOWORD(lparam))
{
case NIN_SELECT:
case NIN_KEYSELECT:
{
_NotifyTrayIconPressedHandlers();
_NotifyNotificationIconPressedHandlers();
return 0;
}
case WM_CONTEXTMENU:
{
const til::point eventPoint{ GET_X_LPARAM(wparam), GET_Y_LPARAM(wparam) };
_NotifyShowTrayContextMenuHandlers(eventPoint);
_NotifyShowNotificationIconContextMenuHandlers(eventPoint);
return 0;
}
}
@ -591,17 +591,17 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
}
case WM_MENUCOMMAND:
{
_NotifyTrayMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam);
_NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam);
return 0;
}
default:
// We'll want to receive this message when explorer.exe restarts
// so that we can re-add our icon to the tray.
// so that we can re-add our icon to the notification area.
// This unfortunately isn't a switch case because we register the
// message at runtime.
if (message == WM_TASKBARCREATED)
{
_NotifyReAddTrayIconHandlers();
_NotifyReAddNotificationIconHandlers();
return 0;
}
}
@ -628,7 +628,7 @@ void IslandWindow::OnResize(const UINT width, const UINT height)
void IslandWindow::OnMinimize()
{
// TODO GH#1989 Stop rendering island content when the app is minimized.
if (_minimizeToTray)
if (_minimizeToNotificationArea)
{
HideWindow();
}
@ -1640,9 +1640,67 @@ void IslandWindow::HideWindow()
ShowWindow(GetHandle(), SW_HIDE);
}
void IslandWindow::SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept
void IslandWindow::SetMinimizeToNotificationAreaBehavior(bool MinimizeToNotificationArea) noexcept
{
_minimizeToTray = minimizeToTray;
_minimizeToNotificationArea = MinimizeToNotificationArea;
}
// Method Description:
// - Opens the window's system menu.
// - The system menu is the menu that opens when the user presses Alt+Space or
// right clicks on the title bar.
// - Before updating the menu, we update the buttons like "Maximize" and
// "Restore" so that they are grayed out depending on the window's state.
// Arguments:
// - cursorX: the cursor's X position in screen coordinates
// - cursorY: the cursor's Y position in screen coordinates
void IslandWindow::OpenSystemMenu(const std::optional<int> mouseX, const std::optional<int> mouseY) const noexcept
{
const auto systemMenu = GetSystemMenu(_window.get(), FALSE);
WINDOWPLACEMENT placement;
if (!GetWindowPlacement(_window.get(), &placement))
{
return;
}
const bool isMaximized = placement.showCmd == SW_SHOWMAXIMIZED;
// Update the options based on window state.
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE;
mii.fType = MFT_STRING;
auto setState = [&](UINT item, bool enabled) {
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
SetMenuItemInfo(systemMenu, item, FALSE, &mii);
};
setState(SC_RESTORE, isMaximized);
setState(SC_MOVE, !isMaximized);
setState(SC_SIZE, !isMaximized);
setState(SC_MINIMIZE, true);
setState(SC_MAXIMIZE, !isMaximized);
setState(SC_CLOSE, true);
SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE);
int xPos;
int yPos;
if (mouseX && mouseY)
{
xPos = mouseX.value();
yPos = mouseY.value();
}
else
{
RECT windowPos;
::GetWindowRect(GetHandle(), &windowPos);
xPos = windowPos.left;
yPos = windowPos.top;
}
const auto ret = TrackPopupMenu(systemMenu, TPM_RETURNCMD, xPos, yPos, 0, _window.get(), nullptr);
if (ret != 0)
{
PostMessage(_window.get(), WM_SYSCOMMAND, ret, 0);
}
}
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);

View file

@ -49,18 +49,20 @@ public:
void HideWindow();
void SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept;
void SetMinimizeToNotificationAreaBehavior(bool MinimizeToNotificationArea) noexcept;
void OpenSystemMenu(const std::optional<int> mouseX, const std::optional<int> mouseY) const noexcept;
DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
WINRT_CALLBACK(WindowActivated, winrt::delegate<void()>);
WINRT_CALLBACK(HotkeyPressed, winrt::delegate<void(long)>);
WINRT_CALLBACK(NotifyTrayIconPressed, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyShowTrayContextMenu, winrt::delegate<void(til::point)>);
WINRT_CALLBACK(NotifyTrayMenuItemSelected, winrt::delegate<void(HMENU, UINT)>);
WINRT_CALLBACK(NotifyReAddTrayIcon, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate<void(til::point)>);
WINRT_CALLBACK(NotifyNotificationIconMenuItemSelected, winrt::delegate<void(HMENU, UINT)>);
WINRT_CALLBACK(NotifyReAddNotificationIcon, winrt::delegate<void()>);
WINRT_CALLBACK(WindowMoved, winrt::delegate<void()>);
@ -125,7 +127,7 @@ protected:
void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);
bool _minimizeToTray{ false };
bool _minimizeToNotificationArea{ false };
private:
// This minimum width allows for width the tabs fit

View file

@ -750,7 +750,7 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
// reason so we have to do it ourselves.
if (wParam == HTCAPTION)
{
_OpenSystemMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
OpenSystemMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
break;
}
@ -947,47 +947,3 @@ bool NonClientIslandWindow::_IsTitlebarVisible() const
{
return !(_fullscreen || _borderless);
}
// Method Description:
// - Opens the window's system menu.
// - The system menu is the menu that opens when the user presses Alt+Space or
// right clicks on the title bar.
// - Before updating the menu, we update the buttons like "Maximize" and
// "Restore" so that they are grayed out depending on the window's state.
// Arguments:
// - cursorX: the cursor's X position in screen coordinates
// - cursorY: the cursor's Y position in screen coordinates
void NonClientIslandWindow::_OpenSystemMenu(const int cursorX, const int cursorY) const noexcept
{
const auto systemMenu = GetSystemMenu(_window.get(), FALSE);
WINDOWPLACEMENT placement;
if (!GetWindowPlacement(_window.get(), &placement))
{
return;
}
const bool isMaximized = placement.showCmd == SW_SHOWMAXIMIZED;
// Update the options based on window state.
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE;
mii.fType = MFT_STRING;
auto setState = [&](UINT item, bool enabled) {
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
SetMenuItemInfo(systemMenu, item, FALSE, &mii);
};
setState(SC_RESTORE, isMaximized);
setState(SC_MOVE, !isMaximized);
setState(SC_SIZE, !isMaximized);
setState(SC_MINIMIZE, true);
setState(SC_MAXIMIZE, !isMaximized);
setState(SC_CLOSE, true);
SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE);
const auto ret = TrackPopupMenu(systemMenu, TPM_RETURNCMD, cursorX, cursorY, 0, _window.get(), nullptr);
if (ret != 0)
{
PostMessage(_window.get(), WM_SYSCOMMAND, ret, 0);
}
}

View file

@ -87,6 +87,4 @@ private:
void _UpdateFrameMargins() const noexcept;
void _UpdateMaximizedState();
void _UpdateIslandPosition(const UINT windowWidth, const UINT windowHeight);
void _OpenSystemMenu(const int mouseX, const int mouseY) const noexcept;
};

View file

@ -3,7 +3,7 @@
#include "pch.h"
#include "icon.h"
#include "TrayIcon.h"
#include "NotificationIcon.h"
#include "CustomWindowMessages.h"
#include <ScopedResourceLoader.h>
@ -12,54 +12,54 @@
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Microsoft::Terminal;
TrayIcon::TrayIcon(const HWND owningHwnd) :
NotificationIcon::NotificationIcon(const HWND owningHwnd) :
_owningHwnd{ owningHwnd }
{
CreateTrayIcon();
CreateNotificationIcon();
}
TrayIcon::~TrayIcon()
NotificationIcon::~NotificationIcon()
{
RemoveIconFromTray();
RemoveIconFromNotificationArea();
}
void TrayIcon::_CreateWindow()
void NotificationIcon::_CreateWindow()
{
WNDCLASSW wc{};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = wil::GetModuleInstanceHandle();
wc.lpszClassName = L"TRAY_ICON_HOSTING_WINDOW_CLASS";
wc.lpszClassName = L"NOTIFICATION_ICON_HOSTING_WINDOW_CLASS";
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProcW;
wc.hIcon = static_cast<HICON>(GetActiveAppIconHandle(true));
RegisterClass(&wc);
_trayIconHwnd = wil::unique_hwnd(CreateWindowW(wc.lpszClassName,
wc.lpszClassName,
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_MESSAGE,
nullptr,
wc.hInstance,
nullptr));
_notificationIconHwnd = wil::unique_hwnd(CreateWindowW(wc.lpszClassName,
wc.lpszClassName,
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_MESSAGE,
nullptr,
wc.hInstance,
nullptr));
WINRT_VERIFY(_trayIconHwnd);
WINRT_VERIFY(_notificationIconHwnd);
}
// Method Description:
// - Creates and adds an icon to the notification tray.
// - Creates and adds an icon to the notification area.
// If an icon already exists, update the HWND associated
// to the icon with this window's HWND.
// Arguments:
// - <unused>
// Return Value:
// - <none>
void TrayIcon::CreateTrayIcon()
void NotificationIcon::CreateNotificationIcon()
{
if (!_trayIconHwnd)
if (!_notificationIconHwnd)
{
// Creating a disabled, non visible window just so we can set it
// as the foreground window when showing the context menu.
@ -71,7 +71,7 @@ void TrayIcon::CreateTrayIcon()
NOTIFYICONDATA nid{};
nid.cbSize = sizeof(NOTIFYICONDATA);
// This HWND will receive the callbacks sent by the tray icon.
// This HWND will receive the callbacks sent by the notification icon.
nid.hWnd = _owningHwnd;
// App-defined identifier of the icon. The HWND and ID are used
@ -80,7 +80,7 @@ void TrayIcon::CreateTrayIcon()
// going to be showing one so the ID doesn't really matter.
nid.uID = 1;
nid.uCallbackMessage = CM_NOTIFY_FROM_TRAY;
nid.uCallbackMessage = CM_NOTIFY_FROM_NOTIFICATION_AREA;
// AppName happens to be in CascadiaPackage's Resources.
ScopedResourceLoader loader{ L"Resources" };
@ -98,7 +98,7 @@ void TrayIcon::CreateTrayIcon()
nid.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_SETVERSION, &nid);
_trayIconData = nid;
_notificationIconData = nid;
}
// Method Description:
@ -109,14 +109,14 @@ void TrayIcon::CreateTrayIcon()
// - peasants: The map of all peasants that should be available in the context menu.
// Return Value:
// - <none>
void TrayIcon::ShowTrayContextMenu(const til::point& coord,
const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
void NotificationIcon::ShowContextMenu(const til::point& coord,
const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
{
if (const auto hMenu = _CreateTrayContextMenu(peasants))
if (const auto hMenu = _CreateContextMenu(peasants))
{
// We'll need to set our window to the foreground before calling
// TrackPopupMenuEx or else the menu won't dismiss when clicking away.
SetForegroundWindow(_trayIconHwnd.get());
SetForegroundWindow(_notificationIconHwnd.get());
// User can select menu items with the left and right buttons.
UINT uFlags = TPM_RIGHTBUTTON;
@ -137,12 +137,12 @@ void TrayIcon::ShowTrayContextMenu(const til::point& coord,
}
// Method Description:
// - This creates the context menu for our tray icon.
// - This creates the context menu for our notification icon.
// Arguments:
// - peasants: A map of all peasants' ID to their window name.
// Return Value:
// - The handle to the newly created context menu.
HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
HMENU NotificationIcon::_CreateContextMenu(const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
{
auto hMenu = CreatePopupMenu();
if (hMenu)
@ -155,7 +155,7 @@ HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Termi
SetMenuInfo(hMenu, &mi);
// Focus Current Terminal Window
AppendMenu(hMenu, MF_STRING, gsl::narrow<UINT_PTR>(TrayMenuItemAction::FocusTerminal), RS_(L"TrayIconFocusTerminal").c_str());
AppendMenu(hMenu, MF_STRING, gsl::narrow<UINT_PTR>(NotificationIconMenuItemAction::FocusTerminal), RS_(L"NotificationIconFocusTerminal").c_str());
AppendMenu(hMenu, MF_SEPARATOR, 0, L"");
// Submenu for Windows
@ -183,10 +183,10 @@ HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Termi
submenuInfo.cbSize = sizeof(MENUINFO);
submenuInfo.fMask = MIM_MENUDATA;
submenuInfo.dwStyle = MNS_NOTIFYBYPOS;
submenuInfo.dwMenuData = (UINT_PTR)TrayMenuItemAction::SummonWindow;
submenuInfo.dwMenuData = (UINT_PTR)NotificationIconMenuItemAction::SummonWindow;
SetMenuInfo(submenu, &submenuInfo);
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)submenu, RS_(L"TrayIconWindowSubmenu").c_str());
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)submenu, RS_(L"NotificationIconWindowSubmenu").c_str());
}
}
return hMenu;
@ -194,13 +194,13 @@ HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Termi
// Method Description:
// - This is the handler for when one of the menu items are selected within
// the tray icon's context menu.
// the notification icon's context menu.
// Arguments:
// - menu: The handle to the menu that holds the menu item that was selected.
// - menuItemIndex: The index of the menu item within the given menu.
// Return Value:
// - <none>
void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
void NotificationIcon::MenuItemSelected(const HMENU menu, const UINT menuItemIndex)
{
// Check the menu's data for a specific action.
MENUINFO mi{};
@ -209,7 +209,7 @@ void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
GetMenuInfo(menu, &mi);
if (mi.dwMenuData)
{
if (gsl::narrow<TrayMenuItemAction>(mi.dwMenuData) == TrayMenuItemAction::SummonWindow)
if (gsl::narrow<NotificationIconMenuItemAction>(mi.dwMenuData) == NotificationIconMenuItemAction::SummonWindow)
{
winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{};
args.WindowID(GetMenuItemID(menu, menuItemIndex));
@ -222,10 +222,10 @@ void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
}
// Now check the menu item itself for an action.
const auto action = gsl::narrow<TrayMenuItemAction>(GetMenuItemID(menu, menuItemIndex));
const auto action = gsl::narrow<NotificationIconMenuItemAction>(GetMenuItemID(menu, menuItemIndex));
switch (action)
{
case TrayMenuItemAction::FocusTerminal:
case NotificationIconMenuItemAction::FocusTerminal:
{
winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{};
args.SummonBehavior().ToggleVisibility(false);
@ -238,12 +238,12 @@ void TrayIcon::TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex)
}
// Method Description:
// - This is the handler for when the tray icon itself is left-clicked.
// - This is the handler for when the notification icon itself is left-clicked.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TrayIcon::TrayIconPressed()
void NotificationIcon::NotificationIconPressed()
{
// No name in the args means summon the mru window.
winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{};
@ -254,24 +254,24 @@ void TrayIcon::TrayIconPressed()
}
// Method Description:
// - Re-add a tray icon using our currently saved tray icon data.
// - Re-add a notification icon using our currently saved notification icon data.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TrayIcon::ReAddTrayIcon()
void NotificationIcon::ReAddNotificationIcon()
{
Shell_NotifyIcon(NIM_ADD, &_trayIconData);
Shell_NotifyIcon(NIM_SETVERSION, &_trayIconData);
Shell_NotifyIcon(NIM_ADD, &_notificationIconData);
Shell_NotifyIcon(NIM_SETVERSION, &_notificationIconData);
}
// Method Description:
// - Deletes our tray icon.
// - Deletes our notification icon.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TrayIcon::RemoveIconFromTray()
void NotificationIcon::RemoveIconFromNotificationArea()
{
Shell_NotifyIcon(NIM_DELETE, &_trayIconData);
Shell_NotifyIcon(NIM_DELETE, &_notificationIconData);
}

View file

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "../cascadia/inc/cppwinrt_utils.h"
// This enumerates all the possible actions
// that our notification icon context menu could do.
enum class NotificationIconMenuItemAction
{
FocusTerminal, // Focus the MRU terminal.
SummonWindow
};
class NotificationIcon
{
public:
NotificationIcon() = delete;
NotificationIcon(const HWND owningHwnd);
~NotificationIcon();
void CreateNotificationIcon();
void RemoveIconFromNotificationArea();
void ReAddNotificationIcon();
void NotificationIconPressed();
void ShowContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
void MenuItemSelected(const HMENU menu, const UINT menuItemIndex);
WINRT_CALLBACK(SummonWindowRequested, winrt::delegate<void(winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs)>);
private:
void _CreateWindow();
HMENU _CreateContextMenu(const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
wil::unique_hwnd _notificationIconHwnd;
HWND _owningHwnd;
NOTIFYICONDATA _notificationIconData;
};

View file

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "../cascadia/inc/cppwinrt_utils.h"
// This enumerates all the possible actions
// that our tray icon context menu could do.
enum class TrayMenuItemAction
{
FocusTerminal, // Focus the MRU terminal.
SummonWindow
};
class TrayIcon
{
public:
TrayIcon() = delete;
TrayIcon(const HWND owningHwnd);
~TrayIcon();
void CreateTrayIcon();
void RemoveIconFromTray();
void ReAddTrayIcon();
void TrayIconPressed();
void ShowTrayContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
void TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex);
WINRT_CALLBACK(SummonWindowRequested, winrt::delegate<void(winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs)>);
private:
void _CreateWindow();
HMENU _CreateTrayContextMenu(const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
wil::unique_hwnd _trayIconHwnd;
HWND _owningHwnd;
NOTIFYICONDATA _trayIconData;
};

View file

@ -47,7 +47,7 @@
<ClInclude Include="CustomWindowMessages.h" />
<ClInclude Include="IslandWindow.h" />
<ClInclude Include="NonClientIslandWindow.h" />
<ClInclude Include="TrayIcon.h" />
<ClInclude Include="NotificationIcon.h" />
<ClInclude Include="VirtualDesktopUtils.h" />
</ItemGroup>
<ItemGroup>
@ -58,7 +58,7 @@
<ClCompile Include="AppHost.cpp" />
<ClCompile Include="IslandWindow.cpp" />
<ClCompile Include="NonClientIslandWindow.cpp" />
<ClCompile Include="TrayIcon.cpp" />
<ClCompile Include="NotificationIcon.cpp" />
<ClCompile Include="VirtualDesktopUtils.cpp" />
<ClCompile Include="icon.cpp" />
</ItemGroup>

View file

@ -25,7 +25,7 @@ TRACELOGGING_DEFINE_PROVIDER(
// !! BODGY !!
// Manually use the resources from TerminalApp as our resources.
// The WindowsTerminal project doesn't actually build a Resources.resw file, but
// we still need to be able to localize strings for the tray icon menu. Anything
// we still need to be able to localize strings for the notification icon menu. Anything
// you want localized for WindowsTerminal.exe should be stuck in
// ...\TerminalApp\Resources\en-US\Resources.resw
#include <LibraryResources.h>
@ -184,16 +184,13 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
}
}
// GH#7125 = System XAML will show a system dialog on Alt Space. We might want to
// explicitly prevent that - for example when an action is bound to it. So similar to
// above, we steal the event and hand it off to the host. When the host does not process
// it, we will still dispatch like normal.
// GH#7125 = System XAML will show a system dialog on Alt Space. We want to
// explicitly prevent that because we handle that ourselves. So similar to
// above, we steal the event and hand it off to the host.
if (_messageIsAltSpaceKeypress(message))
{
if (host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true))
{
continue;
}
host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true);
continue;
}
TranslateMessage(&message);

View file

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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -3,13 +3,13 @@
<Import Project="$(MSBuildThisFileDirectory)common.build.post.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View file

@ -8,7 +8,7 @@
<Import Project="..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<CppWinRTHeapEnforcement>AnyValueHereWillDisableTheOptOut</CppWinRTHeapEnforcement>

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