Merge remote-tracking branch 'origin/main' into dev/migrie/f/just-elevated-state-2
This commit is contained in:
commit
faa06f807d
5
.github/actions/spelling/expect/expect.txt
vendored
5
.github/actions/spelling/expect/expect.txt
vendored
|
@ -189,13 +189,12 @@ cacafire
|
|||
callee
|
||||
capslock
|
||||
CARETBLINKINGENABLED
|
||||
carlos
|
||||
CARRIAGERETURN
|
||||
cascadia
|
||||
cassert
|
||||
castsi
|
||||
catid
|
||||
carlos
|
||||
zamora
|
||||
cazamor
|
||||
CBash
|
||||
cbegin
|
||||
|
@ -2724,6 +2723,7 @@ wixproj
|
|||
wline
|
||||
wlinestream
|
||||
wmain
|
||||
wmemory
|
||||
WMSZ
|
||||
wnd
|
||||
WNDALLOC
|
||||
|
@ -2850,6 +2850,7 @@ YSize
|
|||
YSubstantial
|
||||
YVIRTUALSCREEN
|
||||
YWalk
|
||||
zamora
|
||||
ZCmd
|
||||
ZCtrl
|
||||
zsh
|
||||
|
|
|
@ -1184,8 +1184,9 @@
|
|||
"pattern": "focusPane"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"default": 0,
|
||||
"description": "The ID of the pane to focus"
|
||||
}
|
||||
}
|
||||
|
@ -1229,7 +1230,7 @@
|
|||
"description": "When provided, summon the window whose name or ID matches the given name value. If no such window exists, then create a new window with that name."
|
||||
},
|
||||
"dropdownDuration": {
|
||||
"type": "number",
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"default": 0,
|
||||
"description": "When provided with a positive number, \"slide\" the window in from the top of the screen using an animation that lasts dropdownDuration milliseconds."
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
Enabled="false"
|
||||
DisplayName="ms-resource:AppName" />
|
||||
</uap5:Extension>
|
||||
<!-- <uap3:Extension Category="windows.appExtension">
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.console.host"
|
||||
Id="OpenConsole"
|
||||
DisplayName="OpenConsole"
|
||||
|
@ -102,15 +102,15 @@
|
|||
<com:Interface Id="E686C757-9A35-4A1C-B3CE-0BCC8B5C69F4" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
|
||||
<com:Interface Id="59D55CCE-FC8A-48B4-ACE8-0A9286C6557F" ProxyStubClsid="3171DE52-6EFA-4AEF-8A9F-D02BD67E7A4F"/>
|
||||
</com:ComInterface>
|
||||
</com:Extension> -->
|
||||
</com:Extension>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<!-- <com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
|
||||
<com:ExeServer DisplayName="OpenConsole" Executable="OpenConsole.exe">
|
||||
<com:Class Id="2EACA947-7F5F-4CFA-BA87-8F7FBEEFBE69"/>
|
||||
</com:ExeServer>
|
||||
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
|
||||
<com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/>
|
||||
</com:ExeServer> -->
|
||||
</com:ExeServer>
|
||||
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
|
||||
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
|
|
|
@ -1092,7 +1092,7 @@ namespace SettingsModelLocalTests
|
|||
},
|
||||
{
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"source": "Terminal.App.UnitTest.1",
|
||||
"historySize": 2222
|
||||
},
|
||||
|
|
|
@ -272,20 +272,30 @@ namespace SettingsModelLocalTests
|
|||
void ProfileTests::DuplicateProfileTest()
|
||||
{
|
||||
static constexpr std::string_view userProfiles{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"backgroundImage": "file:///some/path",
|
||||
"hidden": false,
|
||||
}
|
||||
]
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"font": {
|
||||
"size": 123
|
||||
}
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"backgroundImage": "file:///some/path",
|
||||
"hidden": false,
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userProfiles);
|
||||
const auto profile = settings->AllProfiles().GetAt(0);
|
||||
const auto duplicatedProfile = settings->DuplicateProfile(profile);
|
||||
|
||||
// GH#11392: Ensure duplicated profiles properly inherit the base layer, even for nested objects.
|
||||
VERIFY_ARE_EQUAL(123, duplicatedProfile.FontInfo().FontSize());
|
||||
|
||||
duplicatedProfile.Guid(profile.Guid());
|
||||
duplicatedProfile.Name(profile.Name());
|
||||
|
||||
|
@ -300,6 +310,17 @@ namespace SettingsModelLocalTests
|
|||
// the GUID generated for a dynamic profile (with a source) is different
|
||||
// than that of a profile without a source.
|
||||
|
||||
static constexpr std::string_view inboxSettings{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"source": "Terminal.App.UnitTest.0"
|
||||
},
|
||||
{
|
||||
"name" : "profile1"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
static constexpr std::string_view userSettings{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
|
@ -312,9 +333,9 @@ namespace SettingsModelLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings, DefaultJson);
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings, inboxSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(4u, settings->AllProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
|
||||
VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid());
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace SettingsModelLocalTests
|
|||
// Return Value:
|
||||
// - the JsonObject representing this instance
|
||||
template<typename T>
|
||||
void RoundtripTest(const std::string& jsonString)
|
||||
void RoundtripTest(const std::string_view& jsonString)
|
||||
{
|
||||
const auto json{ VerifyParseSucceeded(jsonString) };
|
||||
const auto settings{ T::FromJson(json) };
|
||||
|
@ -69,7 +69,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::GlobalSettings()
|
||||
{
|
||||
const std::string globalsString{ R"(
|
||||
static constexpr std::string_view globalsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
|
||||
|
@ -105,7 +105,7 @@ namespace SettingsModelLocalTests
|
|||
"actions": []
|
||||
})" };
|
||||
|
||||
const std::string smallGlobalsString{ R"(
|
||||
static constexpr std::string_view smallGlobalsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"actions": []
|
||||
|
@ -117,7 +117,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::Profile()
|
||||
{
|
||||
const std::string profileString{ R"(
|
||||
static constexpr std::string_view profileString{ R"(
|
||||
{
|
||||
"name": "Windows PowerShell",
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
|
@ -152,7 +152,7 @@ namespace SettingsModelLocalTests
|
|||
"selectionBackground": "#CCAABB",
|
||||
|
||||
"useAcrylic": false,
|
||||
"acrylicOpacity": 0.5,
|
||||
"opacity": 50,
|
||||
|
||||
"backgroundImage": "made_you_look.jpeg",
|
||||
"backgroundImageStretchMode": "uniformToFill",
|
||||
|
@ -167,7 +167,7 @@ namespace SettingsModelLocalTests
|
|||
"experimental.retroTerminalEffect": false
|
||||
})" };
|
||||
|
||||
const std::string smallProfileString{ R"(
|
||||
static constexpr std::string_view smallProfileString{ R"(
|
||||
{
|
||||
"name": "Custom Profile"
|
||||
})" };
|
||||
|
@ -175,7 +175,7 @@ namespace SettingsModelLocalTests
|
|||
// Setting "tabColor" to null tests two things:
|
||||
// - null should count as an explicit user-set value, not falling back to the parent's value
|
||||
// - null should be acceptable even though we're working with colors
|
||||
const std::string weirdProfileString{ R"(
|
||||
static constexpr std::string_view weirdProfileString{ R"(
|
||||
{
|
||||
"guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}",
|
||||
"name": "Weird Profile",
|
||||
|
@ -192,7 +192,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::ColorScheme()
|
||||
{
|
||||
const std::string schemeString{ R"({
|
||||
static constexpr std::string_view schemeString{ R"({
|
||||
"name": "Campbell",
|
||||
|
||||
"cursorColor": "#FFFFFF",
|
||||
|
@ -225,56 +225,56 @@ namespace SettingsModelLocalTests
|
|||
void SerializationTests::Actions()
|
||||
{
|
||||
// simple command
|
||||
const std::string actionsString1{ R"([
|
||||
static constexpr std::string_view actionsString1{ R"([
|
||||
{ "command": "paste" }
|
||||
])" };
|
||||
|
||||
// complex command
|
||||
const std::string actionsString2A{ R"([
|
||||
static constexpr std::string_view actionsString2A{ R"([
|
||||
{ "command": { "action": "setTabColor" } }
|
||||
])" };
|
||||
const std::string actionsString2B{ R"([
|
||||
static constexpr std::string_view actionsString2B{ R"([
|
||||
{ "command": { "action": "setTabColor", "color": "#112233" } }
|
||||
])" };
|
||||
const std::string actionsString2C{ R"([
|
||||
static constexpr std::string_view actionsString2C{ R"([
|
||||
{ "command": { "action": "copy" } },
|
||||
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } }
|
||||
])" };
|
||||
|
||||
// simple command with key chords
|
||||
const std::string actionsString3{ R"([
|
||||
static constexpr std::string_view actionsString3{ R"([
|
||||
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+a" },
|
||||
{ "command": "toggleAlwaysOnTop", "keys": "ctrl+b" }
|
||||
])" };
|
||||
|
||||
// complex command with key chords
|
||||
const std::string actionsString4A{ R"([
|
||||
static constexpr std::string_view actionsString4A{ R"([
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" },
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" }
|
||||
])" };
|
||||
const std::string actionsString4B{ R"([
|
||||
static constexpr std::string_view 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"([
|
||||
static constexpr std::string_view actionsString5{ R"([
|
||||
{ "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" },
|
||||
{ "command": "scrollToTop", "keys": "ctrl+f" }
|
||||
])" };
|
||||
|
||||
// complex command with new terminal args
|
||||
const std::string actionsString6{ R"([
|
||||
static constexpr std::string_view actionsString6{ R"([
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" },
|
||||
])" };
|
||||
|
||||
// complex command with meaningful null arg
|
||||
const std::string actionsString7{ R"([
|
||||
static constexpr std::string_view actionsString7{ R"([
|
||||
{ "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" }
|
||||
])" };
|
||||
|
||||
// nested command
|
||||
const std::string actionsString8{ R"([
|
||||
static constexpr std::string_view actionsString8{ R"([
|
||||
{
|
||||
"name": "Change font size...",
|
||||
"commands": [
|
||||
|
@ -286,7 +286,7 @@ namespace SettingsModelLocalTests
|
|||
])" };
|
||||
|
||||
// iterable command
|
||||
const std::string actionsString9A{ R"([
|
||||
static constexpr std::string_view actionsString9A{ R"([
|
||||
{
|
||||
"name": "New tab",
|
||||
"commands": [
|
||||
|
@ -299,7 +299,7 @@ namespace SettingsModelLocalTests
|
|||
]
|
||||
}
|
||||
])" };
|
||||
const std::string actionsString9B{ R"([
|
||||
static constexpr std::string_view actionsString9B{ R"([
|
||||
{
|
||||
"commands":
|
||||
[
|
||||
|
@ -315,7 +315,7 @@ namespace SettingsModelLocalTests
|
|||
"name": "Send Input ..."
|
||||
}
|
||||
])" };
|
||||
const std::string actionsString9C{ R""([
|
||||
static constexpr std::string_view actionsString9C{ R""([
|
||||
{
|
||||
"commands":
|
||||
[
|
||||
|
@ -338,7 +338,7 @@ namespace SettingsModelLocalTests
|
|||
"name": "Send Input (Evil) ..."
|
||||
}
|
||||
])"" };
|
||||
const std::string actionsString9D{ R""([
|
||||
static constexpr std::string_view actionsString9D{ R""([
|
||||
{
|
||||
"command":
|
||||
{
|
||||
|
@ -352,7 +352,7 @@ namespace SettingsModelLocalTests
|
|||
])"" };
|
||||
|
||||
// unbound command
|
||||
const std::string actionsString10{ R"([
|
||||
static constexpr std::string_view actionsString10{ R"([
|
||||
{ "command": "unbound", "keys": "ctrl+c" }
|
||||
])" };
|
||||
|
||||
|
@ -395,7 +395,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::CascadiaSettings()
|
||||
{
|
||||
const std::string settingsString{ R"({
|
||||
static constexpr std::string_view settingsString{ R"({
|
||||
"$help" : "https://aka.ms/terminal-documentation",
|
||||
"$schema" : "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
||||
|
@ -465,7 +465,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::LegacyFontSettings()
|
||||
{
|
||||
const std::string profileString{ R"(
|
||||
static constexpr std::string_view profileString{ R"(
|
||||
{
|
||||
"name": "Profile with legacy font settings",
|
||||
|
||||
|
@ -474,7 +474,7 @@ namespace SettingsModelLocalTests
|
|||
"fontWeight": "normal"
|
||||
})" };
|
||||
|
||||
const std::string expectedOutput{ R"(
|
||||
static constexpr std::string_view expectedOutput{ R"(
|
||||
{
|
||||
"name": "Profile with legacy font settings",
|
||||
|
||||
|
|
|
@ -1031,9 +1031,11 @@ namespace TerminalAppLocalTests
|
|||
// The first action is going to always be a new-tab action
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, appArgs._startupActions.at(0).Action());
|
||||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
const auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NextTab, actionAndArgs.Action());
|
||||
VERIFY_IS_NULL(actionAndArgs.Args());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
const auto myArgs = actionAndArgs.Args().as<NextTabArgs>();
|
||||
VERIFY_ARE_EQUAL(TabSwitcherMode::Disabled, myArgs.SwitcherMode().Value());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
|
@ -1047,7 +1049,9 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::PrevTab, actionAndArgs.Action());
|
||||
VERIFY_IS_NULL(actionAndArgs.Args());
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs.Args());
|
||||
const auto myArgs = actionAndArgs.Args().as<PrevTabArgs>();
|
||||
VERIFY_ARE_EQUAL(TabSwitcherMode::Disabled, myArgs.SwitcherMode().Value());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
|
|
|
@ -16,6 +16,31 @@ using namespace winrt::Microsoft::Terminal::Control;
|
|||
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
static constexpr std::wstring_view inboxSettings{ LR"({
|
||||
"schemes": [{
|
||||
"name": "Campbell",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#0C0C0C",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
"yellow": "#C19C00",
|
||||
"blue": "#0037DA",
|
||||
"purple": "#881798",
|
||||
"cyan": "#3A96DD",
|
||||
"white": "#CCCCCC",
|
||||
"brightBlack": "#767676",
|
||||
"brightRed": "#E74856",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightWhite": "#F2F2F2"
|
||||
}]
|
||||
})" };
|
||||
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
// an updated TAEF that will let us install framework packages when the test
|
||||
|
@ -107,11 +132,10 @@ namespace TerminalAppLocalTests
|
|||
"iterateOn": "profiles",
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}" }
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
|
@ -231,11 +255,10 @@ namespace TerminalAppLocalTests
|
|||
"iterateOn": "profiles",
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}" }
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
|
@ -357,11 +380,10 @@ namespace TerminalAppLocalTests
|
|||
"iterateOn": "profiles",
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}" }
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
|
@ -495,11 +517,10 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -590,11 +611,10 @@ namespace TerminalAppLocalTests
|
|||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -714,11 +734,10 @@ namespace TerminalAppLocalTests
|
|||
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -851,11 +870,10 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -954,11 +972,10 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -1085,9 +1102,72 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
],
|
||||
"schemes": [
|
||||
{ "name": "scheme_0" },
|
||||
{ "name": "scheme_1" },
|
||||
{ "name": "scheme_2" },
|
||||
{
|
||||
"name": "Campbell",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#0C0C0C",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
"yellow": "#C19C00",
|
||||
"blue": "#0037DA",
|
||||
"purple": "#881798",
|
||||
"cyan": "#3A96DD",
|
||||
"white": "#CCCCCC",
|
||||
"brightBlack": "#767676",
|
||||
"brightRed": "#E74856",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightWhite": "#F2F2F2"
|
||||
},
|
||||
{
|
||||
"name": "Campbell PowerShell",
|
||||
"foreground": "#CCCCCC",
|
||||
"background": "#012456",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#0C0C0C",
|
||||
"red": "#C50F1F",
|
||||
"green": "#13A10E",
|
||||
"yellow": "#C19C00",
|
||||
"blue": "#0037DA",
|
||||
"purple": "#881798",
|
||||
"cyan": "#3A96DD",
|
||||
"white": "#CCCCCC",
|
||||
"brightBlack": "#767676",
|
||||
"brightRed": "#E74856",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightWhite": "#F2F2F2"
|
||||
},
|
||||
{
|
||||
"name": "Vintage",
|
||||
"foreground": "#C0C0C0",
|
||||
"background": "#000000",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#000000",
|
||||
"red": "#800000",
|
||||
"green": "#008000",
|
||||
"yellow": "#808000",
|
||||
"blue": "#000080",
|
||||
"purple": "#800080",
|
||||
"cyan": "#008080",
|
||||
"white": "#C0C0C0",
|
||||
"brightBlack": "#808080",
|
||||
"brightRed": "#FF0000",
|
||||
"brightGreen": "#00FF00",
|
||||
"brightYellow": "#FFFF00",
|
||||
"brightBlue": "#0000FF",
|
||||
"brightPurple": "#FF00FF",
|
||||
"brightCyan": "#00FFFF",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
|
@ -1100,10 +1180,6 @@ namespace TerminalAppLocalTests
|
|||
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
|
||||
// Since at least one profile does not reference a color scheme,
|
||||
// we add a warning saying "the color scheme is unknown"
|
||||
VERIFY_ARE_EQUAL(1u, settings.Warnings().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
auto nameMap{ settings.ActionMap().NameMap() };
|
||||
|
@ -1130,8 +1206,6 @@ namespace TerminalAppLocalTests
|
|||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
// This is the same warning as above
|
||||
VERIFY_ARE_EQUAL(1u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
// Yes, this test is testing splitPane with profiles named after each
|
||||
|
@ -1139,7 +1213,7 @@ namespace TerminalAppLocalTests
|
|||
// just easy tests to write.
|
||||
|
||||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command scheme_0");
|
||||
auto command = expandedCommands.Lookup(L"iterable command Campbell");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
@ -1153,11 +1227,11 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"scheme_0", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command scheme_1");
|
||||
auto command = expandedCommands.Lookup(L"iterable command Campbell PowerShell");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
@ -1171,11 +1245,11 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"scheme_1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"Campbell PowerShell", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = expandedCommands.Lookup(L"iterable command scheme_2");
|
||||
auto command = expandedCommands.Lookup(L"iterable command Vintage");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
@ -1189,7 +1263,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"scheme_2", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"Vintage", realArgs.TerminalArgs().Profile());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,22 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
}
|
||||
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) {}
|
||||
~DebugInputTapConnection() = default;
|
||||
void Start()
|
||||
winrt::fire_and_forget Start()
|
||||
{
|
||||
// GH#11282: It's possible that we're about to be started, _before_
|
||||
// our paired connection is started. Both will get Start()'ed when
|
||||
// their owning TermControl is finally laid out. However, if we're
|
||||
// started first, then we'll immediately start printing to the other
|
||||
// control as well, which might not have initialized yet. If we do
|
||||
// that, we'll explode.
|
||||
//
|
||||
// Instead, wait here until the other connection is started too,
|
||||
// before actually starting the connection to the client app. This
|
||||
// will ensure both controls are initialized before the client app
|
||||
// is.
|
||||
co_await winrt::resume_background();
|
||||
_pairedTap->_start.wait();
|
||||
|
||||
_wrappedConnection.Start();
|
||||
}
|
||||
void WriteInput(hstring const& data)
|
||||
|
@ -59,6 +73,9 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
void DebugTapConnection::Start()
|
||||
{
|
||||
// presume the wrapped connection is started.
|
||||
|
||||
// This is explained in the comment for GH#11282 above.
|
||||
_start.count_down();
|
||||
}
|
||||
|
||||
void DebugTapConnection::WriteInput(hstring const& data)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include "../../inc/cppwinrt_utils.h"
|
||||
#include <til/latch.h>
|
||||
|
||||
namespace winrt::Microsoft::TerminalApp::implementation
|
||||
{
|
||||
|
@ -36,6 +37,8 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _wrappedConnection;
|
||||
winrt::weak_ref<Microsoft::Terminal::TerminalConnection::ITerminalConnection> _inputSide;
|
||||
|
||||
til::latch _start{ 1 };
|
||||
|
||||
friend class DebugInputTapConnection;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -128,7 +128,15 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
|
|||
auto controlSettings = _control.Settings().as<TerminalSettings>();
|
||||
|
||||
args.Profile(controlSettings.ProfileName());
|
||||
args.StartingDirectory(controlSettings.StartingDirectory());
|
||||
// If we know the user's working directory use it instead of the profile.
|
||||
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
|
||||
{
|
||||
args.StartingDirectory(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.StartingDirectory(controlSettings.StartingDirectory());
|
||||
}
|
||||
args.TabTitle(controlSettings.StartingTitle());
|
||||
args.Commandline(controlSettings.Commandline());
|
||||
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#include "ColorHelper.h"
|
||||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
#include "..\TerminalSettingsModel\FileUtils.h"
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
@ -410,33 +413,45 @@ namespace winrt::TerminalApp::implementation
|
|||
// - tab: tab to export
|
||||
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
|
||||
{
|
||||
// This will be used to set up the file picker "filter", to select .txt
|
||||
// files by default.
|
||||
static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
|
||||
{ L"Text Files (*.txt)", L"*.txt" },
|
||||
{ L"All Files (*.*)", L"*.*" }
|
||||
};
|
||||
// An arbitrary GUID to associate with all instances of this
|
||||
// dialog, so they all re-open in the same path as they were
|
||||
// open before:
|
||||
static constexpr winrt::guid clientGuidExportFile{ 0xF6AF20BB, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
|
||||
|
||||
try
|
||||
{
|
||||
if (const auto control{ tab.GetActiveTerminalControl() })
|
||||
{
|
||||
const FileSavePicker savePicker;
|
||||
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
|
||||
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
|
||||
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
|
||||
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
|
||||
savePicker.SuggestedFileName(control.Title());
|
||||
// GH#11356 - we can't use the UWP apis for writing the file,
|
||||
// because they don't work elevated (shocker) So just use the
|
||||
// shell32 file picker manually.
|
||||
auto path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
|
||||
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
|
||||
try
|
||||
{
|
||||
// Default to the Downloads folder
|
||||
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr) };
|
||||
dialog->SetDefaultFolder(folderShellItem.get());
|
||||
}
|
||||
CATCH_LOG(); // non-fatal
|
||||
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
|
||||
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
|
||||
THROW_IF_FAILED(dialog->SetDefaultExtension(L"txt"));
|
||||
|
||||
const StorageFile file = co_await savePicker.PickSaveFileAsync();
|
||||
if (file != nullptr)
|
||||
// Default to using the tab title as the file name
|
||||
THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
|
||||
});
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
const auto buffer = control.ReadEntireBuffer();
|
||||
CachedFileManager::DeferUpdates(file);
|
||||
co_await FileIO::WriteTextAsync(file, buffer);
|
||||
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
|
||||
switch (status)
|
||||
{
|
||||
case FileUpdateStatus::Complete:
|
||||
case FileUpdateStatus::CompleteAndRenamed:
|
||||
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
|
||||
break;
|
||||
default:
|
||||
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
|
||||
}
|
||||
CascadiaSettings::ExportFile(path, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<mux:TabView.TabStripHeader>
|
||||
<!-- EA18 is the "Shield" glyph -->
|
||||
<FontIcon x:Uid="ElevationShield"
|
||||
Margin="9,4,0,0"
|
||||
Margin="9,4,0,4"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
|
||||
|
|
|
@ -1461,15 +1461,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// when the TabViewItem is unselected. So we still need to set the other
|
||||
// properties ourselves.
|
||||
//
|
||||
// GH#11294: DESPITE the fact that there's a Background() API that we
|
||||
// could just call like:
|
||||
//
|
||||
// TabViewItem().Background(deselectedTabBrush);
|
||||
//
|
||||
// We actually can't, because it will make the part of the tab that
|
||||
// doesn't contain the text totally transparent to hit tests. So we
|
||||
// actually _do_ still need to set TabViewItemHeaderBackground manually.
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
|
||||
// In GH#11294 we thought we'd still need to set
|
||||
// TabViewItemHeaderBackground manually, but GH#11382 discovered that
|
||||
// Background() was actually okay after all.
|
||||
TabViewItem().Background(deselectedTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
|
||||
|
@ -1536,6 +1531,11 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
// GH#11382 DON'T set the background to null. If you do that, then the
|
||||
// tab won't be hit testable at all. Transparent, however, is a totally
|
||||
// valid hit test target. That makes sense.
|
||||
TabViewItem().Background(WUX::Media::SolidColorBrush{ Windows::UI::Colors::Transparent() });
|
||||
|
||||
_RefreshVisualState();
|
||||
_colorCleared();
|
||||
}
|
||||
|
|
|
@ -442,6 +442,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
_settings.Opacity(newOpacity);
|
||||
|
||||
// GH#11285 - If the user is on Windows 10, and they changed the
|
||||
// transparency of the control s.t. it should be partially opaque, then
|
||||
// opt them in to acrylic. It's the only way to have transparency on
|
||||
// Windows 10.
|
||||
// We'll also turn the acrylic back off when they're fully opaque, which
|
||||
// is what the Terminal did prior to 1.12.
|
||||
if (!IsVintageOpacityAvailable())
|
||||
{
|
||||
_settings.UseAcrylic(newOpacity < 1.0);
|
||||
}
|
||||
|
||||
auto eventArgs = winrt::make_self<TransparencyChangedEventArgs>(newOpacity);
|
||||
_TransparencyChangedHandlers(*this, *eventArgs);
|
||||
}
|
||||
|
@ -570,6 +581,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
_settings = settings;
|
||||
|
||||
// GH#11285 - If the user is on Windows 10, and they wanted opacity, but
|
||||
// didn't explicitly request acrylic, then opt them in to acrylic.
|
||||
// On Windows 11+, this isn't needed, because we can have vintage opacity.
|
||||
if (!IsVintageOpacityAvailable() && _settings.Opacity() < 1.0 && !_settings.UseAcrylic())
|
||||
{
|
||||
_settings.UseAcrylic(true);
|
||||
}
|
||||
|
||||
// Initialize our font information.
|
||||
const auto fontFace = _settings.FontFace();
|
||||
const short fontHeight = ::base::saturated_cast<short>(_settings.FontSize());
|
||||
|
@ -1025,7 +1044,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
TerminalConnection::ConnectionState ControlCore::ConnectionState() const
|
||||
{
|
||||
return _connection.State();
|
||||
return _connection ? _connection.State() : TerminalConnection::ConnectionState::Closed;
|
||||
}
|
||||
|
||||
hstring ControlCore::Title()
|
||||
|
@ -1545,4 +1564,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
return hstring(ss.str());
|
||||
}
|
||||
|
||||
// Helper to check if we're on Windows 11 or not. This is used to check if
|
||||
// we need to use acrylic to achieve transparency, because vintage opacity
|
||||
// doesn't work in islands on win10.
|
||||
// Remove when we can remove the rest of GH#11285
|
||||
bool ControlCore::IsVintageOpacityAvailable() noexcept
|
||||
{
|
||||
OSVERSIONINFOEXW osver{};
|
||||
osver.dwOSVersionInfoSize = sizeof(osver);
|
||||
osver.dwBuildNumber = 22000;
|
||||
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
|
||||
|
||||
return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
hstring ReadEntireBuffer() const;
|
||||
|
||||
static bool IsVintageOpacityAvailable() noexcept;
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
|
|
|
@ -849,7 +849,13 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
|
|||
// will release this lock when it's destructed.
|
||||
[[nodiscard]] std::unique_lock<til::ticket_lock> Terminal::LockForReading()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
return std::unique_lock{ _readWriteLock };
|
||||
#else
|
||||
auto lock = std::unique_lock{ _readWriteLock };
|
||||
_lastLocker = GetCurrentThreadId();
|
||||
return lock;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -859,7 +865,13 @@ WORD Terminal::_TakeVirtualKeyFromLastKeyEvent(const WORD scanCode) noexcept
|
|||
// will release this lock when it's destructed.
|
||||
[[nodiscard]] std::unique_lock<til::ticket_lock> Terminal::LockForWriting()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
return std::unique_lock{ _readWriteLock };
|
||||
#else
|
||||
auto lock = std::unique_lock{ _readWriteLock };
|
||||
_lastLocker = GetCurrentThreadId();
|
||||
return lock;
|
||||
#endif
|
||||
}
|
||||
|
||||
Viewport Terminal::_GetMutableViewport() const noexcept
|
||||
|
|
|
@ -269,6 +269,9 @@ private:
|
|||
// But we can abuse the fact that the surrounding members rarely change and are huge
|
||||
// (std::function is like 64 bytes) to create some natural padding without wasting space.
|
||||
til::ticket_lock _readWriteLock;
|
||||
#ifndef NDEBUG
|
||||
DWORD _lastLocker;
|
||||
#endif
|
||||
|
||||
std::function<void(const int, const int, const int)> _pfnScrollPositionChanged;
|
||||
std::function<void(const til::color)> _pfnBackgroundColorChanged;
|
||||
|
|
|
@ -233,6 +233,9 @@ catch (...)
|
|||
void Terminal::LockConsole() noexcept
|
||||
{
|
||||
_readWriteLock.lock();
|
||||
#ifndef NDEBUG
|
||||
_lastLocker = GetCurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
@ -369,7 +369,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
const auto kbdVM{ get_self<KeyBindingViewModel>(_KeyBindingList.GetAt(i)) };
|
||||
const auto& otherKeys{ kbdVM->CurrentKeys() };
|
||||
if (keys.Modifiers() == otherKeys.Modifiers() && keys.Vkey() == otherKeys.Vkey())
|
||||
if (otherKeys && keys.Modifiers() == otherKeys.Modifiers() && keys.Vkey() == otherKeys.Vkey())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
@ -7,6 +7,7 @@
|
|||
#include "EnumEntry.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include "..\WinRTUtils\inc\Utils.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
@ -8,6 +8,7 @@
|
|||
#include "EnumEntry.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include "..\WinRTUtils\inc\Utils.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
@ -46,6 +47,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
// NOTE: this is similar to what is done with BackgroundImagePath above
|
||||
_NotifyChanges(L"UseParentProcessDirectory", L"UseCustomStartingDirectory");
|
||||
}
|
||||
else if (viewModelProperty == L"UseAcrylic")
|
||||
{
|
||||
// GH#11372: If we're on Windows 10, and someone turns off
|
||||
// acrylic, we're going to disable opacity for them. Opacity
|
||||
// doesn't work without acrylic on Windows 10.
|
||||
//
|
||||
// BODGY: CascadiaSettings's function IsDefaultTerminalAvailable
|
||||
// is basically a "are we on Windows 11" check, because defterm
|
||||
// only works on Win11. So we'll use that.
|
||||
//
|
||||
// Remove when we can remove the rest of GH#11285
|
||||
if (!UseAcrylic() && !CascadiaSettings::IsDefaultTerminalAvailable())
|
||||
{
|
||||
Opacity(1.0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Do the same for the starting directory
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
@ -22,7 +22,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
|
||||
void SetAcrylicOpacityPercentageValue(double value)
|
||||
{
|
||||
_profile.DefaultAppearance().Opacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(value));
|
||||
Opacity(winrt::Microsoft::Terminal::Settings::Editor::Converters::PercentageValueToPercentage(value));
|
||||
|
||||
// GH#11372: If we're on Windows 10, and someone wants opacity, then
|
||||
// we'll turn acrylic on for them. Opacity doesn't work without
|
||||
// acrylic on Windows 10.
|
||||
//
|
||||
// BODGY: CascadiaSettings's function IsDefaultTerminalAvailable
|
||||
// is basically a "are we on Windows 11" check, because defterm
|
||||
// only works on Win11. So we'll use that.
|
||||
//
|
||||
// Remove when we can remove the rest of GH#11285
|
||||
if (value < 100.0 && winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings::IsDefaultTerminalAvailable())
|
||||
{
|
||||
UseAcrylic(true);
|
||||
}
|
||||
};
|
||||
|
||||
void SetPadding(double value)
|
||||
|
|
|
@ -249,6 +249,7 @@
|
|||
<!-- Opacity -->
|
||||
<local:SettingContainer x:Name="OpacityContainer"
|
||||
x:Uid="Profile_Opacity"
|
||||
Margin="0"
|
||||
ClearSettingValue="{x:Bind State.Profile.ClearOpacity}"
|
||||
HasSettingValue="{x:Bind State.Profile.HasOpacity, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind State.Profile.OpacityOverrideSource, Mode=OneWay}">
|
||||
|
|
|
@ -15,30 +15,6 @@ using namespace winrt::Windows::UI::Xaml;
|
|||
|
||||
UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.Settings.Editor/Resources");
|
||||
|
||||
// Function Description:
|
||||
// - Helper that opens a file picker pre-seeded with image file types.
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd)
|
||||
{
|
||||
static constexpr COMDLG_FILTERSPEC supportedImageFileTypes[] = {
|
||||
{ L"All Supported Bitmap Types (*.jpg, *.jpeg, *.png, *.bmp, *.gif, *.tiff, *.ico)", L"*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.ico" },
|
||||
{ L"All Files (*.*)", L"*.*" }
|
||||
};
|
||||
|
||||
static constexpr winrt::guid clientGuidImagePicker{ 0x55675F54, 0x74A1, 0x4552, { 0xA3, 0x9D, 0x94, 0xAE, 0x85, 0xD8, 0xF2, 0x7A } };
|
||||
return OpenFilePicker(parentHwnd, [](auto&& dialog) {
|
||||
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidImagePicker));
|
||||
try
|
||||
{
|
||||
auto pictureFolderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_PicturesLibrary, KF_FLAG_DEFAULT, nullptr) };
|
||||
dialog->SetDefaultFolder(pictureFolderShellItem.get());
|
||||
}
|
||||
CATCH_LOG(); // non-fatal
|
||||
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedImageFileTypes), supportedImageFileTypes));
|
||||
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
|
||||
THROW_IF_FAILED(dialog->SetDefaultExtension(L"jpg;jpeg;png;bmp;gif;tiff;ico"));
|
||||
});
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings
|
||||
{
|
||||
hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable)
|
||||
|
|
|
@ -97,42 +97,6 @@ public: \
|
|||
private: \
|
||||
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;
|
||||
|
||||
// Function Description:
|
||||
// - This function presents a File Open "common dialog" and returns its selected file asynchronously.
|
||||
// Parameters:
|
||||
// - customize: A lambda that receives an IFileDialog* to customize.
|
||||
// Return value:
|
||||
// (async) path to the selected item.
|
||||
template<typename TLambda>
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenFilePicker(HWND parentHwnd, TLambda&& customize)
|
||||
{
|
||||
auto fileDialog{ winrt::create_instance<IFileDialog>(CLSID_FileOpenDialog) };
|
||||
DWORD flags{};
|
||||
THROW_IF_FAILED(fileDialog->GetOptions(&flags));
|
||||
THROW_IF_FAILED(fileDialog->SetOptions(flags | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_DONTADDTORECENT)); // filesystem objects only; no recent places
|
||||
customize(fileDialog.get());
|
||||
|
||||
auto hr{ fileDialog->Show(parentHwnd) };
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
|
||||
{
|
||||
co_return winrt::hstring{};
|
||||
}
|
||||
THROW_HR(hr);
|
||||
}
|
||||
|
||||
winrt::com_ptr<IShellItem> result;
|
||||
THROW_IF_FAILED(fileDialog->GetResult(result.put()));
|
||||
|
||||
wil::unique_cotaskmem_string filePath;
|
||||
THROW_IF_FAILED(result->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
|
||||
|
||||
co_return winrt::hstring{ filePath.get() };
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd);
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings
|
||||
{
|
||||
winrt::hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable);
|
||||
|
|
|
@ -1683,7 +1683,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
JsonUtils::SetValueForKey(json, NameKey, args->_Name);
|
||||
JsonUtils::SetValueForKey(json, DesktopKey, args->_Desktop);
|
||||
JsonUtils::SetValueForKey(json, MonitorKey, args->_Monitor);
|
||||
JsonUtils::GetValueForKey(json, DropdownDurationKey, args->_DropdownDuration);
|
||||
JsonUtils::SetValueForKey(json, DropdownDurationKey, args->_DropdownDuration);
|
||||
JsonUtils::SetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility);
|
||||
return json;
|
||||
}
|
||||
|
|
|
@ -94,15 +94,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
static void RegisterShortcutAction(ShortcutAction shortcutAction, std::unordered_map<hstring, Model::ActionAndArgs>& list, std::unordered_set<InternalActionID>& visited)
|
||||
{
|
||||
const auto actionAndArgs{ make_self<ActionAndArgs>(shortcutAction) };
|
||||
if (actionAndArgs->Action() != ShortcutAction::Invalid)
|
||||
/*We have a valid action.*/
|
||||
/*Check if the action was already added.*/
|
||||
if (visited.find(Hash(*actionAndArgs)) == visited.end())
|
||||
{
|
||||
/*We have a valid action.*/
|
||||
/*Check if the action was already added.*/
|
||||
if (visited.find(Hash(*actionAndArgs)) == visited.end())
|
||||
/*This is an action that wasn't added!*/
|
||||
/*Let's add it if it has a name.*/
|
||||
if (const auto name{ actionAndArgs->GenerateName() }; !name.empty())
|
||||
{
|
||||
/*This is an action that wasn't added!*/
|
||||
/*Let's add it.*/
|
||||
const auto name{ actionAndArgs->GenerateName() };
|
||||
list.insert({ name, *actionAndArgs });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include "CascadiaSettings.h"
|
||||
#include "CascadiaSettings.g.cpp"
|
||||
|
||||
#include "DefaultTerminal.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
|
@ -15,6 +18,13 @@ using namespace winrt::Microsoft::Terminal::Control;
|
|||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace Microsoft::Console;
|
||||
|
||||
// Creating a child of a profile requires us to copy certain
|
||||
// required attributes. This method handles those attributes.
|
||||
//
|
||||
// NOTE however that it doesn't call _FinalizeInheritance() for you! Don't forget that!
|
||||
//
|
||||
// At the time of writing only one caller needs to call _FinalizeInheritance(),
|
||||
// which is why this unsafety wasn't further abstracted away.
|
||||
winrt::com_ptr<Profile> Model::implementation::CreateChild(const winrt::com_ptr<Profile>& parent)
|
||||
{
|
||||
auto profile = winrt::make_self<Profile>();
|
||||
|
@ -368,6 +378,7 @@ winrt::com_ptr<Profile> CascadiaSettings::_createNewProfile(const std::wstring_v
|
|||
LOG_IF_FAILED(CoCreateGuid(&guid));
|
||||
|
||||
auto profile = CreateChild(_baseLayerProfile);
|
||||
profile->_FinalizeInheritance();
|
||||
profile->Guid(guid);
|
||||
profile->Name(winrt::hstring{ name });
|
||||
return profile;
|
||||
|
@ -842,31 +853,21 @@ bool CascadiaSettings::IsDefaultTerminalAvailable() noexcept
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - an iterable collection of all available terminals that could be the default.
|
||||
IObservableVector<Model::DefaultTerminal> CascadiaSettings::DefaultTerminals() const noexcept
|
||||
IObservableVector<Model::DefaultTerminal> CascadiaSettings::DefaultTerminals() noexcept
|
||||
{
|
||||
const auto available = DefaultTerminal::Available();
|
||||
std::vector<Model::DefaultTerminal> terminals{ available.Size(), nullptr };
|
||||
available.GetMany(0, terminals);
|
||||
return winrt::single_threaded_observable_vector(std::move(terminals));
|
||||
_refreshDefaultTerminals();
|
||||
return _defaultTerminals;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the currently selected default terminal application.
|
||||
// - DANGER! This will be null unless you've called
|
||||
// CascadiaSettings::RefreshDefaultTerminals. At the time of this comment (May
|
||||
|
||||
// 2021), only the Launch page in the settings UI calls that method, so this
|
||||
// value is unset unless you've navigated to that page.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the selected default terminal application
|
||||
Settings::Model::DefaultTerminal CascadiaSettings::CurrentDefaultTerminal() noexcept
|
||||
{
|
||||
if (!_currentDefaultTerminal)
|
||||
{
|
||||
_currentDefaultTerminal = DefaultTerminal::Current();
|
||||
}
|
||||
_refreshDefaultTerminals();
|
||||
return _currentDefaultTerminal;
|
||||
}
|
||||
|
||||
|
@ -880,3 +881,28 @@ void CascadiaSettings::CurrentDefaultTerminal(const Model::DefaultTerminal& term
|
|||
{
|
||||
_currentDefaultTerminal = terminal;
|
||||
}
|
||||
|
||||
// This function is implicitly called by DefaultTerminals/CurrentDefaultTerminal().
|
||||
// It reloads the selection of available, installed terminals and caches them.
|
||||
// WinUI requires us that the `SelectedItem` of a collection is member of the list given to `ItemsSource`.
|
||||
// It's thus important that _currentDefaultTerminal is a member of _defaultTerminals.
|
||||
// Right now this is implicitly the case thanks to DefaultTerminal::Available(),
|
||||
// but in the future it might be worthwhile to change the code to use list indices instead.
|
||||
void CascadiaSettings::_refreshDefaultTerminals()
|
||||
{
|
||||
if (!_defaultTerminals)
|
||||
{
|
||||
auto [defaultTerminals, defaultTerminal] = DefaultTerminal::Available();
|
||||
_defaultTerminals = winrt::single_threaded_observable_vector(std::move(defaultTerminals));
|
||||
_currentDefaultTerminal = std::move(defaultTerminal);
|
||||
}
|
||||
}
|
||||
|
||||
void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteUTF8FileAtomic({ path.c_str() }, til::u16u8(content));
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
winrt::com_ptr<implementation::Profile> baseLayerProfile;
|
||||
std::vector<winrt::com_ptr<implementation::Profile>> profiles;
|
||||
std::unordered_map<winrt::guid, winrt::com_ptr<implementation::Profile>> profilesByGuid;
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct SettingsLoader
|
||||
|
@ -63,12 +65,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
bool duplicateProfile = false;
|
||||
|
||||
private:
|
||||
struct JsonSettings
|
||||
{
|
||||
Json::Value root;
|
||||
const Json::Value& colorSchemes;
|
||||
const Json::Value& profileDefaults;
|
||||
const Json::Value& profilesList;
|
||||
};
|
||||
|
||||
static std::pair<size_t, size_t> _lineAndColumnFromPosition(const std::string_view& string, const size_t position);
|
||||
static void _rethrowSerializationExceptionWithLocationInfo(const JsonUtils::DeserializationError& e, const std::string_view& settingsString);
|
||||
static Json::Value _parseJSON(const std::string_view& content);
|
||||
static const Json::Value& _getJSONValue(const Json::Value& json, const std::string_view& key) noexcept;
|
||||
gsl::span<const winrt::com_ptr<implementation::Profile>> _getNonUserOriginProfiles() const;
|
||||
void _parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings, bool updatesKeyAllowed = false);
|
||||
void _parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
|
||||
void _parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
|
||||
static JsonSettings _parseJson(const std::string_view& content);
|
||||
static winrt::com_ptr<implementation::Profile> _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson);
|
||||
void _appendProfile(winrt::com_ptr<implementation::Profile>&& profile, ParsedSettings& settings);
|
||||
static void _addParentProfile(const winrt::com_ptr<implementation::Profile>& profile, ParsedSettings& settings);
|
||||
void _executeGenerator(const IDynamicProfileGenerator& generator);
|
||||
|
@ -89,6 +102,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
static winrt::hstring DefaultSettingsPath();
|
||||
static winrt::hstring ApplicationDisplayName();
|
||||
static winrt::hstring ApplicationVersion();
|
||||
static void ExportFile(winrt::hstring path, winrt::hstring content);
|
||||
|
||||
CascadiaSettings() noexcept = default;
|
||||
CascadiaSettings(const winrt::hstring& userJSON, const winrt::hstring& inboxJSON);
|
||||
|
@ -120,7 +134,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
// defterm
|
||||
static bool IsDefaultTerminalAvailable() noexcept;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> DefaultTerminals() const noexcept;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> DefaultTerminals() noexcept;
|
||||
Model::DefaultTerminal CurrentDefaultTerminal() noexcept;
|
||||
void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal);
|
||||
|
||||
|
@ -128,6 +142,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
static const std::filesystem::path& _settingsPath();
|
||||
|
||||
winrt::com_ptr<implementation::Profile> _createNewProfile(const std::wstring_view& name) const;
|
||||
void _refreshDefaultTerminals();
|
||||
|
||||
void _resolveDefaultProfile() const;
|
||||
|
||||
|
@ -139,17 +154,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
bool _hasInvalidColorScheme(const Model::Command& command) const;
|
||||
|
||||
// user settings
|
||||
winrt::com_ptr<implementation::GlobalAppSettings> _globals;
|
||||
winrt::com_ptr<implementation::Profile> _baseLayerProfile;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles;
|
||||
winrt::com_ptr<implementation::GlobalAppSettings> _globals = winrt::make_self<implementation::GlobalAppSettings>();
|
||||
winrt::com_ptr<implementation::Profile> _baseLayerProfile = winrt::make_self<implementation::Profile>();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
|
||||
|
||||
// load errors
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::SettingsLoadWarnings> _warnings;
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::SettingsLoadWarnings> _warnings = winrt::single_threaded_vector<Model::SettingsLoadWarnings>();
|
||||
winrt::Windows::Foundation::IReference<Model::SettingsLoadErrors> _loadError;
|
||||
winrt::hstring _deserializationErrorMessage;
|
||||
|
||||
// defterm
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::DefaultTerminal> _defaultTerminals{ nullptr };
|
||||
Model::DefaultTerminal _currentDefaultTerminal{ nullptr };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
static String ApplicationDisplayName { get; };
|
||||
static String ApplicationVersion { get; };
|
||||
|
||||
static void ExportFile(String path, String content);
|
||||
|
||||
CascadiaSettings(String userJSON, String inboxJSON);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "userDefaults.h"
|
||||
|
||||
#include "ApplicationState.h"
|
||||
#include "DefaultTerminal.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
|
@ -36,8 +37,6 @@ static constexpr std::string_view ProfilesKey{ "profiles" };
|
|||
static constexpr std::string_view DefaultSettingsKey{ "defaults" };
|
||||
static constexpr std::string_view ProfilesListKey{ "list" };
|
||||
static constexpr std::string_view SchemesKey{ "schemes" };
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view GuidKey{ "guid" };
|
||||
|
||||
static constexpr std::wstring_view jsonExtension{ L".json" };
|
||||
static constexpr std::wstring_view FragmentsSubDirectory{ L"\\Fragments" };
|
||||
|
@ -80,6 +79,14 @@ static std::filesystem::path buildPath(const std::wstring_view& lhs, const std::
|
|||
return { std::move(buffer) };
|
||||
}
|
||||
|
||||
void ParsedSettings::clear()
|
||||
{
|
||||
globals = {};
|
||||
baseLayerProfile = {};
|
||||
profiles.clear();
|
||||
profilesByGuid.clear();
|
||||
}
|
||||
|
||||
// This is a convenience method used by the CascadiaSettings constructor.
|
||||
// It runs some basic settings layering without relying on external programs or files.
|
||||
// This makes it suitable for most unit tests.
|
||||
|
@ -199,7 +206,7 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
|||
try
|
||||
{
|
||||
const auto content = ReadUTF8File(fragmentExt.path());
|
||||
_parse(OriginTag::Fragment, source, content, fragmentSettings, true);
|
||||
_parseFragment(source, content, fragmentSettings);
|
||||
|
||||
for (const auto& fragmentProfile : fragmentSettings.profiles)
|
||||
{
|
||||
|
@ -430,92 +437,131 @@ gsl::span<const winrt::com_ptr<Profile>> SettingsLoader::_getNonUserOriginProfil
|
|||
}
|
||||
|
||||
// Parses the given JSON string ("content") and fills a ParsedSettings instance with it.
|
||||
void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings, bool updatesKeyAllowed)
|
||||
// This function is to be used for user settings files.
|
||||
void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings)
|
||||
{
|
||||
const auto json = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
|
||||
const auto& profilesObject = _getJSONValue(json, ProfilesKey);
|
||||
const auto& defaultsObject = _getJSONValue(profilesObject, DefaultSettingsKey);
|
||||
const auto& profilesArray = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
|
||||
const auto json = _parseJson(content);
|
||||
|
||||
settings.clear();
|
||||
|
||||
// globals
|
||||
{
|
||||
settings.globals = GlobalAppSettings::FromJson(json);
|
||||
settings.globals = GlobalAppSettings::FromJson(json.root);
|
||||
|
||||
if (const auto& schemes = _getJSONValue(json, SchemesKey))
|
||||
for (const auto& schemeJson : json.colorSchemes)
|
||||
{
|
||||
for (const auto& schemeJson : schemes)
|
||||
if (const auto scheme = ColorScheme::FromJson(schemeJson))
|
||||
{
|
||||
if (schemeJson.isObject())
|
||||
{
|
||||
if (const auto scheme = ColorScheme::FromJson(schemeJson))
|
||||
{
|
||||
settings.globals->AddColorScheme(*scheme);
|
||||
}
|
||||
}
|
||||
settings.globals->AddColorScheme(*scheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// profiles.defaults
|
||||
{
|
||||
settings.baseLayerProfile = Profile::FromJson(defaultsObject);
|
||||
settings.baseLayerProfile = Profile::FromJson(json.profileDefaults);
|
||||
// Remove the `guid` member from the default settings.
|
||||
// That will hyper-explode, so just don't let them do that.
|
||||
settings.baseLayerProfile->ClearGuid();
|
||||
settings.baseLayerProfile->Origin(OriginTag::ProfilesDefaults);
|
||||
}
|
||||
|
||||
// profiles.list
|
||||
{
|
||||
const auto size = profilesArray.size();
|
||||
|
||||
// NOTE: This function is supposed to *replace* the contents of ParsedSettings. Don't break this promise.
|
||||
// SettingsLoader::FindFragmentsAndMergeIntoUserSettings relies on this.
|
||||
settings.profiles.clear();
|
||||
const auto size = json.profilesList.size();
|
||||
settings.profiles.reserve(size);
|
||||
|
||||
settings.profilesByGuid.clear();
|
||||
settings.profilesByGuid.reserve(size);
|
||||
|
||||
for (const auto& profileJson : profilesArray)
|
||||
for (const auto& profileJson : json.profilesList)
|
||||
{
|
||||
auto profile = Profile::FromJson(profileJson);
|
||||
profile->Origin(origin);
|
||||
|
||||
// The Guid() generation below depends on the value of Source().
|
||||
// --> Provide one if we got one.
|
||||
if (!source.empty())
|
||||
auto profile = _parseProfile(origin, source, profileJson);
|
||||
// GH#9962: Discard Guid-less, Name-less profiles.
|
||||
if (profile->HasGuid())
|
||||
{
|
||||
profile->Source(source);
|
||||
_appendProfile(std::move(profile), settings);
|
||||
}
|
||||
|
||||
// The Guid() getter generates one from Name() and Source() if none exists otherwise.
|
||||
// We want to ensure that every profile has a GUID no matter what, not just to
|
||||
// cache the value, but also to make them consistently identifiable later on.
|
||||
if (!profile->HasGuid())
|
||||
{
|
||||
if (profile->HasName())
|
||||
{
|
||||
profile->Guid(profile->Guid());
|
||||
}
|
||||
else if (!updatesKeyAllowed || profile->Updates() == winrt::guid{})
|
||||
{
|
||||
// We introduced a bug (GH#9962, fixed in GH#9964) that would result in one or
|
||||
// more nameless, guid-less profiles being emitted into the user's settings file.
|
||||
// Those profiles would show up in the list as "Default" later.
|
||||
//
|
||||
// Fragments however can contain an alternative "updates" key, which works similar to the "guid".
|
||||
// If updatesKeyAllowed is true (see FindFragmentsAndMergeIntoUserSettings) we permit
|
||||
// such Guid-less, Name-less profiles as long as they have a valid Updates field.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_appendProfile(std::move(profile), settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just like _parse, but is to be used for fragment files, which don't support anything but color
|
||||
// schemes and profiles. Additionally this function supports profiles which specify an "updates" key.
|
||||
void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings)
|
||||
{
|
||||
const auto json = _parseJson(content);
|
||||
|
||||
settings.clear();
|
||||
|
||||
{
|
||||
settings.globals = winrt::make_self<GlobalAppSettings>();
|
||||
|
||||
for (const auto& schemeJson : json.colorSchemes)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (const auto scheme = ColorScheme::FromJson(schemeJson))
|
||||
{
|
||||
settings.globals->AddColorScheme(*scheme);
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto size = json.profilesList.size();
|
||||
settings.profiles.reserve(size);
|
||||
settings.profilesByGuid.reserve(size);
|
||||
|
||||
for (const auto& profileJson : json.profilesList)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto profile = _parseProfile(OriginTag::Fragment, source, profileJson);
|
||||
// GH#9962: Discard Guid-less, Name-less profiles, but...
|
||||
// allow ones with an Updates field, as those are special for fragments.
|
||||
if (profile->HasGuid() || profile->Updates() != winrt::guid{})
|
||||
{
|
||||
_appendProfile(std::move(profile), settings);
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view& content)
|
||||
{
|
||||
auto root = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
|
||||
const auto& colorSchemes = _getJSONValue(root, SchemesKey);
|
||||
const auto& profilesObject = _getJSONValue(root, ProfilesKey);
|
||||
const auto& profileDefaults = _getJSONValue(profilesObject, DefaultSettingsKey);
|
||||
const auto& profilesList = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
|
||||
return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList };
|
||||
}
|
||||
|
||||
// Just a common helper function between _parse and _parseFragment.
|
||||
// Parses a profile and ensures it has a Guid if possible.
|
||||
winrt::com_ptr<Profile> SettingsLoader::_parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson)
|
||||
{
|
||||
auto profile = Profile::FromJson(profileJson);
|
||||
profile->Origin(origin);
|
||||
|
||||
// The Guid() generation below depends on the value of Source().
|
||||
// --> Provide one if we got one.
|
||||
if (!source.empty())
|
||||
{
|
||||
profile->Source(source);
|
||||
}
|
||||
|
||||
// If none exists. the Guid() getter generates one from Name() and optionally Source().
|
||||
// We want to ensure that every profile has a GUID no matter what, not just to
|
||||
// cache the value, but also to make them consistently identifiable later on.
|
||||
if (!profile->HasGuid() && profile->HasName())
|
||||
{
|
||||
profile->Guid(profile->Guid());
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
// Adds a profile to the ParsedSettings instance. Takes ownership of the profile.
|
||||
// It ensures no duplicate GUIDs are added to the ParsedSettings instance.
|
||||
void SettingsLoader::_appendProfile(winrt::com_ptr<Profile>&& profile, ParsedSettings& settings)
|
||||
|
@ -550,6 +596,7 @@ void SettingsLoader::_addParentProfile(const winrt::com_ptr<implementation::Prof
|
|||
//
|
||||
// When a user modifies a profile they shouldn't modify the (static/constant)
|
||||
// inbox profile of course. That's why we need to call CreateChild here.
|
||||
// But we don't need to call _FinalizeInheritance() yet as this is handled by SettingsLoader::FinalizeLayering().
|
||||
settings.profiles.emplace_back(CreateChild(profile));
|
||||
}
|
||||
}
|
||||
|
@ -696,7 +743,14 @@ CascadiaSettings::CascadiaSettings(const std::string_view& userJSON, const std::
|
|||
{
|
||||
}
|
||||
|
||||
CascadiaSettings::CascadiaSettings(SettingsLoader&& loader)
|
||||
CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) :
|
||||
// The CascadiaSettings class declaration initializes these fields by default,
|
||||
// but we're going to set these fields in our constructor later on anyways.
|
||||
_globals{},
|
||||
_baseLayerProfile{},
|
||||
_allProfiles{},
|
||||
_activeProfiles{},
|
||||
_warnings{}
|
||||
{
|
||||
std::vector<Model::Profile> allProfiles;
|
||||
std::vector<Model::Profile> activeProfiles;
|
||||
|
|
|
@ -10,85 +10,70 @@
|
|||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::DefaultTerminal> DefaultTerminal::_available = winrt::single_threaded_vector<Model::DefaultTerminal>();
|
||||
Model::DefaultTerminal DefaultTerminal::_current = nullptr;
|
||||
|
||||
DefaultTerminal::DefaultTerminal(DelegationConfig::DelegationPackage pkg) :
|
||||
_pkg(pkg)
|
||||
DefaultTerminal::DefaultTerminal(DelegationConfig::DelegationPackage&& pkg) :
|
||||
_pkg{ pkg }
|
||||
{
|
||||
}
|
||||
|
||||
winrt::hstring DefaultTerminal::Name() const
|
||||
{
|
||||
static const std::wstring def{ RS_(L"InboxWindowsConsoleName") };
|
||||
return _pkg.terminal.name.empty() ? winrt::hstring{ def } : winrt::hstring{ _pkg.terminal.name };
|
||||
return _pkg.terminal.name.empty() ? winrt::hstring{ RS_(L"InboxWindowsConsoleName") } : winrt::hstring{ _pkg.terminal.name };
|
||||
}
|
||||
|
||||
winrt::hstring DefaultTerminal::Version() const
|
||||
{
|
||||
// If there's no version information... return empty string instead.
|
||||
if (DelegationConfig::PkgVersion{} == _pkg.terminal.version)
|
||||
const auto& version = _pkg.terminal.version;
|
||||
if (DelegationConfig::PkgVersion{} == version)
|
||||
{
|
||||
return winrt::hstring{};
|
||||
}
|
||||
|
||||
const auto name = fmt::format(L"{}.{}.{}.{}", _pkg.terminal.version.major, _pkg.terminal.version.minor, _pkg.terminal.version.build, _pkg.terminal.version.revision);
|
||||
return winrt::hstring{ name };
|
||||
fmt::wmemory_buffer buffer;
|
||||
fmt::format_to(buffer, L"{}.{}.{}.{}", version.major, version.minor, version.build, version.revision);
|
||||
return winrt::hstring{ buffer.data(), gsl::narrow_cast<winrt::hstring::size_type>(buffer.size()) };
|
||||
}
|
||||
|
||||
winrt::hstring DefaultTerminal::Author() const
|
||||
{
|
||||
static const std::wstring def{ RS_(L"InboxWindowsConsoleAuthor") };
|
||||
return _pkg.terminal.author.empty() ? winrt::hstring{ def } : winrt::hstring{ _pkg.terminal.author };
|
||||
return _pkg.terminal.author.empty() ? winrt::hstring{ RS_(L"InboxWindowsConsoleAuthor") } : winrt::hstring{ _pkg.terminal.author };
|
||||
}
|
||||
|
||||
winrt::hstring DefaultTerminal::Icon() const
|
||||
{
|
||||
static const std::wstring_view def{ L"\uE756" };
|
||||
return _pkg.terminal.logo.empty() ? winrt::hstring{ def } : winrt::hstring{ _pkg.terminal.logo };
|
||||
return _pkg.terminal.logo.empty() ? winrt::hstring{ L"\uE756" } : winrt::hstring{ _pkg.terminal.logo };
|
||||
}
|
||||
|
||||
void DefaultTerminal::Refresh()
|
||||
std::pair<std::vector<Model::DefaultTerminal>, Model::DefaultTerminal> DefaultTerminal::Available()
|
||||
{
|
||||
// The potential of returning nullptr for defaultTerminal feels weird, but XAML can
|
||||
// handle that appropriately and will select nothing as current in the dropdown.
|
||||
std::vector<Model::DefaultTerminal> defaultTerminals;
|
||||
Model::DefaultTerminal defaultTerminal{ nullptr };
|
||||
|
||||
std::vector<DelegationConfig::DelegationPackage> allPackages;
|
||||
DelegationConfig::DelegationPackage currentPackage;
|
||||
|
||||
LOG_IF_FAILED(DelegationConfig::s_GetAvailablePackages(allPackages, currentPackage));
|
||||
|
||||
_available.Clear();
|
||||
for (const auto& pkg : allPackages)
|
||||
for (auto& pkg : allPackages)
|
||||
{
|
||||
auto p = winrt::make<DefaultTerminal>(pkg);
|
||||
// Be a bit careful here: We're moving pkg into the constructor.
|
||||
// Afterwards it'll be invalid, so we need to cache isCurrent.
|
||||
const auto isCurrent = pkg == currentPackage;
|
||||
auto p = winrt::make<DefaultTerminal>(std::move(pkg));
|
||||
|
||||
_available.Append(p);
|
||||
|
||||
if (pkg == currentPackage)
|
||||
if (isCurrent)
|
||||
{
|
||||
_current = p;
|
||||
defaultTerminal = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVectorView<Model::DefaultTerminal> DefaultTerminal::Available()
|
||||
{
|
||||
Refresh();
|
||||
return _available.GetView();
|
||||
}
|
||||
|
||||
Model::DefaultTerminal DefaultTerminal::Current()
|
||||
{
|
||||
if (!_current)
|
||||
{
|
||||
Refresh();
|
||||
defaultTerminals.emplace_back(std::move(p));
|
||||
}
|
||||
|
||||
// The potential of returning nullptr feels weird, but XAML can handle that appropriately
|
||||
// and will select nothing as current in the dropdown.
|
||||
return _current;
|
||||
return { std::move(defaultTerminals), std::move(defaultTerminal) };
|
||||
}
|
||||
|
||||
void DefaultTerminal::Current(const Model::DefaultTerminal& term)
|
||||
{
|
||||
THROW_IF_FAILED(DelegationConfig::s_SetDefaultByPackage(winrt::get_self<DefaultTerminal>(term)->_pkg, true));
|
||||
_current = term;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ Author(s):
|
|||
#pragma once
|
||||
|
||||
#include "DefaultTerminal.g.h"
|
||||
#include "../inc/cppwinrt_utils.h"
|
||||
|
||||
#include "../../propslib/DelegationConfig.hpp"
|
||||
|
||||
|
@ -27,26 +26,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{
|
||||
struct DefaultTerminal : public DefaultTerminalT<DefaultTerminal>
|
||||
{
|
||||
DefaultTerminal(DelegationConfig::DelegationPackage pkg);
|
||||
explicit DefaultTerminal(DelegationConfig::DelegationPackage&& pkg);
|
||||
|
||||
hstring Name() const;
|
||||
hstring Author() const;
|
||||
hstring Version() const;
|
||||
hstring Icon() const;
|
||||
|
||||
static void Refresh();
|
||||
static Windows::Foundation::Collections::IVectorView<Model::DefaultTerminal> Available();
|
||||
static Model::DefaultTerminal Current();
|
||||
static std::pair<std::vector<Model::DefaultTerminal>, Model::DefaultTerminal> Available();
|
||||
static void Current(const Model::DefaultTerminal& term);
|
||||
|
||||
private:
|
||||
DelegationConfig::DelegationPackage _pkg;
|
||||
static Windows::Foundation::Collections::IVector<Model::DefaultTerminal> _available;
|
||||
static Model::DefaultTerminal _current;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(DefaultTerminal);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,5 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
String Author { get; };
|
||||
String Version { get; };
|
||||
String Icon { get; };
|
||||
|
||||
static DefaultTerminal Current;
|
||||
static Windows.Foundation.Collections.IVectorView<DefaultTerminal> Available { get; };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,8 +139,11 @@ namespace ControlUnitTests
|
|||
|
||||
// GH#603: Adjusting opacity shouldn't change whether or not we
|
||||
// requested acrylic.
|
||||
VERIFY_IS_TRUE(settings->UseAcrylic());
|
||||
VERIFY_IS_TRUE(core->_settings.UseAcrylic());
|
||||
|
||||
auto expectedUseAcrylic = winrt::Microsoft::Terminal::Control::implementation::ControlCore::IsVintageOpacityAvailable() ? true :
|
||||
(expectedOpacity < 1.0 ? true : false);
|
||||
VERIFY_ARE_EQUAL(expectedUseAcrylic, settings->UseAcrylic());
|
||||
VERIFY_ARE_EQUAL(expectedUseAcrylic, core->_settings.UseAcrylic());
|
||||
};
|
||||
core->TransparencyChanged(opacityCallback);
|
||||
|
||||
|
|
|
@ -119,8 +119,10 @@ namespace ControlUnitTests
|
|||
VERIFY_ARE_EQUAL(expectedOpacity, settings->Opacity());
|
||||
VERIFY_ARE_EQUAL(expectedOpacity, core->_settings.Opacity());
|
||||
|
||||
VERIFY_ARE_EQUAL(useAcrylic, settings->UseAcrylic());
|
||||
VERIFY_ARE_EQUAL(useAcrylic, core->_settings.UseAcrylic());
|
||||
auto expectedUseAcrylic = winrt::Microsoft::Terminal::Control::implementation::ControlCore::IsVintageOpacityAvailable() ? useAcrylic :
|
||||
(expectedOpacity < 1.0 ? true : false);
|
||||
VERIFY_ARE_EQUAL(expectedUseAcrylic, settings->UseAcrylic());
|
||||
VERIFY_ARE_EQUAL(expectedUseAcrylic, core->_settings.UseAcrylic());
|
||||
};
|
||||
core->TransparencyChanged(opacityCallback);
|
||||
|
||||
|
|
33
src/cascadia/WinRTUtils/Utils.cpp
Normal file
33
src/cascadia/WinRTUtils/Utils.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// Function Description:
|
||||
// - Helper that opens a file picker pre-seeded with image file types.
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd)
|
||||
{
|
||||
static constexpr COMDLG_FILTERSPEC supportedImageFileTypes[] = {
|
||||
{ L"All Supported Bitmap Types (*.jpg, *.jpeg, *.png, *.bmp, *.gif, *.tiff, *.ico)", L"*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.ico" },
|
||||
{ L"All Files (*.*)", L"*.*" }
|
||||
};
|
||||
|
||||
static constexpr winrt::guid clientGuidImagePicker{ 0x55675F54, 0x74A1, 0x4552, { 0xA3, 0x9D, 0x94, 0xAE, 0x85, 0xD8, 0xF2, 0x7A } };
|
||||
return OpenFilePicker(parentHwnd, [](auto&& dialog) {
|
||||
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidImagePicker));
|
||||
try
|
||||
{
|
||||
auto pictureFolderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_PicturesLibrary, KF_FLAG_DEFAULT, nullptr) };
|
||||
dialog->SetDefaultFolder(pictureFolderShellItem.get());
|
||||
}
|
||||
CATCH_LOG(); // non-fatal
|
||||
|
||||
#pragma warning(suppress : 26485) // so we can pass in the supportedImageFileTypes without the analyzer complaining
|
||||
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedImageFileTypes), supportedImageFileTypes));
|
||||
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
|
||||
THROW_IF_FAILED(dialog->SetDefaultExtension(L"jpg;jpeg;png;bmp;gif;tiff;ico"));
|
||||
});
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="LibraryResources.cpp" />
|
||||
<ClCompile Include="ScopedResourceLoader.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= idl Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
|
|
@ -19,3 +19,51 @@ inline winrt::Windows::Foundation::Rect ScaleRect(winrt::Windows::Foundation::Re
|
|||
rect.Height = base::ClampMul(rect.Height, scaleLocal);
|
||||
return rect;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - This function presents a File Open "common dialog" and returns its selected file asynchronously.
|
||||
// Parameters:
|
||||
// - customize: A lambda that receives an IFileDialog* to customize.
|
||||
// Return value:
|
||||
// (async) path to the selected item.
|
||||
template<typename TLambda>
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> FilePicker(HWND parentHwnd, bool saveDialog, TLambda&& customize)
|
||||
{
|
||||
auto fileDialog{ saveDialog ? winrt::create_instance<IFileDialog>(CLSID_FileSaveDialog) :
|
||||
winrt::create_instance<IFileDialog>(CLSID_FileOpenDialog) };
|
||||
DWORD flags{};
|
||||
THROW_IF_FAILED(fileDialog->GetOptions(&flags));
|
||||
THROW_IF_FAILED(fileDialog->SetOptions(flags | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_DONTADDTORECENT)); // filesystem objects only; no recent places
|
||||
customize(fileDialog.get());
|
||||
|
||||
const auto hr{ fileDialog->Show(parentHwnd) };
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
|
||||
{
|
||||
co_return winrt::hstring{};
|
||||
}
|
||||
THROW_HR(hr);
|
||||
}
|
||||
|
||||
winrt::com_ptr<IShellItem> result;
|
||||
THROW_IF_FAILED(fileDialog->GetResult(result.put()));
|
||||
|
||||
wil::unique_cotaskmem_string filePath;
|
||||
THROW_IF_FAILED(result->GetDisplayName(SIGDN_FILESYSPATH, &filePath));
|
||||
|
||||
co_return winrt::hstring{ filePath.get() };
|
||||
}
|
||||
|
||||
template<typename TLambda>
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenFilePicker(HWND parentHwnd, TLambda&& customize)
|
||||
{
|
||||
return FilePicker(parentHwnd, false, customize);
|
||||
}
|
||||
template<typename TLambda>
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> SaveFilePicker(HWND parentHwnd, TLambda&& customize)
|
||||
{
|
||||
return FilePicker(parentHwnd, true, customize);
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// pch.h
|
||||
|
@ -31,3 +31,6 @@
|
|||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.System.h>
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <shobjidl_core.h>
|
||||
|
|
|
@ -1191,7 +1191,7 @@ void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args)
|
|||
{
|
||||
uint32_t actualDropdownDuration = args.DropdownDuration();
|
||||
// If the user requested an animation, let's check if animations are enabled in the OS.
|
||||
if (args.DropdownDuration() > 0)
|
||||
if (actualDropdownDuration > 0)
|
||||
{
|
||||
BOOL animationsEnabled = TRUE;
|
||||
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
|
||||
|
|
|
@ -310,6 +310,12 @@ void NonClientIslandWindow::OnSize(const UINT width, const UINT height)
|
|||
{
|
||||
_UpdateIslandPosition(width, height);
|
||||
}
|
||||
|
||||
// GH#11367: We need to do this,
|
||||
// otherwise the titlebar may still be partially visible
|
||||
// when we move between different DPI monitors.
|
||||
RefreshCurrentDPI();
|
||||
_UpdateFrameMargins();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -29,8 +29,9 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
typedef RECT (*GetSuggestionWindowPos)();
|
||||
typedef RECT (*GetTextBoxAreaPos)();
|
||||
|
||||
BOOL ActivateTextServices(HWND hwndConsole, GetSuggestionWindowPos pfnPosition);
|
||||
BOOL ActivateTextServices(HWND hwndConsole, GetSuggestionWindowPos pfnPosition, GetTextBoxAreaPos pfnTextArea);
|
||||
void DeactivateTextServices();
|
||||
BOOL NotifyTextServices(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* lplResult);
|
||||
|
||||
|
|
|
@ -54,3 +54,15 @@ RECT GetImeSuggestionWindowPos()
|
|||
|
||||
return rcSuggestion;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - This method gives a rectangle to where text box is currently rendered
|
||||
// such that the touch keyboard can pop up when the rectangle is tapped.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Rectangle specifying current text box area.
|
||||
RECT GetTextBoxArea()
|
||||
{
|
||||
return Microsoft::Console::Interactivity::ServiceLocator::LocateConsoleWindow()->GetWindowRect();
|
||||
}
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
#pragma hdrstop
|
||||
|
||||
RECT GetImeSuggestionWindowPos();
|
||||
RECT GetTextBoxArea();
|
||||
|
|
|
@ -263,7 +263,7 @@ using namespace Microsoft::Console::Types;
|
|||
HandleFocusEvent(TRUE);
|
||||
|
||||
// ActivateTextServices does nothing if already active so this is OK to be called every focus.
|
||||
ActivateTextServices(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), GetImeSuggestionWindowPos);
|
||||
ActivateTextServices(ServiceLocator::LocateConsoleWindow()->GetWindowHandle(), GetImeSuggestionWindowPos, GetTextBoxArea);
|
||||
|
||||
// set the text area to have focus for accessibility consumers
|
||||
if (_pUiaProvider)
|
||||
|
|
|
@ -33,9 +33,11 @@ class CConsoleTSF final :
|
|||
{
|
||||
public:
|
||||
CConsoleTSF(HWND hwndConsole,
|
||||
GetSuggestionWindowPos pfnPosition) :
|
||||
GetSuggestionWindowPos pfnPosition,
|
||||
GetTextBoxAreaPos pfnTextArea) :
|
||||
_hwndConsole(hwndConsole),
|
||||
_pfnPosition(pfnPosition),
|
||||
_pfnTextArea(pfnTextArea),
|
||||
_cRef(1),
|
||||
_tid()
|
||||
{
|
||||
|
@ -66,21 +68,27 @@ public:
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// This returns Rectangle of the text box of whole console.
|
||||
// When a user taps inside the rectangle while hardware keyboard is not available,
|
||||
// touch keyboard is invoked.
|
||||
STDMETHODIMP GetScreenExt(RECT* pRect)
|
||||
{
|
||||
if (pRect)
|
||||
{
|
||||
*pRect = _pfnPosition();
|
||||
*pRect = _pfnTextArea();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// This returns rectangle of current command line edit area.
|
||||
// When a user types in East Asian language, candidate window is shown at this position.
|
||||
// Emoji and more panel (Win+.) is shown at the position, too.
|
||||
STDMETHODIMP GetTextExt(LONG, LONG, RECT* pRect, BOOL* pbClipped)
|
||||
{
|
||||
if (pRect)
|
||||
{
|
||||
GetScreenExt(pRect);
|
||||
*pRect = _pfnPosition();
|
||||
}
|
||||
|
||||
if (pbClipped)
|
||||
|
@ -198,6 +206,7 @@ private:
|
|||
// Console info.
|
||||
HWND _hwndConsole;
|
||||
GetSuggestionWindowPos _pfnPosition;
|
||||
GetTextBoxAreaPos _pfnTextArea;
|
||||
|
||||
// Miscellaneous flags
|
||||
BOOL _fModifyingDoc = FALSE; // Set TRUE, when calls ITfRange::SetText
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
CConsoleTSF* g_pConsoleTSF = nullptr;
|
||||
|
||||
extern "C" BOOL ActivateTextServices(HWND hwndConsole, GetSuggestionWindowPos pfnPosition)
|
||||
extern "C" BOOL ActivateTextServices(HWND hwndConsole, GetSuggestionWindowPos pfnPosition, GetTextBoxAreaPos pfnTextArea)
|
||||
{
|
||||
if (!g_pConsoleTSF && hwndConsole)
|
||||
{
|
||||
g_pConsoleTSF = new (std::nothrow) CConsoleTSF(hwndConsole, pfnPosition);
|
||||
g_pConsoleTSF = new (std::nothrow) CConsoleTSF(hwndConsole, pfnPosition, pfnTextArea);
|
||||
if (g_pConsoleTSF && SUCCEEDED(g_pConsoleTSF->Initialize()))
|
||||
{
|
||||
// Conhost calls this function only when the console window has focus.
|
||||
|
|
|
@ -114,12 +114,6 @@ HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple
|
|||
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
|
||||
*ppUtr = nullptr;
|
||||
|
||||
_pData->LockConsole();
|
||||
auto Unlock = wil::scope_exit([&]() noexcept {
|
||||
_pData->UnlockConsole();
|
||||
});
|
||||
RETURN_HR_IF(E_FAIL, !_pData->IsUiaDataInitialized() || !_pData->IsSelectionActive());
|
||||
|
||||
const auto start = _pData->GetSelectionAnchor();
|
||||
|
||||
// we need to make end exclusive
|
||||
|
|
Loading…
Reference in a new issue