mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-21 06:18:34 +00:00
Compare commits
62 Commits
dev/migrie
...
dev/cazamo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e8be883c6 | ||
|
|
76f8a74022 | ||
|
|
d913907237 | ||
|
|
a47ed99272 | ||
|
|
95b031e27c | ||
|
|
8f9ccc55d1 | ||
|
|
3909cc103a | ||
|
|
8358f8d93f | ||
|
|
48d59e8304 | ||
|
|
83f2a3bb3d | ||
|
|
87fa526fb7 | ||
|
|
c6a31710d9 | ||
|
|
19bd0c94e7 | ||
|
|
629c06d0ad | ||
|
|
1202f89399 | ||
|
|
4a95341caf | ||
|
|
3cf7677d17 | ||
|
|
ac3fecb134 | ||
|
|
5aaf5b4d59 | ||
|
|
2961a104af | ||
|
|
7b1a660e59 | ||
|
|
cb03b97e67 | ||
|
|
f26c246c7c | ||
|
|
649c546960 | ||
|
|
a930aa390e | ||
|
|
d1bf0fcd1e | ||
|
|
66033dcb01 | ||
|
|
95f63a9100 | ||
|
|
6654f0d155 | ||
|
|
35e1168bfa | ||
|
|
ef4f2ca03e | ||
|
|
f87596f8f7 | ||
|
|
17c6f8e9ff | ||
|
|
81d773d2bf | ||
|
|
8ad4d1f19a | ||
|
|
99ffaa8a1a | ||
|
|
c9dea60bbe | ||
|
|
a158cc81ae | ||
|
|
049e37e514 | ||
|
|
eb0fb3e822 | ||
|
|
dfeb855d18 | ||
|
|
ba8bd006f4 | ||
|
|
177430272c | ||
|
|
69318d3ba1 | ||
|
|
ce99c2a349 | ||
|
|
e6aa902224 | ||
|
|
d12a71cdf9 | ||
|
|
bb0e1d3979 | ||
|
|
2c22b68e15 | ||
|
|
654c0cc286 | ||
|
|
c07553cb57 | ||
|
|
491cb21722 | ||
|
|
90699da23b | ||
|
|
eb349935a0 | ||
|
|
4c53c595e7 | ||
|
|
72cbe59078 | ||
|
|
ce0505073c | ||
|
|
847749f19e | ||
|
|
52d1533c4f | ||
|
|
ac3e4bfe56 | ||
|
|
12eb69f665 | ||
|
|
00d1dc99e4 |
13
.vscode/extensions.json
vendored
Normal file
13
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-vscode.cpptools"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
]
|
||||
}
|
||||
24
.vscode/launch.json
vendored
Normal file
24
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug OpenConsole by Launching (x64, debug)",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}\\bin\\x64\\debug\\openconsole.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
},
|
||||
{
|
||||
"name": "Debug Terminal by Attaching (You go build/register/launch it first.)",
|
||||
"type": "cppvsdbg",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
32
.vscode/settings.json
vendored
Normal file
32
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"C_Cpp.default.browse.databaseFilename": "${workspaceFolder}\\.vscode\\.BROWSE.VC.DB",
|
||||
"C_Cpp.default.browse.path": [
|
||||
"${workspaceFolder}"
|
||||
],
|
||||
"C_Cpp.loggingLevel": "None",
|
||||
"files.associations": {
|
||||
"xstring": "cpp",
|
||||
"*.idl": "cpp",
|
||||
"array": "cpp",
|
||||
"future": "cpp",
|
||||
"istream": "cpp",
|
||||
"memory": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"variant": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xlocmon": "cpp",
|
||||
"xlocnum": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"multi_span": "cpp",
|
||||
"pointers": "cpp",
|
||||
"vector": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/bin/**": true,
|
||||
"**/obj/**": true,
|
||||
"**/packages/**": true,
|
||||
"**/generated files/**": true
|
||||
}
|
||||
}
|
||||
107
.vscode/tasks.json
vendored
Normal file
107
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Build Terminal/Console",
|
||||
"command": "powershell.exe",
|
||||
"args": [
|
||||
"-Command",
|
||||
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
|
||||
"Set-MsBuildDevEnvironment;",
|
||||
"$project = switch(\"${input:buildProjectChoice}\"){OpenConsole{\"Conhost\\Host_EXE\"} Terminal{\"Terminal\\CascadiaPackage\"}};",
|
||||
"$target = switch(\"${input:buildModeChoice}\"){Build{\"\"} Rebuild{\":Rebuild\"} Clean{\":Clean\"}};",
|
||||
"$target = $project + $target;",
|
||||
"msbuild",
|
||||
"${workspaceFolder}\\OpenConsole.sln",
|
||||
"/p:Configuration=${input:configChoice}",
|
||||
"/p:Platform=${input:platformChoice}",
|
||||
"/t:$target",
|
||||
"/verbosity:minimal"
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"runOptions": {
|
||||
"reevaluateOnRerun": false,
|
||||
"instanceLimit": 1,
|
||||
"runOn": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Register Windows Terminal x64 Debug",
|
||||
"command": "powershell.exe",
|
||||
"args": [
|
||||
"-Command",
|
||||
"Import-Module ${workspaceFolder}\\tools\\OpenConsole.psm1;",
|
||||
"Set-MsBuildDevEnvironment;",
|
||||
"Set-Location -Path ${workspaceFolder}\\src\\cascadia\\CascadiaPackage\\AppPackages\\CascadiaPackage_0.0.1.0_x64_Debug_Test;",
|
||||
"if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) { Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe'};",
|
||||
"New-Item ..\\loose -Type Directory -Force;",
|
||||
"makeappx unpack /v /o /p .\\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\\Loose\\;",
|
||||
"Add-AppxPackage -Path ..\\loose\\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown"
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Run Windows Terminal Dev",
|
||||
"command": "wtd.exe",
|
||||
"args": [
|
||||
],
|
||||
"problemMatcher": ["$msCompile"],
|
||||
}
|
||||
],
|
||||
"inputs":[
|
||||
{
|
||||
"id": "platformChoice",
|
||||
"type": "pickString",
|
||||
"description": "Processor architecture choice",
|
||||
"options":[
|
||||
"x64",
|
||||
"x86",
|
||||
"arm64"
|
||||
],
|
||||
"default": "x64"
|
||||
},
|
||||
{
|
||||
"id": "configChoice",
|
||||
"type": "pickString",
|
||||
"description": "Debug or release?",
|
||||
"options":[
|
||||
"Debug",
|
||||
"Release"
|
||||
],
|
||||
"default": "Debug"
|
||||
},
|
||||
{
|
||||
"id": "buildModeChoice",
|
||||
"type": "pickString",
|
||||
"description": "Build, rebuild, or clean?",
|
||||
"options":[
|
||||
"Build",
|
||||
"Rebuild",
|
||||
"Clean"
|
||||
],
|
||||
"default": "Build"
|
||||
},
|
||||
{
|
||||
"id": "buildProjectChoice",
|
||||
"type": "pickString",
|
||||
"description": "OpenConsole or Terminal?",
|
||||
"options":[
|
||||
"OpenConsole",
|
||||
"Terminal"
|
||||
],
|
||||
"default": "Terminal"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
@@ -2410,8 +2410,6 @@ Global
|
||||
{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x86.ActiveCfg = Release|Win32
|
||||
{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x86.Build.0 = Release|Win32
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.Build.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM.ActiveCfg = Debug|ARM
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM.Build.0 = Debug|ARM
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM.Deploy.0 = Debug|ARM
|
||||
@@ -2419,11 +2417,7 @@ Global
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.Build.0 = Debug|ARM64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.Deploy.0 = Debug|ARM64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x64Test.ActiveCfg = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x64Test.Build.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x64Test.Deploy.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x86Test.ActiveCfg = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x86Test.Build.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|DotNet_x86Test.Deploy.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.ActiveCfg = Debug|x64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.Build.0 = Debug|x64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.Deploy.0 = Debug|x64
|
||||
@@ -2431,8 +2425,6 @@ Global
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.Build.0 = Debug|x86
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.Deploy.0 = Debug|x86
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
@@ -2440,11 +2432,7 @@ Global
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x64Test.ActiveCfg = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x64Test.Build.0 = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x64Test.Deploy.0 = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x86Test.ActiveCfg = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x86Test.Build.0 = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|DotNet_x86Test.Deploy.0 = Debug|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.Build.0 = Debug|x64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.Deploy.0 = Debug|x64
|
||||
@@ -2452,8 +2440,6 @@ Global
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.Build.0 = Debug|x86
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM.Build.0 = Release|ARM
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM.Deploy.0 = Release|ARM
|
||||
@@ -2461,11 +2447,7 @@ Global
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x64Test.ActiveCfg = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x64Test.Build.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x64Test.Deploy.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x86Test.ActiveCfg = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x86Test.Build.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|DotNet_x86Test.Deploy.0 = Release|Any CPU
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.ActiveCfg = Release|x64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.Build.0 = Release|x64
|
||||
{F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.Deploy.0 = Release|x64
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>7</VersionMinor>
|
||||
<VersionMinor>8</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -76,9 +76,11 @@
|
||||
"copy",
|
||||
"duplicateTab",
|
||||
"find",
|
||||
"findMatch",
|
||||
"moveFocus",
|
||||
"moveTab",
|
||||
"newTab",
|
||||
"newWindow",
|
||||
"nextTab",
|
||||
"openNewTabDropdown",
|
||||
"openSettings",
|
||||
@@ -138,6 +140,13 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"FindMatchDirection": {
|
||||
"enum": [
|
||||
"next",
|
||||
"prev"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SplitState": {
|
||||
"enum": [
|
||||
"vertical",
|
||||
@@ -213,6 +222,11 @@
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": null,
|
||||
"description": "If provided, will set the tab's color to the given value"
|
||||
},
|
||||
"suppressApplicationTitle": {
|
||||
"type": "boolean",
|
||||
"default": "false",
|
||||
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -559,6 +573,35 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"FindMatchAction": {
|
||||
"description": "Arguments corresponding to a Find Match Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "findMatch" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/FindMatchDirection",
|
||||
"default": "prev",
|
||||
"description": "The direction to search in. \"prev\" will search upwards in the buffer, and \"next\" will search downwards."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"NewWindowAction": {
|
||||
"description": "Arguments corresponding to a New Window Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{ "$ref": "#/definitions/NewTerminalArgs" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type":"string", "pattern": "newWindow" }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -583,6 +626,8 @@
|
||||
{ "$ref": "#/definitions/ScrollUpAction" },
|
||||
{ "$ref": "#/definitions/ScrollDownAction" },
|
||||
{ "$ref": "#/definitions/MoveTabAction" },
|
||||
{ "$ref": "#/definitions/FindMatchAction" },
|
||||
{ "$ref": "#/definitions/NewWindowAction" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
@@ -630,6 +675,16 @@
|
||||
"description": "When set to true, tabs are always displayed. When set to false and \"showTabsInTitlebar\" is set to false, tabs only appear after opening a new tab.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"centerOnLaunch": {
|
||||
"default": false,
|
||||
"description": "When set to `true`, the terminal window will auto-center itself on the display it opens on. The terminal will use the \"initialPosition\" to determine which display to open on.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"inputServiceWarning": {
|
||||
"default": true,
|
||||
"description": "Warning if 'Touch Keyboard and Handwriting Panel Service' is disabled.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyOnSelect": {
|
||||
"default": false,
|
||||
"description": "When set to true, a selection is immediately copied to your clipboard upon creation. When set to false, the selection persists and awaits further action.",
|
||||
@@ -812,6 +867,16 @@
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"windowingBehavior": {
|
||||
"default": "useNew",
|
||||
"description": "Controls how new terminal instances attach to existing windows. \"useNew\" will always create a new window. \"useExisting\" will create new tabs in the most recently used window on this virtual desktop, and \"useAnyExisting\" will create tabs in the most recent window on any desktop.",
|
||||
"enum": [
|
||||
"useNew",
|
||||
"useExisting",
|
||||
"useAnyExisting"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -933,9 +998,9 @@
|
||||
"description": "Sets the color of the cursor. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorHeight": {
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.",
|
||||
"maximum": 100,
|
||||
"minimum": 25,
|
||||
"minimum": 1,
|
||||
"type": ["integer","null"],
|
||||
"default": 25
|
||||
},
|
||||
|
||||
BIN
res/Cascadia.ttf
BIN
res/Cascadia.ttf
Binary file not shown.
Binary file not shown.
@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2102.03)
|
||||
* from microsoft/cascadia-code@b358d1ba3d1629c113671312b18eab52797cc055
|
||||
* Cascadia Code, Cascadia Mono (2102.25)
|
||||
* from microsoft/cascadia-code@911dc421f333e3b72b97381d16fee5b71eb48f04
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 13H16V6H2C0.9 6 0 6.9 0 8V13Z" fill="#CCCCCC"/>
|
||||
<path d="M32 6H16V13H32V6Z" fill="#999999"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.6 KiB |
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="foreground"><stop stop-color="#000000"/></linearGradient>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
36
src/buffer/out/LineRendition.hpp
Normal file
36
src/buffer/out/LineRendition.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- LineRendition.hpp
|
||||
|
||||
Abstract:
|
||||
- Enumerated type for the VT line rendition attribute. This determines the
|
||||
width and height scaling with which each line is rendered.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class LineRendition
|
||||
{
|
||||
SingleWidth,
|
||||
DoubleWidth,
|
||||
DoubleHeightTop,
|
||||
DoubleHeightBottom
|
||||
};
|
||||
|
||||
constexpr SMALL_RECT ScreenToBufferLine(const SMALL_RECT& line, const LineRendition lineRendition)
|
||||
{
|
||||
// Use shift right to quickly divide the Left and Right by 2 for double width lines.
|
||||
const auto scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
|
||||
return { line.Left >> scale, line.Top, line.Right >> scale, line.Bottom };
|
||||
}
|
||||
|
||||
constexpr SMALL_RECT BufferToScreenLine(const SMALL_RECT& line, const LineRendition lineRendition)
|
||||
{
|
||||
// Use shift left to quickly multiply the Left and Right by 2 for double width lines.
|
||||
const SHORT scale = lineRendition == LineRendition::SingleWidth ? 0 : 1;
|
||||
return { line.Left << scale, line.Top, (line.Right << scale) + scale, line.Bottom };
|
||||
}
|
||||
@@ -21,6 +21,7 @@ ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute f
|
||||
_rowWidth{ rowWidth },
|
||||
_charRow{ rowWidth, this },
|
||||
_attrRow{ rowWidth, fillAttribute },
|
||||
_lineRendition{ LineRendition::SingleWidth },
|
||||
_wrapForced{ false },
|
||||
_doubleBytePadded{ false },
|
||||
_pParent{ pParent }
|
||||
@@ -35,6 +36,7 @@ ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute f
|
||||
// - <none>
|
||||
bool ROW::Reset(const TextAttribute Attr)
|
||||
{
|
||||
_lineRendition = LineRendition::SingleWidth;
|
||||
_wrapForced = false;
|
||||
_doubleBytePadded = false;
|
||||
_charRow.Reset();
|
||||
|
||||
@@ -21,6 +21,7 @@ Revision History:
|
||||
#pragma once
|
||||
|
||||
#include "AttrRow.hpp"
|
||||
#include "LineRendition.hpp"
|
||||
#include "OutputCell.hpp"
|
||||
#include "OutputCellIterator.hpp"
|
||||
#include "CharRow.hpp"
|
||||
@@ -48,6 +49,9 @@ public:
|
||||
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
|
||||
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
|
||||
|
||||
LineRendition GetLineRendition() const noexcept { return _lineRendition; }
|
||||
void SetLineRendition(const LineRendition lineRendition) noexcept { _lineRendition = lineRendition; }
|
||||
|
||||
SHORT GetId() const noexcept { return _id; }
|
||||
void SetId(const SHORT id) noexcept { _id = id; }
|
||||
|
||||
@@ -70,6 +74,7 @@ public:
|
||||
private:
|
||||
CharRow _charRow;
|
||||
ATTR_ROW _attrRow;
|
||||
LineRendition _lineRendition;
|
||||
SHORT _id;
|
||||
unsigned short _rowWidth;
|
||||
// Occurs when the user runs out of text in a given row and we're forced to wrap the cursor to the next line
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<ClInclude Include="..\cursor.h" />
|
||||
<ClInclude Include="..\DbcsAttribute.hpp" />
|
||||
<ClInclude Include="..\ICharRow.hpp" />
|
||||
<ClInclude Include="..\LineRendition.hpp" />
|
||||
<ClInclude Include="..\OutputCell.hpp" />
|
||||
<ClInclude Include="..\OutputCellIterator.hpp" />
|
||||
<ClInclude Include="..\OutputCellRect.hpp" />
|
||||
|
||||
@@ -97,7 +97,12 @@ bool Search::FindNext()
|
||||
// - Takes the found word and selects it in the screen buffer
|
||||
void Search::Select() const
|
||||
{
|
||||
_uiaData.SelectNewRegion(_coordSelStart, _coordSelEnd);
|
||||
// Convert buffer selection offsets into the equivalent screen coordinates
|
||||
// required by SelectNewRegion, taking line renditions into account.
|
||||
const auto& textBuffer = _uiaData.GetTextBuffer();
|
||||
const auto selStart = textBuffer.BufferToScreenPosition(_coordSelStart);
|
||||
const auto selEnd = textBuffer.BufferToScreenPosition(_coordSelEnd);
|
||||
_uiaData.SelectNewRegion(selStart, selEnd);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -141,7 +146,10 @@ COORD Search::s_GetInitialAnchor(IUiaData& uiaData, const Direction direction)
|
||||
const COORD textBufferEndPosition = uiaData.GetTextBufferEndPosition();
|
||||
if (uiaData.IsSelectionActive())
|
||||
{
|
||||
auto anchor = uiaData.GetSelectionAnchor();
|
||||
// Convert the screen position of the selection anchor into an equivalent
|
||||
// buffer position to start searching, taking line rendition into account.
|
||||
auto anchor = textBuffer.ScreenToBufferPosition(uiaData.GetSelectionAnchor());
|
||||
|
||||
if (direction == Direction::Forward)
|
||||
{
|
||||
textBuffer.GetSize().IncrementInBoundsCircular(anchor);
|
||||
|
||||
@@ -290,13 +290,14 @@ bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute
|
||||
// We only need to compensate for leading bytes
|
||||
if (dbcsAttribute.IsLeading())
|
||||
{
|
||||
short const sBufferWidth = GetSize().Width();
|
||||
const auto cursorPosition = GetCursor().GetPosition();
|
||||
const auto lineWidth = GetLineWidth(cursorPosition.Y);
|
||||
|
||||
// If we're about to lead on the last column in the row, we need to add a padding space
|
||||
if (GetCursor().GetPosition().X == sBufferWidth - 1)
|
||||
if (cursorPosition.X == lineWidth - 1)
|
||||
{
|
||||
// set that we're wrapping for double byte reasons
|
||||
auto& row = GetRowByOffset(GetCursor().GetPosition().Y);
|
||||
auto& row = GetRowByOffset(cursorPosition.Y);
|
||||
row.SetDoubleBytePadded(true);
|
||||
|
||||
// then move the cursor forward and onto the next row
|
||||
@@ -496,7 +497,7 @@ bool TextBuffer::IncrementCursor()
|
||||
// Cursor position is stored as logical array indices (starts at 0) for the window
|
||||
// Buffer Size is specified as the "length" of the array. It would say 80 for valid values of 0-79.
|
||||
// So subtract 1 from buffer size in each direction to find the index of the final column in the buffer
|
||||
const short iFinalColumnIndex = GetSize().RightInclusive();
|
||||
const short iFinalColumnIndex = GetLineWidth(GetCursor().GetPosition().Y) - 1;
|
||||
|
||||
// Move the cursor one position to the right
|
||||
GetCursor().IncrementXPosition(1);
|
||||
@@ -635,7 +636,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(std::optional<const Microsoft::Consol
|
||||
// Return Value:
|
||||
// - Coordinate position in screen coordinates of the character just before the cursor.
|
||||
// - NOTE: Will return 0,0 if already in the top left corner
|
||||
COORD TextBuffer::_GetPreviousFromCursor() const noexcept
|
||||
COORD TextBuffer::_GetPreviousFromCursor() const
|
||||
{
|
||||
COORD coordPosition = GetCursor().GetPosition();
|
||||
|
||||
@@ -649,11 +650,11 @@ COORD TextBuffer::_GetPreviousFromCursor() const noexcept
|
||||
// Otherwise, only if we're not on the top row (e.g. we don't move anywhere in the top left corner. there is no previous)
|
||||
if (coordPosition.Y > 0)
|
||||
{
|
||||
// move the cursor to the right edge
|
||||
coordPosition.X = GetSize().RightInclusive();
|
||||
|
||||
// and up one line
|
||||
// move the cursor up one line
|
||||
coordPosition.Y--;
|
||||
|
||||
// and to the right edge
|
||||
coordPosition.X = GetLineWidth(coordPosition.Y) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,6 +802,78 @@ void TextBuffer::SetCurrentAttributes(const TextAttribute& currentAttributes) no
|
||||
_currentAttributes = currentAttributes;
|
||||
}
|
||||
|
||||
void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
|
||||
{
|
||||
const auto cursorPosition = GetCursor().GetPosition();
|
||||
const auto rowIndex = cursorPosition.Y;
|
||||
auto& row = GetRowByOffset(rowIndex);
|
||||
if (row.GetLineRendition() != lineRendition)
|
||||
{
|
||||
row.SetLineRendition(lineRendition);
|
||||
// If the line rendition has changed, the row can no longer be wrapped.
|
||||
row.SetWrapForced(false);
|
||||
// And if it's no longer single width, the right half of the row should be erased.
|
||||
if (lineRendition != LineRendition::SingleWidth)
|
||||
{
|
||||
const auto fillChar = L' ';
|
||||
auto fillAttrs = GetCurrentAttributes();
|
||||
fillAttrs.SetStandardErase();
|
||||
const size_t fillOffset = GetLineWidth(rowIndex);
|
||||
const size_t fillLength = GetSize().Width() - fillOffset;
|
||||
const auto fillData = OutputCellIterator{ fillChar, fillAttrs, fillLength };
|
||||
row.WriteCells(fillData, fillOffset, false);
|
||||
// We also need to make sure the cursor is clamped within the new width.
|
||||
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
|
||||
}
|
||||
_NotifyPaint(Viewport::FromDimensions({ 0, rowIndex }, { GetSize().Width(), 1 }));
|
||||
}
|
||||
}
|
||||
|
||||
void TextBuffer::ResetLineRenditionRange(const size_t startRow, const size_t endRow)
|
||||
{
|
||||
for (auto row = startRow; row < endRow; row++)
|
||||
{
|
||||
GetRowByOffset(row).SetLineRendition(LineRendition::SingleWidth);
|
||||
}
|
||||
}
|
||||
|
||||
LineRendition TextBuffer::GetLineRendition(const size_t row) const
|
||||
{
|
||||
return GetRowByOffset(row).GetLineRendition();
|
||||
}
|
||||
|
||||
bool TextBuffer::IsDoubleWidthLine(const size_t row) const
|
||||
{
|
||||
return GetLineRendition(row) != LineRendition::SingleWidth;
|
||||
}
|
||||
|
||||
SHORT TextBuffer::GetLineWidth(const size_t row) const
|
||||
{
|
||||
// Use shift right to quickly divide the width by 2 for double width lines.
|
||||
const auto scale = IsDoubleWidthLine(row) ? 1 : 0;
|
||||
return GetSize().Width() >> scale;
|
||||
}
|
||||
|
||||
COORD TextBuffer::ClampPositionWithinLine(const COORD position) const
|
||||
{
|
||||
const SHORT rightmostColumn = GetLineWidth(position.Y) - 1;
|
||||
return { std::min(position.X, rightmostColumn), position.Y };
|
||||
}
|
||||
|
||||
COORD TextBuffer::ScreenToBufferPosition(const COORD position) const
|
||||
{
|
||||
// Use shift right to quickly divide the X pos by 2 for double width lines.
|
||||
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
|
||||
return { position.X >> scale, position.Y };
|
||||
}
|
||||
|
||||
COORD TextBuffer::BufferToScreenPosition(const COORD position) const
|
||||
{
|
||||
// Use shift left to quickly multiply the X pos by 2 for double width lines.
|
||||
const auto scale = IsDoubleWidthLine(position.Y) ? 1 : 0;
|
||||
return { position.X << scale, position.Y };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Resets the text contents of this buffer with the default character
|
||||
// and the default current color attributes
|
||||
@@ -1425,9 +1498,11 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos) const
|
||||
// - blockSelection: when enabled, only get the rectangular text region,
|
||||
// as opposed to the text extending to the left/right
|
||||
// buffer margins
|
||||
// - bufferCoordinates: when enabled, treat the coordinates as relative to
|
||||
// the buffer rather than the screen.
|
||||
// Return Value:
|
||||
// - the delimiter class for the given char
|
||||
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection) const
|
||||
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const
|
||||
{
|
||||
std::vector<SMALL_RECT> textRects;
|
||||
|
||||
@@ -1461,6 +1536,13 @@ const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, b
|
||||
textRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : bufferSize.RightInclusive();
|
||||
}
|
||||
|
||||
// If we were passed screen coordinates, convert the given range into
|
||||
// equivalent buffer offsets, taking line rendition into account.
|
||||
if (!bufferCoordinates)
|
||||
{
|
||||
textRow = ScreenToBufferLine(textRow, GetLineRendition(row));
|
||||
}
|
||||
|
||||
_ExpandTextRow(textRow);
|
||||
textRects.emplace_back(textRow);
|
||||
}
|
||||
@@ -2044,7 +2126,6 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
|
||||
const COORD cOldLastChar = oldBuffer.GetLastNonSpaceCharacter(lastCharacterViewport);
|
||||
|
||||
const short cOldRowsTotal = cOldLastChar.Y + 1;
|
||||
const short cOldColsTotal = oldBuffer.GetSize().Width();
|
||||
|
||||
COORD cNewCursorPos = { 0 };
|
||||
bool fFoundCursorPos = false;
|
||||
@@ -2056,9 +2137,19 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
|
||||
{
|
||||
// Fetch the row and its "right" which is the last printable character.
|
||||
const ROW& row = oldBuffer.GetRowByOffset(iOldRow);
|
||||
const short cOldColsTotal = oldBuffer.GetLineWidth(iOldRow);
|
||||
const CharRow& charRow = row.GetCharRow();
|
||||
short iRight = gsl::narrow_cast<short>(charRow.MeasureRight());
|
||||
|
||||
// If we're starting a new row, try and preserve the line rendition
|
||||
// from the row in the original buffer.
|
||||
const auto newBufferPos = newBuffer.GetCursor().GetPosition();
|
||||
if (newBufferPos.X == 0)
|
||||
{
|
||||
auto& newRow = newBuffer.GetRowByOffset(newBufferPos.Y);
|
||||
newRow.SetLineRendition(row.GetLineRendition());
|
||||
}
|
||||
|
||||
// There is a special case here. If the row has a "wrap"
|
||||
// flag on it, but the right isn't equal to the width (one
|
||||
// index past the final valid index in the row) then there
|
||||
|
||||
@@ -122,6 +122,16 @@ public:
|
||||
|
||||
void SetCurrentAttributes(const TextAttribute& currentAttributes) noexcept;
|
||||
|
||||
void SetCurrentLineRendition(const LineRendition lineRendition);
|
||||
void ResetLineRenditionRange(const size_t startRow, const size_t endRow);
|
||||
LineRendition GetLineRendition(const size_t row) const;
|
||||
bool IsDoubleWidthLine(const size_t row) const;
|
||||
|
||||
SHORT GetLineWidth(const size_t row) const;
|
||||
COORD ClampPositionWithinLine(const COORD position) const;
|
||||
COORD ScreenToBufferPosition(const COORD position) const;
|
||||
COORD BufferToScreenPosition(const COORD position) const;
|
||||
|
||||
void Reset();
|
||||
|
||||
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
|
||||
@@ -141,7 +151,7 @@ public:
|
||||
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
||||
bool MoveToPreviousGlyph(til::point& pos) const;
|
||||
|
||||
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
|
||||
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection, bool bufferCoordinates) const;
|
||||
|
||||
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
|
||||
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
|
||||
@@ -212,7 +222,7 @@ private:
|
||||
|
||||
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
|
||||
|
||||
COORD _GetPreviousFromCursor() const noexcept;
|
||||
COORD _GetPreviousFromCursor() const;
|
||||
|
||||
void _SetWrapOnCurrentRow();
|
||||
void _AdjustWrapOnCurrentRow(const bool fSet);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
|
||||
IgnorableNamespaces="uap mp rescap">
|
||||
IgnorableNamespaces="uap mp rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="WindowsTerminalDev"
|
||||
@@ -69,6 +69,11 @@
|
||||
Enabled="false"
|
||||
DisplayName="ms-resource:AppNameDev" />
|
||||
</uap5:Extension>
|
||||
<uap3:Extension Category="windows.appExtensionHost">
|
||||
<uap3:AppExtensionHost>
|
||||
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
|
||||
</uap3:AppExtensionHost>
|
||||
</uap3:Extension>
|
||||
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap mp rescap">
|
||||
IgnorableNamespaces="uap mp rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="Microsoft.WindowsTerminalPreview"
|
||||
@@ -64,6 +64,11 @@
|
||||
<desktop:ExecutionAlias Alias="wt.exe" />
|
||||
</uap3:AppExecutionAlias>
|
||||
</uap3:Extension>
|
||||
<uap3:Extension Category="windows.appExtensionHost">
|
||||
<uap3:AppExtensionHost>
|
||||
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
|
||||
</uap3:AppExtensionHost>
|
||||
</uap3:Extension>
|
||||
<uap5:Extension Category="windows.startupTask">
|
||||
<uap5:StartupTask
|
||||
TaskId="StartTerminalOnLoginTask"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap mp rescap">
|
||||
IgnorableNamespaces="uap mp rescap uap3">
|
||||
|
||||
<Identity
|
||||
Name="Microsoft.WindowsTerminal"
|
||||
@@ -64,6 +64,11 @@
|
||||
<desktop:ExecutionAlias Alias="wt.exe" />
|
||||
</uap3:AppExecutionAlias>
|
||||
</uap3:Extension>
|
||||
<uap3:Extension Category="windows.appExtensionHost">
|
||||
<uap3:AppExtensionHost>
|
||||
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
|
||||
</uap3:AppExtensionHost>
|
||||
</uap3:Extension>
|
||||
<uap5:Extension Category="windows.startupTask">
|
||||
<uap5:StartupTask
|
||||
TaskId="StartTerminalOnLoginTask"
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace SettingsModelLocalTests
|
||||
TEST_METHOD(TestAutogeneratedName);
|
||||
TEST_METHOD(TestLayerOnAutogeneratedName);
|
||||
|
||||
TEST_METHOD(TestGenerateCommandline);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
@@ -275,14 +277,6 @@ namespace SettingsModelLocalTests
|
||||
|
||||
void CommandTests::TestAutogeneratedName()
|
||||
{
|
||||
// Tests run in Helix can't report Skipped until GH#7286 is resolved.
|
||||
// Set ignore flag to make Helix run completely overlook it.
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Ignore", L"True")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
// This test to be corrected as a part of GH#7281
|
||||
|
||||
// This test ensures that we'll correctly create commands for actions
|
||||
// that don't have given names, pursuant to the spec in GH#6532.
|
||||
|
||||
@@ -369,4 +363,145 @@ namespace SettingsModelLocalTests
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandTests::TestGenerateCommandline()
|
||||
{
|
||||
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;
|
||||
|
||||
const std::string commands0String{ R"([
|
||||
{
|
||||
"name":"action0",
|
||||
"command": { "action": "newWindow" }
|
||||
},
|
||||
{
|
||||
"name":"action1",
|
||||
"command": { "action": "newTab", "profile": "foo" }
|
||||
},
|
||||
{
|
||||
"name":"action2",
|
||||
"command": { "action": "newWindow", "profile": "foo" }
|
||||
},
|
||||
{
|
||||
"name":"action3",
|
||||
"command": { "action": "newWindow", "commandline": "bar.exe" }
|
||||
},
|
||||
{
|
||||
"name":"action4",
|
||||
"command": { "action": "newWindow", "commandline": "pop.exe ya ha ha" }
|
||||
},
|
||||
{
|
||||
"name":"action5",
|
||||
"command": { "action": "newWindow", "commandline": "pop.exe \"ya ha ha\"" }
|
||||
},
|
||||
{
|
||||
"name":"action6",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\foo", "commandline": "bar.exe" }
|
||||
},
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(7u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action0");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
VERIFY_ARE_EQUAL(L"", cmdline);
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action1");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline);
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action2");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
VERIFY_ARE_EQUAL(L"--profile \"foo\"", cmdline);
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action3");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
VERIFY_ARE_EQUAL(L"-- \"bar.exe\"", cmdline);
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action4");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"-- \"pop.exe ya ha ha\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action5");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"-- \"pop.exe \"ya ha ha\"\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action6");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\foo\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace SettingsModelLocalTests
|
||||
"initialPosition": ",",
|
||||
"launchMode": "default",
|
||||
"alwaysOnTop": false,
|
||||
|
||||
"inputServiceWarning": true,
|
||||
"copyOnSelect": false,
|
||||
"copyFormatting": "all",
|
||||
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
|
||||
|
||||
@@ -34,6 +34,7 @@ Author(s):
|
||||
#include <WexTestClass.h>
|
||||
#include <json.h>
|
||||
#include "consoletaeftemplates.hpp"
|
||||
#include "winrtTaefTemplates.hpp"
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||
|
||||
@@ -6,16 +6,24 @@
|
||||
|
||||
#include "../types/inc/utils.hpp"
|
||||
#include "../TerminalApp/TerminalPage.h"
|
||||
#include "../TerminalApp/AppLogic.h"
|
||||
#include "../TerminalApp/AppCommandlineArgs.h"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace appImpl = TerminalApp::implementation;
|
||||
}
|
||||
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
@@ -64,6 +72,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TEST_METHOD(TestMultipleSplitPaneSizes);
|
||||
|
||||
TEST_METHOD(TestFindTargetWindow);
|
||||
TEST_METHOD(TestFindTargetWindowHelp);
|
||||
TEST_METHOD(TestFindTargetWindowVersion);
|
||||
|
||||
private:
|
||||
void _buildCommandlinesHelper(AppCommandlineArgs& appArgs,
|
||||
const size_t expectedSubcommands,
|
||||
@@ -620,7 +632,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", subcommand, L"--tabColor", L"#009999" };
|
||||
const auto expectedColor = Microsoft::Console::Utils::ColorFromHexString("#009999");
|
||||
const auto expectedColor = ::Microsoft::Console::Utils::ColorFromHexString("#009999");
|
||||
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
@@ -1578,4 +1590,106 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::TestFindTargetWindow()
|
||||
{
|
||||
{
|
||||
std::vector<winrt::hstring> args{ L"wt.exe" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
}
|
||||
{
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-1" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
}
|
||||
{
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"-12345" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
}
|
||||
{
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"-w", L"0" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
}
|
||||
{
|
||||
std::vector<winrt::hstring> args{ L"wt.exe", L"new-tab" };
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
}
|
||||
}
|
||||
|
||||
void CommandlineTest::TestFindTargetWindowHelp()
|
||||
{
|
||||
Log::Comment(L"--help should always create a new window");
|
||||
|
||||
// This is a little helper to make sure that these args _always_ return
|
||||
// UseNew, regardless of the windowing behavior.
|
||||
auto testHelper = [](auto&& args) {
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
};
|
||||
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"-w", L"0", L"new-tab", L"--help" });
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"new-tab", L";", L"--help" });
|
||||
}
|
||||
|
||||
void CommandlineTest::TestFindTargetWindowVersion()
|
||||
{
|
||||
Log::Comment(L"--version should always create a new window");
|
||||
|
||||
// This is a little helper to make sure that these args _always_ return
|
||||
// UseNew, regardless of the windowing behavior.
|
||||
auto testHelper = [](auto&& args) {
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting));
|
||||
|
||||
VERIFY_ARE_EQUAL(WindowingBehaviorUseNew,
|
||||
appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting));
|
||||
};
|
||||
|
||||
testHelper(std::vector<winrt::hstring>{ L"wt.exe", L"--version" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "pch.h"
|
||||
#include "../TerminalApp/CommandLinePaletteItem.h"
|
||||
#include "../TerminalApp/CommandPalette.h"
|
||||
#include "../CppWinrtTailored.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
@@ -29,187 +30,203 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void FilteredCommandTests::VerifyHighlighting()
|
||||
{
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with no filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with empty filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter equals to the string");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter with first character matching");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"A";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 2u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter with other case");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"a";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 2u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter matching several characters");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"ab";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 4u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAA");
|
||||
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(2).TextSegment(), L"B");
|
||||
VERIFY_IS_TRUE(segments.GetAt(2).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(3).TextSegment(), L"BBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(3).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with non matching filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"abcd";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
auto result = RunOnUIThread([]() {
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with no filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with empty filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter equals to the string");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter with first character matching");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"A";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 2u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter with other case");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"a";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 2u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with filter matching several characters");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"ab";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 4u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"A");
|
||||
VERIFY_IS_TRUE(segments.GetAt(0).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(1).TextSegment(), L"AAAAA");
|
||||
VERIFY_IS_FALSE(segments.GetAt(1).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(2).TextSegment(), L"B");
|
||||
VERIFY_IS_TRUE(segments.GetAt(2).IsHighlighted());
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(3).TextSegment(), L"BBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(3).IsHighlighted());
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing command name segmentation with non matching filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"abcd";
|
||||
auto segments = filteredCommand->_computeHighlightedName().Segments();
|
||||
VERIFY_ARE_EQUAL(segments.Size(), 1u);
|
||||
VERIFY_ARE_EQUAL(segments.GetAt(0).TextSegment(), L"AAAAAABBBBBBCCC");
|
||||
VERIFY_IS_FALSE(segments.GetAt(0).IsHighlighted());
|
||||
}
|
||||
});
|
||||
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void FilteredCommandTests::VerifyWeight()
|
||||
{
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with no filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 0);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with empty filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 0);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter equals to the string");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 30); // 1 point for the first char and 2 points for the 14 consequent ones + 1 point for the beginning of the word
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter with first character matching");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"A";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter with other case");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"a";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter matching several characters");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"ab";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 3); // 1 point for the first char match + 1 point for the beginning of the word + 1 point for the match of "b"
|
||||
}
|
||||
auto result = RunOnUIThread([]() {
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with no filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 0);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with empty filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 0);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter equals to the string");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"AAAAAABBBBBBCCC";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 30); // 1 point for the first char and 2 points for the 14 consequent ones + 1 point for the beginning of the word
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter with first character matching");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"A";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter with other case");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"a";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 2); // 1 point for the first char match + 1 point for the beginning of the word
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing weight of command with filter matching several characters");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"ab";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
auto weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(weight, 3); // 1 point for the first char match + 1 point for the beginning of the word + 1 point for the match of "b"
|
||||
}
|
||||
});
|
||||
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void FilteredCommandTests::VerifyCompare()
|
||||
{
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
|
||||
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"BBBBBCCC") };
|
||||
{
|
||||
Log::Comment(L"Testing comparison of commands with no filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
auto result = RunOnUIThread([]() {
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"AAAAAABBBBBBCCC") };
|
||||
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"BBBBBCCC") };
|
||||
{
|
||||
Log::Comment(L"Testing comparison of commands with no filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
|
||||
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
|
||||
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing comparison of commands with empty filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
filteredCommand->_Weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
|
||||
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing comparison of commands with empty filter");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
filteredCommand->_Weight = filteredCommand->_computeWeight();
|
||||
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
filteredCommand2->_Filter = L"";
|
||||
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
|
||||
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
filteredCommand2->_Filter = L"";
|
||||
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
|
||||
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
|
||||
|
||||
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
|
||||
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing comparison of commands with different weights");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"B";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
filteredCommand->_Weight = filteredCommand->_computeWeight();
|
||||
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
|
||||
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Testing comparison of commands with different weights");
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
filteredCommand->_Filter = L"B";
|
||||
filteredCommand->_HighlightedName = filteredCommand->_computeHighlightedName();
|
||||
filteredCommand->_Weight = filteredCommand->_computeWeight();
|
||||
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
filteredCommand2->_Filter = L"B";
|
||||
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
|
||||
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
filteredCommand2->_Filter = L"B";
|
||||
filteredCommand2->_HighlightedName = filteredCommand2->_computeHighlightedName();
|
||||
filteredCommand2->_Weight = filteredCommand2->_computeWeight();
|
||||
|
||||
VERIFY_IS_TRUE(filteredCommand->Weight() < filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word
|
||||
VERIFY_IS_FALSE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
VERIFY_IS_TRUE(filteredCommand->Weight() < filteredCommand2->Weight()); // Second command gets more points due to the beginning of the word
|
||||
VERIFY_IS_FALSE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
});
|
||||
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
void FilteredCommandTests::VerifyCompareIgnoreCase()
|
||||
{
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"a") };
|
||||
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"B") };
|
||||
{
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
auto result = RunOnUIThread([]() {
|
||||
const auto paletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"a") };
|
||||
const auto paletteItem2{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(L"B") };
|
||||
{
|
||||
const auto filteredCommand = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem);
|
||||
const auto filteredCommand2 = winrt::make_self<winrt::TerminalApp::implementation::FilteredCommand>(paletteItem2);
|
||||
|
||||
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
|
||||
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
VERIFY_ARE_EQUAL(filteredCommand->Weight(), filteredCommand2->Weight());
|
||||
VERIFY_IS_TRUE(winrt::TerminalApp::implementation::FilteredCommand::Compare(*filteredCommand, *filteredCommand2));
|
||||
}
|
||||
});
|
||||
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
// * Create a tab with a profile with GUID 1
|
||||
// * Reload the settings so that GUID 1 is no longer in the list of profiles
|
||||
// * Try calling _DuplicateTabViewItem on tab 1
|
||||
// * Try calling _DuplicateFocusedTab on tab 1
|
||||
// * No new tab should be created (and more importantly, the app should not crash)
|
||||
//
|
||||
// Created to test GH#2455
|
||||
@@ -392,7 +392,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Duplicate the first tab");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateTabViewItem();
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -407,7 +407,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
Log::Comment(L"Duplicate the tab, and don't crash");
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_DuplicateTabViewItem();
|
||||
page->_DuplicateFocusedTab();
|
||||
VERIFY_ARE_EQUAL(2u, page->_tabs.Size(), L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -868,13 +868,33 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(4u, page->_mruTabs.Size());
|
||||
|
||||
Log::Comment(L"give alphabetical names to all switch tab actions");
|
||||
RunOnUIThread([&page]() {
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTerminalTabImpl(page->_tabs.GetAt(0))->Title(L"a");
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTerminalTabImpl(page->_tabs.GetAt(1))->Title(L"b");
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTerminalTabImpl(page->_tabs.GetAt(2))->Title(L"c");
|
||||
});
|
||||
TestOnUIThread([&page]() {
|
||||
page->_GetTerminalTabImpl(page->_tabs.GetAt(3))->Title(L"d");
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
Log::Comment(L"Sanity check the titles of our tabs are what we set them to.");
|
||||
|
||||
VERIFY_ARE_EQUAL(L"a", page->_tabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_tabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_tabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_tabs.GetAt(3).Title());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
Log::Comment(L"Change the tab switch order to MRU switching");
|
||||
TestOnUIThread([&page]() {
|
||||
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
|
||||
@@ -897,18 +917,30 @@ namespace TerminalAppLocalTests
|
||||
Log::Comment(L"Switch to the next MRU tab, which is the third tab");
|
||||
RunOnUIThread([&page]() {
|
||||
page->_SelectNextTab(true);
|
||||
// In the course of a single tick, the Command Palette will:
|
||||
// * open
|
||||
// * select the proper tab from the mru's list
|
||||
// * raise an event for _filteredActionsView().SelectionChanged to
|
||||
// immediately preview the new tab
|
||||
// * raise a _SwitchToTabRequestedHandlers event
|
||||
// * then dismiss itself, because we can't fake holing down an
|
||||
// anchor key in the tests
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(L"c", page->_mruTabs.GetAt(0).Title());
|
||||
VERIFY_ARE_EQUAL(L"d", page->_mruTabs.GetAt(1).Title());
|
||||
VERIFY_ARE_EQUAL(L"b", page->_mruTabs.GetAt(2).Title());
|
||||
VERIFY_ARE_EQUAL(L"a", page->_mruTabs.GetAt(3).Title());
|
||||
});
|
||||
|
||||
const auto palette = winrt::get_self<winrt::TerminalApp::implementation::CommandPalette>(page->CommandPalette());
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, palette->_switcherStartIdx, L"Verify the index is 1 as we went right");
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::implementation::CommandPaletteMode::TabSwitchMode, palette->_currentMode, L"Verify we are in the tab switcher mode");
|
||||
|
||||
Log::Comment(L"Verify command palette preserves MRU order of tabs");
|
||||
VERIFY_ARE_EQUAL(4u, palette->_filteredActions.Size());
|
||||
VERIFY_ARE_EQUAL(L"d", palette->_filteredActions.GetAt(0).Item().Name());
|
||||
VERIFY_ARE_EQUAL(L"c", palette->_filteredActions.GetAt(1).Item().Name());
|
||||
VERIFY_ARE_EQUAL(L"b", palette->_filteredActions.GetAt(2).Item().Name());
|
||||
VERIFY_ARE_EQUAL(L"a", palette->_filteredActions.GetAt(3).Item().Name());
|
||||
// At this point, the contents of the command palette's _mruTabs list is
|
||||
// still the _old_ ordering (d, c, b, a). The ordering is only updated
|
||||
// in TerminalPage::_SelectNextTab, but as we saw before, the palette
|
||||
// will also dismiss itself immediately when that's called. So we can't
|
||||
// really inspect the contents of the list in this test, unfortunately.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" IgnorableNamespaces="uap mp uap3">
|
||||
<Identity Name="WindowsTerminal.TestHost" Publisher="CN=Windows Terminal Team" Version="1.0.0.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="fba054a7-f1a1-4cb7-bb21-4949919af2f5" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
@@ -22,6 +22,13 @@
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="taef.png" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap3:Extension Category="windows.appExtensionHost">
|
||||
<uap3:AppExtensionHost>
|
||||
<uap3:Name>com.microsoft.windows.terminal.settings</uap3:Name>
|
||||
</uap3:AppExtensionHost>
|
||||
</uap3:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
</Package>
|
||||
|
||||
@@ -34,6 +34,7 @@ Author(s):
|
||||
#include <WexTestClass.h>
|
||||
#include <json.h>
|
||||
#include "consoletaeftemplates.hpp"
|
||||
#include "winrtTaefTemplates.hpp"
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||
@@ -61,6 +62,8 @@ Author(s):
|
||||
#include <regex>
|
||||
#include <CLI11/CLI11.hpp>
|
||||
|
||||
#include <shobjidl_core.h>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct FindTargetWindowArgs : public FindTargetWindowArgsT<FindTargetWindowArgs>
|
||||
{
|
||||
GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
|
||||
GETSET_PROPERTY(int, ResultTargetWindow, -1);
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr);
|
||||
WINRT_PROPERTY(int, ResultTargetWindow, -1);
|
||||
|
||||
public:
|
||||
FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) :
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
#include "Monarch.h"
|
||||
#include "CommandlineArgs.h"
|
||||
#include "FindTargetWindowArgs.h"
|
||||
@@ -20,6 +21,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
Monarch::Monarch() :
|
||||
_ourPID{ GetCurrentProcessId() }
|
||||
{
|
||||
try
|
||||
{
|
||||
_desktopManager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// This is a private constructor to be used in unit tests, where we don't
|
||||
@@ -39,7 +45,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Add the given peasant to the list of peasants we're tracking. This Peasant may have already been assigned an ID. If it hasn't, then give it an ID.
|
||||
// - Add the given peasant to the list of peasants we're tracking. This
|
||||
// Peasant may have already been assigned an ID. If it hasn't, then give
|
||||
// it an ID.
|
||||
// Arguments:
|
||||
// - peasant: the new Peasant to track.
|
||||
// Return Value:
|
||||
@@ -106,7 +114,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Lookup a peasant by its ID.
|
||||
// - Lookup a peasant by its ID. If the peasant has died, this will also
|
||||
// remove the peasant from our list of peasants.
|
||||
// Arguments:
|
||||
// - peasantID: The ID Of the peasant to find
|
||||
// Return Value:
|
||||
@@ -130,63 +139,239 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
// Remove the peasant from the list of peasants
|
||||
_peasants.erase(peasantID);
|
||||
|
||||
// Remove the peasant from the list of MRU windows. They're dead.
|
||||
// They can't be the MRU anymore.
|
||||
_clearOldMruEntries(peasantID);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc
|
||||
// copy of the WindowActivatedArgs from the peasant. That way, we won't
|
||||
// need to worry about the origin process dying when working with the
|
||||
// WindowActivatedArgs.
|
||||
// - If the peasant process dies while we're making this copy, then we'll
|
||||
// just log it and do nothing. We certainly don't want to track a dead
|
||||
// peasant.
|
||||
// - We'll pass that copy of the WindowActivatedArgs to
|
||||
// _doHandleActivatePeasant, which will actually insert the
|
||||
// WindowActivatedArgs into the list we're using to track the most recent
|
||||
// peasants.
|
||||
// Arguments:
|
||||
// - args: the WindowActivatedArgs describing when and where the peasant was activated.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args)
|
||||
{
|
||||
// TODO:projects/5 Use a heap/priority queue per-desktop to track which
|
||||
// peasant was the most recent per-desktop. When we want to get the most
|
||||
// recent of all desktops (WindowingBehavior::UseExisting), then use the
|
||||
// most recent of all desktops.
|
||||
const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count();
|
||||
const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count();
|
||||
|
||||
// For now, we'll just pay attention to whoever the most recent peasant
|
||||
// was. We're not too worried about the mru peasant dying. Worst case -
|
||||
// when the user executes a `wt -w 0`, we won't be able to find that
|
||||
// peasant, and it'll open in a new window instead of the current one.
|
||||
if (args.ActivatedTime() > _lastActivatedTime)
|
||||
// Start by making a local copy of these args. It's easier for us if our
|
||||
// tracking of these args is all in-proc. That way, the only thing that
|
||||
// could fail due to the peasant dying is _this first copy_.
|
||||
winrt::com_ptr<implementation::WindowActivatedArgs> localArgs{ nullptr };
|
||||
try
|
||||
{
|
||||
_mostRecentPeasant = args.PeasantID();
|
||||
_lastActivatedTime = args.ActivatedTime();
|
||||
localArgs = winrt::make_self<implementation::WindowActivatedArgs>(args);
|
||||
// This method will actually do the hard work
|
||||
_doHandleActivatePeasant(localArgs);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_HandleActivatePeasant_Failed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper for removing a peasant from the list of MRU peasants. We want to
|
||||
// do this both when the peasant dies, and also when the peasant is newly
|
||||
// activated (so that we don't leave an old entry for it in the list).
|
||||
// Arguments:
|
||||
// - peasantID: The ID of the peasant to remove from the MRU list
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_clearOldMruEntries(const uint64_t peasantID)
|
||||
{
|
||||
auto result = std::find_if(_mruPeasants.begin(),
|
||||
_mruPeasants.end(),
|
||||
[peasantID](auto&& other) {
|
||||
return peasantID == other.PeasantID();
|
||||
});
|
||||
|
||||
if (result != std::end(_mruPeasants))
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_RemovedPeasantFromDesktop",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"),
|
||||
TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
_mruPeasants.erase(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Actually handle inserting the WindowActivatedArgs into our list of MRU windows.
|
||||
// Arguments:
|
||||
// - localArgs: an in-proc WindowActivatedArgs that we should add to our list of MRU windows.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_doHandleActivatePeasant(const winrt::com_ptr<implementation::WindowActivatedArgs>& localArgs)
|
||||
{
|
||||
const auto newLastActiveTime = localArgs->ActivatedTime().time_since_epoch().count();
|
||||
|
||||
// * Check all the current lists to look for this peasant.
|
||||
// remove it from any where it exists.
|
||||
_clearOldMruEntries(localArgs->PeasantID());
|
||||
|
||||
// * If the current desktop doesn't have a vector, add one.
|
||||
const auto desktopGuid{ localArgs->DesktopID() };
|
||||
|
||||
// * Add this args list. By using lower_bound with insert, we can get it
|
||||
// into exactly the right spot, without having to re-sort the whole
|
||||
// array.
|
||||
_mruPeasants.insert(std::lower_bound(_mruPeasants.begin(),
|
||||
_mruPeasants.end(),
|
||||
*localArgs,
|
||||
[](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }),
|
||||
*localArgs);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SetMostRecentPeasant",
|
||||
TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"),
|
||||
TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"),
|
||||
TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"),
|
||||
TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"),
|
||||
TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"),
|
||||
TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided localArgs->ActivatedTime()"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
|
||||
uint64_t Monarch::_getMostRecentPeasantID()
|
||||
// Method Description:
|
||||
// - Retrieves the ID of the MRU peasant window. If requested, will limit
|
||||
// the search to windows that are on the current desktop.
|
||||
// Arguments:
|
||||
// - limitToCurrentDesktop: if true, only return the MRU peasant that's
|
||||
// actually on the current desktop.
|
||||
// Return Value:
|
||||
// - the ID of the most recent peasant, otherwise 0 if we could not find one.
|
||||
uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop)
|
||||
{
|
||||
if (_mostRecentPeasant != 0)
|
||||
if (_mruPeasants.empty())
|
||||
{
|
||||
return _mostRecentPeasant;
|
||||
// We haven't yet been told the MRU peasant. Just use the first one.
|
||||
// This is just gonna be a random one, but really shouldn't happen
|
||||
// in practice. The WindowManager should set the MRU peasant
|
||||
// immediately as soon as it creates the monarch/peasant for the
|
||||
// first window.
|
||||
if (_peasants.size() > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _peasants.begin()->second.GetID();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// This shouldn't really happen. If we're the monarch, then the
|
||||
// first peasant should also _be us_. So we should be able to
|
||||
// get our own ID.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_NoPeasants",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We haven't yet been told the MRU peasant. Just use the first one.
|
||||
// This is just gonna be a random one, but really shouldn't happen
|
||||
// in practice. The WindowManager should set the MRU peasant
|
||||
// immediately as soon as it creates the monarch/peasant for the
|
||||
// first window.
|
||||
if (_peasants.size() > 0)
|
||||
// Here, there's at least one MRU peasant.
|
||||
//
|
||||
// We're going to iterate over these peasants until we find one that both:
|
||||
// 1. Is alive
|
||||
// 2. Meets our selection criteria (do we care if it is on this desktop?)
|
||||
//
|
||||
// If the peasant is dead, then we'll remove it, and try the next one.
|
||||
// Once we find one that's alive, we'll either:
|
||||
// * check if we only want a peasant on the current desktop, and if so,
|
||||
// check if this peasant is on the current desktop.
|
||||
// - If it isn't on the current desktop, we'll loop again, on the
|
||||
// following peasant.
|
||||
// * If we don't care, then we'll just return that one.
|
||||
//
|
||||
// We're not just using an iterator because the contents of the list
|
||||
// might change while we're iterating here (if the peasant is dead we'll
|
||||
// remove it from the list).
|
||||
int positionInList = 0;
|
||||
while (_mruPeasants.cbegin() + positionInList < _mruPeasants.cend())
|
||||
{
|
||||
try
|
||||
const auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) };
|
||||
const auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) };
|
||||
if (!peasant)
|
||||
{
|
||||
return _peasants.begin()->second.GetID();
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_Collect_WasDead",
|
||||
TraceLoggingUInt64(mruWindowArgs.PeasantID(),
|
||||
"peasantID",
|
||||
"We thought this peasant was the MRU one, but it was actually already dead."),
|
||||
TraceLoggingGuid(mruWindowArgs.DesktopID(), "desktopGuid", "The GUID of the desktop the window is on"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
// We'll go through the loop again. We removed the current one
|
||||
// at positionInList, so the next one in positionInList will be
|
||||
// a new, different peasant.
|
||||
continue;
|
||||
}
|
||||
catch (...)
|
||||
|
||||
if (limitToCurrentDesktop && _desktopManager)
|
||||
{
|
||||
// This shouldn't really happen. If we're the monarch, then the
|
||||
// first peasant should also _be us_. So we should be able to
|
||||
// get our own ID.
|
||||
return 0;
|
||||
// Check if this peasant is actually on this desktop. We can't
|
||||
// simply get the GUID of the current desktop. We have to ask if
|
||||
// the HWND is on the current desktop.
|
||||
BOOL onCurrentDesktop{ false };
|
||||
|
||||
// SUCCEEDED_LOG will log if it failed, and return true if it SUCCEEDED
|
||||
if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop(reinterpret_cast<HWND>(mruWindowArgs.Hwnd()),
|
||||
&onCurrentDesktop)) &&
|
||||
onCurrentDesktop)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_Collect",
|
||||
TraceLoggingUInt64(mruWindowArgs.PeasantID(),
|
||||
"peasantID",
|
||||
"the ID of the MRU peasant for a desktop"),
|
||||
TraceLoggingGuid(mruWindowArgs.DesktopID(),
|
||||
"desktopGuid",
|
||||
"The GUID of the desktop the window is on"),
|
||||
TraceLoggingBoolean(limitToCurrentDesktop,
|
||||
"limitToCurrentDesktop",
|
||||
"True if we should only search for a window on the current desktop"),
|
||||
TraceLoggingBool(onCurrentDesktop,
|
||||
"onCurrentDesktop",
|
||||
"true if this window was in fact on the current desktop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
return mruWindowArgs.PeasantID();
|
||||
}
|
||||
// If this window wasn't on the current desktop, another one
|
||||
// might be. We'll increment positionInList below, and try
|
||||
// again.
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_Found",
|
||||
TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "The ID of the MRU peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
return mruWindowArgs.PeasantID();
|
||||
}
|
||||
positionInList++;
|
||||
}
|
||||
|
||||
// Here, we've checked all the windows, and none of them was both alive
|
||||
// and the most recent (on this desktop). Just return 0 - the caller
|
||||
// will use this to create a new window.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_NotFound",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -216,16 +401,51 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// the parsed result.
|
||||
const auto targetWindow = findWindowArgs->ResultTargetWindow();
|
||||
|
||||
// If there's a valid ID returned, then let's try and find the peasant that goes with it.
|
||||
if (targetWindow >= 0)
|
||||
{
|
||||
uint64_t windowID = ::base::saturated_cast<uint64_t>(targetWindow);
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline",
|
||||
TraceLoggingInt64(targetWindow, "targetWindow", "The window ID the args specified"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
if (windowID == 0)
|
||||
// If there's a valid ID returned, then let's try and find the peasant
|
||||
// that goes with it. Alternatively, if we were given a magic windowing
|
||||
// constant, we can use that to look up an appropriate peasant.
|
||||
if (targetWindow >= 0 ||
|
||||
targetWindow == WindowingBehaviorUseExisting ||
|
||||
targetWindow == WindowingBehaviorUseAnyExisting)
|
||||
{
|
||||
uint64_t windowID = 0;
|
||||
switch (targetWindow)
|
||||
{
|
||||
windowID = _getMostRecentPeasantID();
|
||||
case WindowingBehaviorUseCurrent:
|
||||
case WindowingBehaviorUseExisting:
|
||||
// TODO:projects/5 for now, just use the MRU window. Technically,
|
||||
// UseExisting and UseCurrent are different.
|
||||
// UseCurrent implies that we should try to do the WT_SESSION
|
||||
// lookup to find the window that spawned this process (then
|
||||
// fall back to sameDesktop if we can't find a match). For now,
|
||||
// it's good enough to just try to find a match on this desktop.
|
||||
windowID = _getMostRecentPeasantID(true);
|
||||
break;
|
||||
case WindowingBehaviorUseAnyExisting:
|
||||
windowID = _getMostRecentPeasantID(false);
|
||||
break;
|
||||
default:
|
||||
windowID = ::base::saturated_cast<uint64_t>(targetWindow);
|
||||
break;
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline",
|
||||
TraceLoggingInt64(windowID,
|
||||
"windowID",
|
||||
"The actual peasant ID we evaluated the window ID as"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
// If_getMostRecentPeasantID returns 0 above, then we couldn't find
|
||||
// a matching window for that style of windowing. _getPeasant will
|
||||
// return nullptr, and we'll fall through to the "create a new
|
||||
// window" branch below.
|
||||
|
||||
if (auto targetPeasant{ _getPeasant(windowID) })
|
||||
{
|
||||
auto result{ winrt::make_self<Remoting::implementation::ProposeCommandlineResult>(false) };
|
||||
@@ -251,9 +471,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline_Existing",
|
||||
TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"),
|
||||
TraceLoggingUInt64(windowID,
|
||||
"peasantID",
|
||||
"the ID of the peasant the commandline waws intended for"),
|
||||
TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"),
|
||||
TraceLoggingBoolean(!result->ShouldCreateWindow(), "succeeded", "true if we successfully dispatched the commandline to the peasant"),
|
||||
TraceLoggingBoolean(!result->ShouldCreateWindow(),
|
||||
"succeeded",
|
||||
"true if we successfully dispatched the commandline to the peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
return *result;
|
||||
}
|
||||
@@ -265,7 +489,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline_Existing",
|
||||
TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"),
|
||||
TraceLoggingUInt64(windowID,
|
||||
"peasantID",
|
||||
"the ID of the peasant the commandline waws intended for"),
|
||||
TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
@@ -275,6 +501,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, we couldn't find an existing window. Make a new one.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_ProposeCommandline_NewWindow",
|
||||
TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"),
|
||||
@@ -283,5 +510,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// In this case, no usable ID was provided. Return { true, nullopt }
|
||||
return winrt::make<Remoting::implementation::ProposeCommandlineResult>(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Monarch.g.h"
|
||||
#include "Peasant.h"
|
||||
#include "../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "WindowActivatedArgs.h"
|
||||
|
||||
// We sure different GUIDs here depending on whether we're running a Release,
|
||||
// Preview, or Dev build. This ensures that different installs don't
|
||||
@@ -30,12 +31,6 @@ constexpr GUID Monarch_clsid
|
||||
}
|
||||
};
|
||||
|
||||
enum class WindowingBehavior : uint64_t
|
||||
{
|
||||
UseNew = 0,
|
||||
UseExisting = 1,
|
||||
};
|
||||
|
||||
namespace RemotingUnitTests
|
||||
{
|
||||
class RemotingTests;
|
||||
@@ -63,17 +58,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
uint64_t _nextPeasantID{ 1 };
|
||||
uint64_t _thisPeasantID{ 0 };
|
||||
uint64_t _mostRecentPeasant{ 0 };
|
||||
winrt::Windows::Foundation::DateTime _lastActivatedTime{};
|
||||
|
||||
WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew };
|
||||
winrt::com_ptr<IVirtualDesktopManager> _desktopManager{ nullptr };
|
||||
|
||||
std::unordered_map<uint64_t, winrt::Microsoft::Terminal::Remoting::IPeasant> _peasants;
|
||||
|
||||
std::vector<Remoting::WindowActivatedArgs> _mruPeasants;
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
|
||||
uint64_t _getMostRecentPeasantID();
|
||||
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop);
|
||||
|
||||
void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
void _doHandleActivatePeasant(const winrt::com_ptr<winrt::Microsoft::Terminal::Remoting::implementation::WindowActivatedArgs>& args);
|
||||
void _clearOldMruEntries(const uint64_t peasantID);
|
||||
|
||||
friend class RemotingUnitTests::RemotingTests;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace Microsoft.Terminal.Remoting
|
||||
runtimeclass WindowActivatedArgs
|
||||
{
|
||||
WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime);
|
||||
WindowActivatedArgs(UInt64 peasantID, UInt64 hwnd, Guid desktopID, Windows.Foundation.DateTime activatedTime);
|
||||
UInt64 PeasantID { get; };
|
||||
UInt64 Hwnd { get; };
|
||||
Guid DesktopID { get; };
|
||||
Windows.Foundation.DateTime ActivatedTime { get; };
|
||||
};
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
struct ProposeCommandlineResult : public ProposeCommandlineResultT<ProposeCommandlineResult>
|
||||
{
|
||||
public:
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
|
||||
GETSET_PROPERTY(bool, ShouldCreateWindow, true);
|
||||
WINRT_PROPERTY(Windows::Foundation::IReference<uint64_t>, Id);
|
||||
WINRT_PROPERTY(bool, ShouldCreateWindow, true);
|
||||
|
||||
public:
|
||||
ProposeCommandlineResult(bool shouldCreateWindow) :
|
||||
|
||||
@@ -18,17 +18,40 @@ Abstract:
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct CompareWindowActivatedArgs
|
||||
{
|
||||
bool operator()(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) const
|
||||
{
|
||||
return lhs.ActivatedTime() > rhs.ActivatedTime();
|
||||
}
|
||||
};
|
||||
struct WindowActivatedArgs : public WindowActivatedArgsT<WindowActivatedArgs>
|
||||
{
|
||||
GETSET_PROPERTY(uint64_t, PeasantID, 0);
|
||||
GETSET_PROPERTY(winrt::guid, DesktopID, {});
|
||||
GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
|
||||
WINRT_PROPERTY(uint64_t, PeasantID, 0);
|
||||
WINRT_PROPERTY(winrt::guid, DesktopID, {});
|
||||
WINRT_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
|
||||
WINRT_PROPERTY(uint64_t, Hwnd, 0);
|
||||
|
||||
public:
|
||||
WindowActivatedArgs(uint64_t peasantID, winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) :
|
||||
WindowActivatedArgs(uint64_t peasantID,
|
||||
uint64_t hwnd,
|
||||
winrt::guid desktopID,
|
||||
winrt::Windows::Foundation::DateTime timestamp) :
|
||||
_PeasantID{ peasantID },
|
||||
_Hwnd{ hwnd },
|
||||
_DesktopID{ desktopID },
|
||||
_ActivatedTime{ timestamp } {};
|
||||
|
||||
WindowActivatedArgs(uint64_t peasantID,
|
||||
winrt::guid desktopID,
|
||||
winrt::Windows::Foundation::DateTime timestamp) :
|
||||
WindowActivatedArgs(peasantID, 0, desktopID, timestamp){};
|
||||
|
||||
WindowActivatedArgs(const Remoting::WindowActivatedArgs& other) :
|
||||
_PeasantID{ other.PeasantID() },
|
||||
_Hwnd{ other.Hwnd() },
|
||||
_DesktopID{ other.DesktopID() },
|
||||
_ActivatedTime{ other.ActivatedTime() } {};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Block minwindef.h min/max macros to prevent <algorithm> conflict
|
||||
#define NOMINMAX
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOHELP
|
||||
#define NOCOMM
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <ShObjIdl.h>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
#include <LibraryIncludes.h>
|
||||
@@ -25,8 +31,6 @@
|
||||
|
||||
#include <wil/cppwinrt.h>
|
||||
|
||||
#include <unknwn.h>
|
||||
|
||||
#include <hstring.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "OpenTerminalHere.h"
|
||||
#include "../WinRTUtils/inc/WtExeUtils.h"
|
||||
#include <ShlObj.h>
|
||||
|
||||
// TODO GH#6112: Localize these strings
|
||||
@@ -10,103 +11,10 @@ static constexpr std::wstring_view VerbDisplayName{ L"Open in Windows Terminal"
|
||||
static constexpr std::wstring_view VerbDevBuildDisplayName{ L"Open in Windows Terminal (Dev Build)" };
|
||||
static constexpr std::wstring_view VerbName{ L"WindowsTerminalOpenHere" };
|
||||
|
||||
static constexpr std::wstring_view WtExe{ L"wt.exe" };
|
||||
static constexpr std::wstring_view WtdExe{ L"wtd.exe" };
|
||||
static constexpr std::wstring_view WindowsTerminalExe{ L"WindowsTerminal.exe" };
|
||||
|
||||
static constexpr std::wstring_view LocalAppDataAppsPath{ L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\" };
|
||||
|
||||
// This code is aggressively copied from
|
||||
// https://github.com/microsoft/Windows-classic-samples/blob/master/Samples/
|
||||
// Win7Samples/winui/shell/appshellintegration/ExplorerCommandVerb/ExplorerCommandVerb.cpp
|
||||
|
||||
// Function Description:
|
||||
// - This is a helper to determine if we're running as a part of the Dev Build
|
||||
// Package or the release package. We'll need to return different text, icons,
|
||||
// and use different commandlines depending on which one the user requested.
|
||||
// - Uses a C++11 "magic static" to make sure this is only computed once.
|
||||
// - If we can't determine if it's the dev build or not, we'll default to true
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true if we believe this extension is being run in the dev build package.
|
||||
static bool IsDevBuild()
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static bool isDevBuild = []() -> bool {
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto id = package.Id();
|
||||
const std::wstring name{ id.FullName() };
|
||||
// Does our PFN start with WindowsTerminalDev?
|
||||
return name.rfind(L"WindowsTerminalDev", 0) == 0;
|
||||
}
|
||||
CATCH_LOG();
|
||||
return true;
|
||||
}();
|
||||
|
||||
return isDevBuild;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for getting the path to the appropriate executable to use
|
||||
// for this instance of the shell extension. If we're running the dev build,
|
||||
// it should be a `wtd.exe`, but if we're preview or release, we want to make
|
||||
// sure to get the correct `wt.exe` that corresponds to _us_.
|
||||
// - If we're unpackaged, this needs to get us `WindowsTerminal.exe`, because
|
||||
// the `wt*exe` alias won't have been installed for this install.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the full path to the exe, one of `wt.exe`, `wtd.exe`, or `WindowsTerminal.exe`.
|
||||
static std::wstring _getExePath()
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static const std::wstring exePath = []() -> std::wstring {
|
||||
// First, check a packaged location for the exe. If we've got a package
|
||||
// family name, that means we're one of the packaged Dev build, packaged
|
||||
// Release build, or packaged Preview build.
|
||||
//
|
||||
// If we're the preview or release build, there's no way of knowing if the
|
||||
// `wt.exe` on the %PATH% is us or not. Fortunately, _our_ execution alias
|
||||
// is located in "%LOCALAPPDATA%\Microsoft\WindowsApps\<our package family
|
||||
// name>", _always_, so we can use that to look up the exe easier.
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto id = package.Id();
|
||||
const std::wstring pfn{ id.FamilyName() };
|
||||
if (!pfn.empty())
|
||||
{
|
||||
const std::filesystem::path windowsAppsPath{ wil::ExpandEnvironmentStringsW<std::wstring>(LocalAppDataAppsPath.data()) };
|
||||
const std::filesystem::path wtPath = windowsAppsPath / pfn / (IsDevBuild() ? WtdExe : WtExe);
|
||||
return wtPath;
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
// If we're here, then we couldn't resolve our exe from the package. This
|
||||
// means we're running unpackaged. We should just use the
|
||||
// WindowsTerminal.exe that's sitting in the directory next to us.
|
||||
try
|
||||
{
|
||||
HMODULE hModule = GetModuleHandle(nullptr);
|
||||
THROW_LAST_ERROR_IF(hModule == nullptr);
|
||||
std::wstring dllPathString;
|
||||
THROW_IF_FAILED(wil::GetModuleFileNameW(hModule, dllPathString));
|
||||
const std::filesystem::path dllPath{ dllPathString };
|
||||
const std::filesystem::path rootDir = dllPath.parent_path();
|
||||
std::filesystem::path wtPath = rootDir / WindowsTerminalExe;
|
||||
return wtPath;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return L"wt.exe";
|
||||
}();
|
||||
return exePath;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method is called when the user activates the context menu item. We'll
|
||||
// launch the Terminal using the current working directory.
|
||||
@@ -148,7 +56,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
|
||||
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
|
||||
|
||||
// Append a "\." to the given path, so that this will work in "C:\"
|
||||
std::wstring cmdline = fmt::format(L"\"{}\" -d \"{}\\.\"", _getExePath(), pszName.get());
|
||||
std::wstring cmdline = fmt::format(L"\"{}\" -d \"{}\\.\"", GetWtExePath(), pszName.get());
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
|
||||
nullptr,
|
||||
cmdline.data(),
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace winrt::Microsoft::Terminal::ShellExtension::implementation
|
||||
struct PlaceholderType : PlaceholderTypeT<PlaceholderType>
|
||||
{
|
||||
PlaceholderType() = default;
|
||||
GETSET_PROPERTY(int32_t, Placeholder, 42);
|
||||
WINRT_PROPERTY(int32_t, Placeholder, 42);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
Name(command.Name());
|
||||
KeyChordText(command.KeyChordText());
|
||||
Icon(command.Icon());
|
||||
Icon(command.IconPath());
|
||||
|
||||
_commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) {
|
||||
auto item{ weakThis.get() };
|
||||
@@ -40,9 +40,9 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
item->KeyChordText(senderCommand.KeyChordText());
|
||||
}
|
||||
else if (changedProperty == L"Icon")
|
||||
else if (changedProperty == L"IconPath")
|
||||
{
|
||||
item->Icon(senderCommand.Icon());
|
||||
item->Icon(senderCommand.IconPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace winrt::TerminalApp::implementation
|
||||
ActionPaletteItem() = default;
|
||||
ActionPaletteItem(Microsoft::Terminal::Settings::Model::Command const& command);
|
||||
|
||||
GETSET_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
|
||||
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::Command, Command, nullptr);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _commandChangedRevoker;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "App.h"
|
||||
|
||||
#include "TerminalPage.h"
|
||||
#include "../WinRTUtils/inc/WtExeUtils.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
@@ -37,7 +39,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_DuplicateTabViewItem();
|
||||
_DuplicateFocusedTab();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -193,6 +195,18 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleFindMatch(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<FindMatchArgs>())
|
||||
{
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
control.SearchMatch(realArgs.Direction() == FindMatchDirection::Next);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
@@ -569,4 +583,89 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Helper to launch a new WT instance. It can either launch the instance
|
||||
// elevated or unelevated.
|
||||
// - To launch elevated, it will as the shell to elevate the process for us.
|
||||
// This might cause a UAC prompt. The elevation is performed on a
|
||||
// background thread, as to not block the UI thread.
|
||||
// Arguments:
|
||||
// - elevate: If true, launch the new Terminal elevated using `runas`
|
||||
// - newTerminalArgs: A NewTerminalArgs describing the terminal instance
|
||||
// that should be spawned. The Profile should be filled in with the GUID
|
||||
// of the profile we want to launch.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
// Important: Don't take the param by reference, since we'll be doing work
|
||||
// on another thread.
|
||||
fire_and_forget TerminalPage::_OpenNewWindow(const bool elevate,
|
||||
const NewTerminalArgs newTerminalArgs)
|
||||
{
|
||||
// Hop to the BG thread
|
||||
co_await winrt::resume_background();
|
||||
|
||||
// This will get us the correct exe for dev/preview/release. If you
|
||||
// don't stick this in a local, it'll get mangled by ShellExecute. I
|
||||
// have no idea why.
|
||||
const auto exePath{ GetWtExePath() };
|
||||
|
||||
// Build the commandline to pass to wt for this set of NewTerminalArgs
|
||||
// `-w -1` will ensure a new window is created.
|
||||
winrt::hstring cmdline{
|
||||
fmt::format(L"-w -1 new-tab {}",
|
||||
newTerminalArgs ? newTerminalArgs.ToCommandline().c_str() :
|
||||
L"")
|
||||
};
|
||||
|
||||
// Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we
|
||||
// can pass the SEE_MASK_NOASYNC flag. That flag allows us to safely
|
||||
// call this on the background thread, and have ShellExecute _not_ call
|
||||
// back to us on the main thread. Without this, if you close the
|
||||
// Terminal quickly after the UAC prompt, the elevated WT will never
|
||||
// actually spawn.
|
||||
SHELLEXECUTEINFOW seInfo{ 0 };
|
||||
seInfo.cbSize = sizeof(seInfo);
|
||||
seInfo.fMask = SEE_MASK_NOASYNC;
|
||||
// `runas` will cause the shell to launch this child process elevated.
|
||||
// `open` will just run the executable normally.
|
||||
seInfo.lpVerb = elevate ? L"runas" : L"open";
|
||||
seInfo.lpFile = exePath.c_str();
|
||||
seInfo.lpParameters = cmdline.c_str();
|
||||
seInfo.nShow = SW_SHOWNORMAL;
|
||||
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNewWindow(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& actionArgs)
|
||||
{
|
||||
NewTerminalArgs newTerminalArgs{ nullptr };
|
||||
// If the caller provided NewTerminalArgs, then try to use those
|
||||
if (actionArgs)
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<NewWindowArgs>())
|
||||
{
|
||||
newTerminalArgs = realArgs.TerminalArgs();
|
||||
}
|
||||
}
|
||||
// Otherwise, if no NewTerminalArgs were provided, then just use a
|
||||
// default-constructed one. The default-constructed one implies that
|
||||
// nothing about the launch should be modified (just use the default
|
||||
// profile).
|
||||
if (!newTerminalArgs)
|
||||
{
|
||||
newTerminalArgs = NewTerminalArgs();
|
||||
}
|
||||
|
||||
auto [profileGuid, settings] = TerminalSettings::BuildSettings(_settings,
|
||||
newTerminalArgs,
|
||||
*_bindings);
|
||||
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
_OpenNewWindow(false, newTerminalArgs);
|
||||
actionArgs.Handled(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser()
|
||||
maximized->excludes(fullscreen);
|
||||
focus->excludes(fullscreen);
|
||||
|
||||
_app.add_option("-w,--window",
|
||||
_windowTarget,
|
||||
RS_A(L"CmdWindowTargetArgDesc"));
|
||||
_app.add_option<std::optional<int>, int>("-w,--window",
|
||||
_windowTarget,
|
||||
RS_A(L"CmdWindowTargetArgDesc"));
|
||||
|
||||
// Subcommands
|
||||
_buildNewTabParser();
|
||||
@@ -417,6 +417,11 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc
|
||||
_startingTabColor,
|
||||
RS_A(L"CmdTabColorArgDesc"));
|
||||
|
||||
subcommand.suppressApplicationTitleOption = subcommand.subcommand->add_flag(
|
||||
"--suppressApplicationTitle,!--useApplicationTitle",
|
||||
_suppressApplicationTitle,
|
||||
RS_A(L"CmdSuppressApplicationTitleDesc"));
|
||||
|
||||
// Using positionals_at_end allows us to support "wt new-tab -d wsl -d Ubuntu"
|
||||
// without CLI11 thinking that we've specified -d twice.
|
||||
// There's an alternate construction where we make all subcommands "prefix commands",
|
||||
@@ -484,6 +489,11 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
|
||||
args.TabColor(static_cast<winrt::Windows::UI::Color>(tabColor));
|
||||
}
|
||||
|
||||
if (*subcommand.suppressApplicationTitleOption)
|
||||
{
|
||||
args.SuppressApplicationTitle(_suppressApplicationTitle);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -522,6 +532,7 @@ void AppCommandlineArgs::_resetStateToDefault()
|
||||
_startingTitle.clear();
|
||||
_startingTabColor.clear();
|
||||
_commandline.clear();
|
||||
_suppressApplicationTitle = false;
|
||||
|
||||
_splitVertical = false;
|
||||
_splitHorizontal = false;
|
||||
@@ -854,10 +865,16 @@ void AppCommandlineArgs::FullResetState()
|
||||
_exitMessage = "";
|
||||
_shouldExitEarly = false;
|
||||
|
||||
_windowTarget = -1;
|
||||
_windowTarget = std::nullopt;
|
||||
}
|
||||
|
||||
int AppCommandlineArgs::GetTargetWindow() const noexcept
|
||||
std::optional<int> AppCommandlineArgs::GetTargetWindow() const noexcept
|
||||
{
|
||||
// If the user provides _any_ negative number, then treat it as -1, for "use a new window".
|
||||
if (_windowTarget.has_value() && *_windowTarget < 0)
|
||||
{
|
||||
return { -1 };
|
||||
}
|
||||
|
||||
return _windowTarget;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
void DisableHelpInExitMessage();
|
||||
void FullResetState();
|
||||
|
||||
int GetTargetWindow() const noexcept;
|
||||
std::optional<int> GetTargetWindow() const noexcept;
|
||||
|
||||
private:
|
||||
static const std::wregex _commandDelimiterRegex;
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
CLI::Option* startingDirectoryOption;
|
||||
CLI::Option* titleOption;
|
||||
CLI::Option* tabColorOption;
|
||||
CLI::Option* suppressApplicationTitleOption;
|
||||
};
|
||||
|
||||
struct NewPaneSubcommand : public NewTerminalSubcommand
|
||||
@@ -85,6 +86,7 @@ private:
|
||||
std::string _startingDirectory;
|
||||
std::string _startingTitle;
|
||||
std::string _startingTabColor;
|
||||
bool _suppressApplicationTitle{ false };
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::FocusDirection _moveFocusDirection{ winrt::Microsoft::Terminal::Settings::Model::FocusDirection::None };
|
||||
|
||||
@@ -106,7 +108,7 @@ private:
|
||||
std::string _exitMessage;
|
||||
bool _shouldExitEarly{ false };
|
||||
|
||||
int _windowTarget{ -1 };
|
||||
std::optional<int> _windowTarget{ std::nullopt };
|
||||
// Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppLogic.h"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
#include "AppLogic.g.cpp"
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
@@ -172,6 +173,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Method Description:
|
||||
// - Returns the settings currently in use by the entire Terminal application.
|
||||
// - IMPORTANT! This can throw! Make sure to try/catch this, so that the
|
||||
// LocalTests don't crash (because their Application::Current() won't be a
|
||||
// AppLogic)
|
||||
// Throws:
|
||||
// - HR E_INVALIDARG if the app isn't up and running.
|
||||
const CascadiaSettings AppLogic::CurrentAppSettings()
|
||||
@@ -487,11 +491,15 @@ namespace winrt::TerminalApp::implementation
|
||||
void AppLogic::_OnLoaded(const IInspectable& /*sender*/,
|
||||
const RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled();
|
||||
if (keyboardServiceIsDisabled)
|
||||
if (_settings.GlobalSettings().InputServiceWarning())
|
||||
{
|
||||
_root->ShowKeyboardServiceWarning();
|
||||
const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled();
|
||||
if (keyboardServiceIsDisabled)
|
||||
{
|
||||
_root->ShowKeyboardServiceWarning();
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle");
|
||||
@@ -660,6 +668,16 @@ namespace winrt::TerminalApp::implementation
|
||||
};
|
||||
}
|
||||
|
||||
bool AppLogic::CenterOnLaunch()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
return _settings.GlobalSettings().CenterOnLaunch();
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
@@ -1194,27 +1212,54 @@ namespace winrt::TerminalApp::implementation
|
||||
// - args: an array of strings to process as a commandline. These args can contain spaces
|
||||
// Return Value:
|
||||
// - 0: We should handle the args "in the current window".
|
||||
// - -1: We should handle the args in a new window
|
||||
// - WindowingBehaviorUseNew: We should handle the args in a new window
|
||||
// - WindowingBehaviorUseExisting: We should handle the args "in
|
||||
// the current window ON THIS DESKTOP"
|
||||
// - WindowingBehaviorUseAnyExisting: We should handle the args "in the current
|
||||
// window ON ANY DESKTOP"
|
||||
// - anything else: We should handle the commandline in the window with the given ID.
|
||||
int32_t AppLogic::FindTargetWindow(array_view<const winrt::hstring> args)
|
||||
{
|
||||
return AppLogic::_doFindTargetWindow(args, _settings.GlobalSettings().WindowingBehavior());
|
||||
}
|
||||
|
||||
// The main body of this function is a static helper, to facilitate unit-testing
|
||||
int32_t AppLogic::_doFindTargetWindow(array_view<const winrt::hstring> args,
|
||||
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior)
|
||||
{
|
||||
::TerminalApp::AppCommandlineArgs appArgs;
|
||||
const auto result = appArgs.ParseArgs(args);
|
||||
if (result == 0)
|
||||
{
|
||||
return appArgs.GetTargetWindow();
|
||||
if (!appArgs.GetExitMessage().empty())
|
||||
{
|
||||
return WindowingBehaviorUseNew;
|
||||
}
|
||||
|
||||
// TODO:projects/5
|
||||
//
|
||||
// In the future, we'll want to use the windowingBehavior setting to
|
||||
// determine what happens when a window ID wasn't manually provided.
|
||||
//
|
||||
// Maybe that'd be a special return value out of here, to tell the
|
||||
// monarch to do something special:
|
||||
//
|
||||
// -1 -> create a new window
|
||||
// -2 -> find the mru, this desktop
|
||||
// -3 -> MRU, any desktop (is this not just 0?)
|
||||
const auto parsedTarget = appArgs.GetTargetWindow();
|
||||
if (parsedTarget.has_value())
|
||||
{
|
||||
// parsedTarget might be -1, if the user explicitly requested -1
|
||||
// (or any other negative number) on the commandline. So the set
|
||||
// of possible values here is {-1, 0, ℤ+}
|
||||
return *parsedTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the user did not provide any value on the commandline,
|
||||
// then lookup our windowing behavior to determine what to do
|
||||
// now.
|
||||
switch (windowingBehavior)
|
||||
{
|
||||
case WindowingMode::UseExisting:
|
||||
return WindowingBehaviorUseExisting;
|
||||
case WindowingMode::UseAnyExisting:
|
||||
return WindowingBehaviorUseAnyExisting;
|
||||
case WindowingMode::UseNew:
|
||||
default:
|
||||
return WindowingBehaviorUseNew;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any unsuccessful parse will be a new window. That new window will try
|
||||
@@ -1228,7 +1273,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// create a new window. Then, in that new window, we'll try to set the
|
||||
// StartupActions, which will again fail, returning the correct error
|
||||
// message.
|
||||
return -1;
|
||||
return WindowingBehaviorUseNew;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
#include "Jumplist.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class CommandlineTest;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct AppLogic : AppLogicT<AppLogic, IInitializeWithWindow>
|
||||
@@ -39,6 +47,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool AlwaysOnTop() const;
|
||||
|
||||
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
|
||||
bool CenterOnLaunch();
|
||||
TerminalApp::InitialPosition GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY);
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode();
|
||||
@@ -89,6 +98,8 @@ namespace winrt::TerminalApp::implementation
|
||||
::TerminalApp::AppCommandlineArgs _appArgs;
|
||||
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
|
||||
int _ParseArgs(winrt::array_view<const hstring>& args);
|
||||
static int32_t _doFindTargetWindow(winrt::array_view<const hstring> args,
|
||||
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior);
|
||||
|
||||
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);
|
||||
void _ShowLoadWarningsDialog();
|
||||
@@ -124,6 +135,10 @@ namespace winrt::TerminalApp::implementation
|
||||
FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged);
|
||||
FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell);
|
||||
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TerminalAppLocalTests::CommandlineTest;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace TerminalApp
|
||||
Boolean AlwaysOnTop { get; };
|
||||
|
||||
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
|
||||
Boolean CenterOnLaunch { get; };
|
||||
|
||||
InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY);
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace winrt::TerminalApp::implementation
|
||||
CommandLinePaletteItem() = default;
|
||||
CommandLinePaletteItem(winrt::hstring const& commandLine);
|
||||
|
||||
GETSET_PROPERTY(winrt::hstring, CommandLine);
|
||||
WINRT_PROPERTY(winrt::hstring, CommandLine);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,12 +48,12 @@ namespace winrt::TerminalApp::implementation
|
||||
void EnableTabSearchMode();
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);
|
||||
|
||||
TYPED_EVENT(SwitchToTabRequested, winrt::TerminalApp::CommandPalette, winrt::TerminalApp::TabBase);
|
||||
TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::CommandPalette, winrt::hstring);
|
||||
|
||||
@@ -32,7 +32,6 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<local:EmptyStringVisibilityConverter x:Key="CommandKeyChordVisibilityConverter"/>
|
||||
<local:EmptyStringVisibilityConverter x:Key="ParsedCommandLineTextVisibilityConverter"/>
|
||||
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter"/>
|
||||
<local:HasNestedCommandsVisibilityConverter x:Key="HasNestedCommandsVisibilityConverter"/>
|
||||
<model:IconPathConverter x:Key="IconSourceConverter"/>
|
||||
|
||||
<DataTemplate x:Key="GeneralItemTemplate" x:DataType="local:FilteredCommand">
|
||||
@@ -40,7 +39,6 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<!-- This HorizontalContentAlignment="Stretch" is important
|
||||
to make sure it takes the entire width of the line -->
|
||||
<ListViewItem HorizontalContentAlignment="Stretch"
|
||||
IsTabStop="False"
|
||||
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
|
||||
|
||||
@@ -71,7 +69,12 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
|
||||
<!-- The block for the key chord is only visible
|
||||
when there's actual text set as the label. See
|
||||
CommandKeyChordVisibilityConverter for details. -->
|
||||
CommandKeyChordVisibilityConverter for details.
|
||||
We're setting the accessibility view on the
|
||||
border and text block to Raw because otherwise,
|
||||
Narrator will read out the key chord. Problem is,
|
||||
it already did that because it was the list item's
|
||||
"AcceleratorKey". It's redundant. -->
|
||||
<Border
|
||||
Grid.Column="2"
|
||||
Visibility="{x:Bind Item.KeyChordText,
|
||||
@@ -80,12 +83,78 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
Style="{ThemeResource KeyChordBorderStyle}"
|
||||
Padding="2,0,2,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center">
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
|
||||
<TextBlock
|
||||
Style="{ThemeResource KeyChordTextBlockStyle}"
|
||||
FontSize="12"
|
||||
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
|
||||
Text="{x:Bind Item.KeyChordText, Mode=OneWay}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="NestedItemTemplate" x:DataType="local:FilteredCommand">
|
||||
|
||||
<!-- This HorizontalContentAlignment="Stretch" is important
|
||||
to make sure it takes the entire width of the line -->
|
||||
<ListViewItem x:Uid="CommandPalette_MoreOptions"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<!-- icon -->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<!-- command label -->
|
||||
<ColumnDefinition Width="*"/>
|
||||
<!-- key chord -->
|
||||
<ColumnDefinition Width="16"/>
|
||||
<!-- gutter for scrollbar -->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<IconSourceElement
|
||||
Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
IconSource="{x:Bind Item.Icon,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource IconSourceConverter}}"/>
|
||||
|
||||
<local:HighlightedTextControl
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Text="{x:Bind HighlightedName, Mode=OneWay}"/>
|
||||
|
||||
<!-- The block for the key chord is only visible
|
||||
when there's actual text set as the label. See
|
||||
CommandKeyChordVisibilityConverter for details.
|
||||
We're setting the accessibility view on the
|
||||
border and text block to Raw because otherwise,
|
||||
Narrator will read out the key chord. Problem is,
|
||||
it already did that because it was the list item's
|
||||
"AcceleratorKey". It's redundant. -->
|
||||
<Border
|
||||
Grid.Column="2"
|
||||
Visibility="{x:Bind Item.KeyChordText,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource CommandKeyChordVisibilityConverter}}"
|
||||
Style="{ThemeResource KeyChordBorderStyle}"
|
||||
Padding="2,0,2,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
|
||||
<TextBlock
|
||||
Style="{ThemeResource KeyChordTextBlockStyle}"
|
||||
FontSize="12"
|
||||
Text="{x:Bind Item.KeyChordText, Mode=OneWay}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
</Border>
|
||||
|
||||
<!-- xE70E is ChevronUp. Rotated 90 degrees, it's _ChevronRight_ -->
|
||||
@@ -93,9 +162,6 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Glyph=""
|
||||
HorizontalAlignment="Right"
|
||||
Visibility="{x:Bind Item,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource HasNestedCommandsVisibilityConverter}}"
|
||||
Grid.Column="2">
|
||||
|
||||
<FontIcon.RenderTransform>
|
||||
@@ -112,7 +178,6 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<!-- This HorizontalContentAlignment="Stretch" is important
|
||||
to make sure it takes the entire width of the line -->
|
||||
<ListViewItem HorizontalContentAlignment="Stretch"
|
||||
IsTabStop="False"
|
||||
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}">
|
||||
|
||||
@@ -186,7 +251,10 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector" TabItemTemplate="{StaticResource TabItemTemplate}" GeneralItemTemplate="{StaticResource GeneralItemTemplate}"/>
|
||||
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector"
|
||||
TabItemTemplate="{StaticResource TabItemTemplate}"
|
||||
GeneralItemTemplate="{StaticResource GeneralItemTemplate}"
|
||||
NestedItemTemplate="{StaticResource NestedItemTemplate}"/>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
|
||||
@@ -25,10 +25,10 @@ namespace winrt::TerminalApp::implementation
|
||||
static int Compare(winrt::TerminalApp::FilteredCommand const& first, winrt::TerminalApp::FilteredCommand const& second);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::PaletteItem, Item, _PropertyChangedHandlers, nullptr);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Filter, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(int, Weight, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::PaletteItem, Item, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Filter, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::HighlightedText, HighlightedName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(int, Weight, _PropertyChangedHandlers);
|
||||
|
||||
private:
|
||||
winrt::TerminalApp::HighlightedText _computeHighlightedName();
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "HasNestedCommandsVisibilityConverter.h"
|
||||
#include "HasNestedCommandsVisibilityConverter.g.cpp"
|
||||
|
||||
using namespace winrt::Windows;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Method Description:
|
||||
// - Attempt to convert something into another type. For the
|
||||
// HasNestedCommandsVisibilityConverter, we're gonna check if `value` is a
|
||||
// string, and try and convert it into a Visibility value. If the input
|
||||
// param wasn't a string, or was the empty string, we'll return
|
||||
// Visibility::Collapsed. Otherwise, we'll return Visible.
|
||||
|
||||
// Arguments:
|
||||
// - value: the input object to attempt to convert into a Visibility.
|
||||
// Return Value:
|
||||
// - Visible if the object was a string and wasn't the empty string.
|
||||
Foundation::IInspectable HasNestedCommandsVisibilityConverter::Convert(Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
|
||||
Foundation::IInspectable const& /* parameter */,
|
||||
hstring const& /* language */)
|
||||
{
|
||||
const auto paletteItem{ value.try_as<winrt::TerminalApp::ActionPaletteItem>() };
|
||||
const auto& hasNestedCommands = paletteItem && paletteItem.Command().HasNestedCommands();
|
||||
return winrt::box_value(hasNestedCommands ? Visibility::Visible : Visibility::Collapsed);
|
||||
}
|
||||
|
||||
// unused for one-way bindings
|
||||
Foundation::IInspectable HasNestedCommandsVisibilityConverter::ConvertBack(Foundation::IInspectable const& /* value */,
|
||||
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
|
||||
Foundation::IInspectable const& /* parameter */,
|
||||
hstring const& /* language */)
|
||||
{
|
||||
throw hresult_not_implemented();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "HasNestedCommandsVisibilityConverter.g.h"
|
||||
#include "../inc/cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct HasNestedCommandsVisibilityConverter : HasNestedCommandsVisibilityConverterT<HasNestedCommandsVisibilityConverter>
|
||||
{
|
||||
HasNestedCommandsVisibilityConverter() = default;
|
||||
|
||||
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& targetType,
|
||||
Windows::Foundation::IInspectable const& parameter,
|
||||
hstring const& language);
|
||||
|
||||
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& targetType,
|
||||
Windows::Foundation::IInspectable const& parameter,
|
||||
hstring const& language);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(HasNestedCommandsVisibilityConverter);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
// See https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-quickstart
|
||||
|
||||
// We use the default attribute to declare IValueConverter as the default
|
||||
// interface. In the listing, HasNestedCommandsVisibilityConverter has only a
|
||||
// constructor, and no methods, so no default interface is generated for it.
|
||||
// The default attribute is optimal if you won't be adding instance members
|
||||
// to HasNestedCommandsVisibilityConverter, because no QueryInterface will be
|
||||
// required to call the IValueConverter methods
|
||||
runtimeclass HasNestedCommandsVisibilityConverter : [default] Windows.UI.Xaml.Data.IValueConverter
|
||||
{
|
||||
HasNestedCommandsVisibilityConverter();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -17,8 +17,8 @@ namespace winrt::TerminalApp::implementation
|
||||
HighlightedTextSegment(winrt::hstring const& text, bool isHighlighted);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, TextSegment, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsHighlighted, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, TextSegment, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsHighlighted, _PropertyChangedHandlers);
|
||||
};
|
||||
|
||||
struct HighlightedText : HighlightedTextT<HighlightedText>
|
||||
@@ -27,7 +27,7 @@ namespace winrt::TerminalApp::implementation
|
||||
HighlightedText(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::HighlightedTextSegment> const& segments);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::HighlightedTextSegment>, Segments, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::HighlightedTextSegment>, Segments, _PropertyChangedHandlers);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "MinMaxCloseControl.h"
|
||||
|
||||
#include "MinMaxCloseControl.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
@@ -77,6 +80,7 @@ namespace winrt::TerminalApp::implementation
|
||||
MinimizeButton().Height(maximizedHeight);
|
||||
MaximizeButton().Height(maximizedHeight);
|
||||
CloseButton().Height(maximizedHeight);
|
||||
MaximizeToolTip().Text(RS_(L"WindowRestoreDownButtonToolTip"));
|
||||
break;
|
||||
|
||||
case WindowVisualState::WindowVisualStateNormal:
|
||||
@@ -87,6 +91,7 @@ namespace winrt::TerminalApp::implementation
|
||||
MinimizeButton().Height(windowedHeight);
|
||||
MaximizeButton().Height(windowedHeight);
|
||||
CloseButton().Height(windowedHeight);
|
||||
MaximizeToolTip().Text(RS_(L"WindowMaximizeButtonToolTip"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,13 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<x:String x:Key="CaptionButtonPathWindowMaximized">M 0 2 h 8 v 8 h -8 v -8 M 2 2 v -2 h 8 v 8 h -2</x:String>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip>
|
||||
<TextBlock>
|
||||
<Run x:Name="MaximizeToolTip"/>
|
||||
</TextBlock>
|
||||
</ToolTip>
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button Height="{StaticResource CaptionButtonHeightWindowed}" MinWidth="46.0" Width="46.0"
|
||||
x:Name="CloseButton"
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace winrt::TerminalApp::implementation
|
||||
public:
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return TabItemTemplate();
|
||||
}
|
||||
else if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
|
||||
{
|
||||
if (actionPaletteItem.Command().HasNestedCommands())
|
||||
{
|
||||
return NestedItemTemplate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GeneralItemTemplate();
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace winrt::TerminalApp::implementation
|
||||
Windows::UI::Xaml::DataTemplate SelectTemplateCore(winrt::Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::DependencyObject const&);
|
||||
Windows::UI::Xaml::DataTemplate SelectTemplateCore(winrt::Windows::Foundation::IInspectable const&);
|
||||
|
||||
GETSET_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, TabItemTemplate);
|
||||
GETSET_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, GeneralItemTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, TabItemTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, NestedItemTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, GeneralItemTemplate);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace TerminalApp
|
||||
PaletteItemTemplateSelector();
|
||||
|
||||
Windows.UI.Xaml.DataTemplate TabItemTemplate;
|
||||
Windows.UI.Xaml.DataTemplate NestedItemTemplate;
|
||||
Windows.UI.Xaml.DataTemplate GeneralItemTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +211,9 @@
|
||||
<data name="RenameTabText" xml:space="preserve">
|
||||
<value>Rename Tab</value>
|
||||
</data>
|
||||
<data name="DuplicateTabText" xml:space="preserve">
|
||||
<value>Duplicate Tab</value>
|
||||
</data>
|
||||
<data name="InvalidBackgroundImage" xml:space="preserve">
|
||||
<value>Found a profile with an invalid "backgroundImage". Defaulting that profile to have no background image. Make sure that when setting a "backgroundImage", the value is a valid file path to an image.</value>
|
||||
<comment>{Locked="\"backgroundImage\""}</comment>
|
||||
@@ -313,6 +316,10 @@
|
||||
<data name="CmdTabColorArgDesc" xml:space="preserve">
|
||||
<value>Open the tab with the specified color, in #rrggbb format</value>
|
||||
</data>
|
||||
<data name="CmdSuppressApplicationTitleDesc" xml:space="preserve">
|
||||
<value>Open the tab with tabTitle overriding default title and suppressing title change messages from the application</value>
|
||||
<comment>{Locked="\"tabTitle\""}</comment>
|
||||
</data>
|
||||
<data name="CmdVersionDesc" xml:space="preserve">
|
||||
<value>Display the application version</value>
|
||||
</data>
|
||||
@@ -350,6 +357,9 @@
|
||||
<data name="NewPaneRun.Text" xml:space="preserve">
|
||||
<value>Alt+Click to split the current window</value>
|
||||
</data>
|
||||
<data name="NewWindowRun.Text" xml:space="preserve">
|
||||
<value>Shift+Click to open a new window</value>
|
||||
</data>
|
||||
<data name="WindowCloseButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
@@ -359,9 +369,6 @@
|
||||
<data name="WindowMaximizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Maximize</value>
|
||||
</data>
|
||||
<data name="WindowMaximizeButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Maximize</value>
|
||||
</data>
|
||||
<data name="WindowMinimizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Minimize</value>
|
||||
</data>
|
||||
@@ -451,7 +458,7 @@
|
||||
</data>
|
||||
<data name="CommandPalette_ParsedCommandLine" xml:space="preserve">
|
||||
<value>Executing command line will invoke the following commands:</value>
|
||||
<comment>Will be followed by a list of strings describing parsed commands</comment>
|
||||
<comment>Will be followed by a list of strings describing parsed commands</comment>
|
||||
</data>
|
||||
<data name="CommandPalette_FailedParsingCommandLine" xml:space="preserve">
|
||||
<value>Failed parsing command line:</value>
|
||||
@@ -559,4 +566,13 @@
|
||||
<data name="ClipboardTextHeader.Text" xml:space="preserve">
|
||||
<value>Clipboard contents (preview):</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="CommandPalette_MoreOptions.[using:Windows.UI.Xaml.Automation]AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>More options</value>
|
||||
</data>
|
||||
<data name="WindowMaximizeButtonToolTip" xml:space="preserve">
|
||||
<value>Maximize</value>
|
||||
</data>
|
||||
<data name="WindowRestoreDownButtonToolTip" xml:space="preserve">
|
||||
<value>Restore Down</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -60,12 +60,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_NewTabHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::NewWindow:
|
||||
{
|
||||
_NewWindowHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::CloseWindow:
|
||||
{
|
||||
_CloseWindowHandlers(*this, eventArgs);
|
||||
@@ -256,11 +250,21 @@ namespace winrt::TerminalApp::implementation
|
||||
_BreakIntoDebuggerHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::FindMatch:
|
||||
{
|
||||
_FindMatchHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::TogglePaneReadOnly:
|
||||
{
|
||||
_TogglePaneReadOnlyHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::NewWindow:
|
||||
{
|
||||
_NewWindowHandlers(*this, eventArgs);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(OpenNewTabDropdown, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(DuplicateTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(NewTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(NewWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(CloseWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(CloseTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(ClosePane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
@@ -65,7 +64,9 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(TabSearch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(MoveTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(BreakIntoDebugger, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(FindMatch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(TogglePaneReadOnly, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
TYPED_EVENT(NewWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> NewTab;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> OpenNewTabDropdown;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> DuplicateTab;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> NewWindow;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> CloseWindow;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> CloseTab;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> ClosePane;
|
||||
@@ -51,6 +50,8 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> TabSearch;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> MoveTab;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> BreakIntoDebugger;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> FindMatch;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> TogglePaneReadOnly;
|
||||
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, Microsoft.Terminal.Settings.Model.ActionEventArgs> NewWindow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace winrt::TerminalApp::implementation
|
||||
Controls::MenuFlyoutItem closeTabMenuItem;
|
||||
Controls::FontIcon closeSymbol;
|
||||
closeSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
closeSymbol.Glyph(L"\xE8BB");
|
||||
closeSymbol.Glyph(L"\xE711");
|
||||
|
||||
closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
|
||||
@@ -29,16 +29,16 @@ namespace winrt::TerminalApp::implementation
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
// The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector.
|
||||
GETSET_PROPERTY(uint32_t, TabViewIndex, 0);
|
||||
WINRT_PROPERTY(uint32_t, TabViewIndex, 0);
|
||||
// The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector.
|
||||
GETSET_PROPERTY(uint32_t, TabViewNumTabs, 0);
|
||||
WINRT_PROPERTY(uint32_t, TabViewNumTabs, 0);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, ReadOnly, _PropertyChangedHandlers, false);
|
||||
GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, ReadOnly, _PropertyChangedHandlers, false);
|
||||
WINRT_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
protected:
|
||||
winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace winrt::TerminalApp::implementation
|
||||
"TabRenamerOpened",
|
||||
TraceLoggingDescription("Event emitted when the tab renamer is opened"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -93,7 +93,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TraceLoggingDescription("Event emitted when the tab renamer is closed"),
|
||||
TraceLoggingBoolean(_renameCancelled, "CancelledRename", "True if the user cancelled the rename, false if they committed."),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
|
||||
_CloseRenameBox();
|
||||
if (!_renameCancelled)
|
||||
@@ -106,7 +106,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Hides the rename box and displays the title text block
|
||||
void TabHeaderControl::_CloseRenameBox()
|
||||
{
|
||||
HeaderRenamerTextBox().Visibility(Windows::UI::Xaml::Visibility::Collapsed);
|
||||
HeaderTextBlock().Visibility(Windows::UI::Xaml::Visibility::Visible);
|
||||
if (HeaderRenamerTextBox().Visibility() == Windows::UI::Xaml::Visibility::Visible)
|
||||
{
|
||||
HeaderRenamerTextBox().Visibility(Windows::UI::Xaml::Visibility::Collapsed);
|
||||
HeaderTextBlock().Visibility(Windows::UI::Xaml::Visibility::Visible);
|
||||
_RenameEndedHandlers(*this, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ namespace winrt::TerminalApp::implementation
|
||||
WINRT_CALLBACK(TitleChangeRequested, TerminalApp::TitleChangeRequestedArgs);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(double, RenamerMaxWidth, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(double, RenamerMaxWidth, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
|
||||
|
||||
TYPED_EVENT(RenameEnded, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
bool _receivedKeyDown{ false };
|
||||
|
||||
@@ -17,5 +17,6 @@ namespace TerminalApp
|
||||
TerminalTabStatus TabStatus { get; set; };
|
||||
|
||||
event TitleChangeRequestedArgs TitleChangeRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> RenameEnded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation
|
||||
return _tab.get();
|
||||
}
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::TerminalApp::TerminalTabStatus, TabStatus, _PropertyChangedHandlers);
|
||||
|
||||
private:
|
||||
winrt::weak_ref<winrt::TerminalApp::TabBase> _tab;
|
||||
|
||||
@@ -37,10 +37,11 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Placement="Mouse">
|
||||
<TextBlock IsTextSelectionEnabled="False">
|
||||
<Run x:Uid="NewTabRun"/> <LineBreak />
|
||||
<Run x:Uid="NewTabRun"/> <LineBreak/>
|
||||
<Run x:Uid="NewPaneRun"
|
||||
FontStyle="Italic">
|
||||
</Run>
|
||||
FontStyle="Italic"/> <LineBreak/>
|
||||
<Run x:Uid="NewWindowRun"
|
||||
FontStyle="Italic"/>
|
||||
</TextBlock>
|
||||
</ToolTip>
|
||||
</ToolTipService.ToolTip>
|
||||
|
||||
@@ -124,9 +124,6 @@
|
||||
<ClInclude Include="EmptyStringVisibilityConverter.h">
|
||||
<DependentUpon>EmptyStringVisibilityConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HasNestedCommandsVisibilityConverter.h">
|
||||
<DependentUpon>HasNestedCommandsVisibilityConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pane.h" />
|
||||
<ClInclude Include="ColorHelper.h" />
|
||||
<ClInclude Include="TerminalSettings.h">
|
||||
@@ -204,9 +201,6 @@
|
||||
<ClCompile Include="EmptyStringVisibilityConverter.cpp">
|
||||
<DependentUpon>EmptyStringVisibilityConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HasNestedCommandsVisibilityConverter.cpp">
|
||||
<DependentUpon>HasNestedCommandsVisibilityConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pane.cpp" />
|
||||
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
|
||||
<ClCompile Include="ColorHelper.cpp" />
|
||||
@@ -293,7 +287,6 @@
|
||||
</Midl>
|
||||
<Midl Include="FilteredCommand.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
<Midl Include="HasNestedCommandsVisibilityConverter.idl" />
|
||||
<Midl Include="TerminalSettings.idl" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Misc Files ======================== -->
|
||||
|
||||
@@ -93,21 +93,27 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI)
|
||||
{
|
||||
_settings = settings;
|
||||
if (needRefreshUI)
|
||||
{
|
||||
_RefreshUIForSettingsReload();
|
||||
}
|
||||
|
||||
// Upon settings update we reload the system settings for scrolling as well.
|
||||
// TODO: consider reloading this value periodically.
|
||||
_systemRowsToScroll = _ReadSystemRowsToScroll();
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
co_await winrt::resume_foreground(Dispatcher());
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
// Make sure to _UpdateCommandsForPalette before
|
||||
// _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make
|
||||
// sure the KeyChordText of Commands is updated, which needs to
|
||||
// happen before the Settings UI is reloaded and tries to re-read
|
||||
// those values.
|
||||
_UpdateCommandsForPalette();
|
||||
CommandPalette().SetKeyMap(_settings.KeyMap());
|
||||
|
||||
if (needRefreshUI)
|
||||
{
|
||||
_RefreshUIForSettingsReload();
|
||||
}
|
||||
|
||||
// Upon settings update we reload the system settings for scrolling as well.
|
||||
// TODO: consider reloading this value periodically.
|
||||
_systemRowsToScroll = _ReadSystemRowsToScroll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,8 +141,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
_tabRow.PointerMoved({ this, &TerminalPage::_RestorePointerCursorHandler });
|
||||
|
||||
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
_tabView.CanReorderTabs(!isElevated);
|
||||
_tabView.CanDragTabs(!isElevated);
|
||||
|
||||
@@ -201,6 +206,13 @@ namespace winrt::TerminalApp::implementation
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
|
||||
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
|
||||
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
|
||||
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
@@ -213,6 +225,10 @@ namespace winrt::TerminalApp::implementation
|
||||
0.5f,
|
||||
nullptr);
|
||||
}
|
||||
else if (shiftPressed && !debugTap)
|
||||
{
|
||||
page->_OpenNewWindow(false, NewTerminalArgs());
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(nullptr);
|
||||
@@ -261,7 +277,11 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Store cursor, so we can restore it, e.g., after mouse vanishing
|
||||
// (we'll need to adapt this logic once we make cursor context aware)
|
||||
_defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor();
|
||||
try
|
||||
{
|
||||
_defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor();
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -577,11 +597,16 @@ namespace winrt::TerminalApp::implementation
|
||||
auto newPaneRun = WUX::Documents::Run();
|
||||
newPaneRun.Text(RS_(L"NewPaneRun/Text"));
|
||||
newPaneRun.FontStyle(FontStyle::Italic);
|
||||
auto newWindowRun = WUX::Documents::Run();
|
||||
newWindowRun.Text(RS_(L"NewWindowRun/Text"));
|
||||
newWindowRun.FontStyle(FontStyle::Italic);
|
||||
|
||||
auto textBlock = WUX::Controls::TextBlock{};
|
||||
textBlock.Inlines().Append(newTabRun);
|
||||
textBlock.Inlines().Append(WUX::Documents::LineBreak{});
|
||||
textBlock.Inlines().Append(newPaneRun);
|
||||
textBlock.Inlines().Append(WUX::Documents::LineBreak{});
|
||||
textBlock.Inlines().Append(newWindowRun);
|
||||
|
||||
auto toolTip = WUX::Controls::ToolTip{};
|
||||
toolTip.Content(textBlock);
|
||||
@@ -599,6 +624,13 @@ namespace winrt::TerminalApp::implementation
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
|
||||
const auto rShiftState = window.GetKeyState(VirtualKey::RightShift);
|
||||
const auto lShiftState = window.GetKeyState(VirtualKey::LeftShift);
|
||||
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings.GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
@@ -611,6 +643,12 @@ namespace winrt::TerminalApp::implementation
|
||||
0.5f,
|
||||
newTerminalArgs);
|
||||
}
|
||||
else if (shiftPressed && !debugTap)
|
||||
{
|
||||
// Manually fill in the evaluated profile.
|
||||
newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(page->_settings.GetProfileForArgs(newTerminalArgs)));
|
||||
page->_OpenNewWindow(false, newTerminalArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(newTerminalArgs);
|
||||
@@ -649,7 +687,7 @@ namespace winrt::TerminalApp::implementation
|
||||
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
|
||||
newTabFlyout.Items().Append(settingsItem);
|
||||
|
||||
Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsFile };
|
||||
Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsUI };
|
||||
Microsoft::Terminal::Settings::Model::ActionAndArgs settingsAction{ ShortcutAction::OpenSettings, args };
|
||||
const auto settingsKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(settingsAction) };
|
||||
if (settingsKeyChord)
|
||||
@@ -838,6 +876,16 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
newTabImpl->DuplicateRequested([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_DuplicateTab(*tab);
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
|
||||
@@ -858,9 +906,22 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
newTabImpl->TabRenamerDeactivated([weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
|
||||
if (const auto page{ weakThis.get() })
|
||||
{
|
||||
if (!page->_newTabButton.Flyout().IsOpen())
|
||||
{
|
||||
if (const auto tab{ page->_GetFocusedTab() })
|
||||
{
|
||||
tab.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
TermControl newControl{ settings, debugConnection };
|
||||
TermControl newControl{ *(winrt::get_self<TerminalSettings>(settings)->CreateChild()), debugConnection };
|
||||
_RegisterTerminalEvents(newControl, *newTabImpl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl);
|
||||
@@ -967,12 +1028,30 @@ namespace winrt::TerminalApp::implementation
|
||||
const RoutedEventArgs&)
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
|
||||
// check alt state
|
||||
const auto rAltState{ window.GetKeyState(VirtualKey::RightMenu) };
|
||||
const auto lAltState{ window.GetKeyState(VirtualKey::LeftMenu) };
|
||||
const bool altPressed{ WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
// check shift state
|
||||
const auto shiftState{ window.GetKeyState(VirtualKey::Shift) };
|
||||
const auto lShiftState{ window.GetKeyState(VirtualKey::LeftShift) };
|
||||
const auto rShiftState{ window.GetKeyState(VirtualKey::RightShift) };
|
||||
const auto shiftPressed{ WI_IsFlagSet(shiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) };
|
||||
|
||||
auto target{ SettingsTarget::SettingsUI };
|
||||
if (shiftPressed)
|
||||
{
|
||||
target = SettingsTarget::SettingsFile;
|
||||
}
|
||||
else if (altPressed)
|
||||
{
|
||||
target = SettingsTarget::DefaultsFile;
|
||||
}
|
||||
_LaunchSettings(target);
|
||||
}
|
||||
|
||||
@@ -1115,7 +1194,9 @@ namespace winrt::TerminalApp::implementation
|
||||
_actionDispatch->TabSearch({ this, &TerminalPage::_HandleOpenTabSearch });
|
||||
_actionDispatch->MoveTab({ this, &TerminalPage::_HandleMoveTab });
|
||||
_actionDispatch->BreakIntoDebugger({ this, &TerminalPage::_HandleBreakIntoDebugger });
|
||||
_actionDispatch->FindMatch({ this, &TerminalPage::_HandleFindMatch });
|
||||
_actionDispatch->TogglePaneReadOnly({ this, &TerminalPage::_HandleTogglePaneReadOnly });
|
||||
_actionDispatch->NewWindow({ this, &TerminalPage::_HandleNewWindow });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1186,43 +1267,52 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Method Description:
|
||||
// - Duplicates the current focused tab
|
||||
void TerminalPage::_DuplicateTabViewItem()
|
||||
void TerminalPage::_DuplicateFocusedTab()
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
|
||||
const auto& profileGuid = terminalTab->GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
{
|
||||
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
|
||||
const auto workingDirectory = terminalTab->GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
settings.StartingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settings);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
_DuplicateTab(*terminalTab);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Duplicates specified tab
|
||||
// Arguments:
|
||||
// - tab: tab to duplicate
|
||||
void TerminalPage::_DuplicateTab(const TerminalTab& tab)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
|
||||
const auto& profileGuid = tab.GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
{
|
||||
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
settings.StartingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settings);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Look for the index of the input tabView in the tabs vector,
|
||||
// and call _RemoveTab
|
||||
@@ -1373,8 +1463,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// Add an event handler for when the terminal wants to set a progress indicator on the taskbar
|
||||
term.SetTaskbarProgress({ this, &TerminalPage::_SetTaskbarProgressHandler });
|
||||
|
||||
term.HidePointerCursor({ this, &TerminalPage::_HidePointerCursorHandler });
|
||||
term.RestorePointerCursor({ this, &TerminalPage::_RestorePointerCursorHandler });
|
||||
term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler });
|
||||
term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
|
||||
// Bind Tab events to the TermControl and the Tab's Pane
|
||||
hostingTab.Initialize(term);
|
||||
@@ -1823,7 +1913,7 @@ namespace winrt::TerminalApp::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
TermControl newControl{ controlSettings, controlConnection };
|
||||
TermControl newControl{ *(winrt::get_self<TerminalSettings>(controlSettings)->CreateChild()), controlConnection };
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl, *focusedTab);
|
||||
@@ -2131,7 +2221,7 @@ namespace winrt::TerminalApp::implementation
|
||||
try
|
||||
{
|
||||
auto parsed = winrt::Windows::Foundation::Uri(eventArgs.Uri().c_str());
|
||||
if (parsed.SchemeName() == L"http" || parsed.SchemeName() == L"https")
|
||||
if (_IsUriSupported(parsed))
|
||||
{
|
||||
ShellExecute(nullptr, L"open", eventArgs.Uri().c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
}
|
||||
@@ -2168,6 +2258,37 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines if the given URI is currently supported
|
||||
// Arguments:
|
||||
// - The parsed URI
|
||||
// Return value:
|
||||
// - True if we support it, false otherwise
|
||||
bool TerminalPage::_IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri)
|
||||
{
|
||||
if (parsedUri.SchemeName() == L"http" || parsedUri.SchemeName() == L"https")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (parsedUri.SchemeName() == L"file")
|
||||
{
|
||||
const auto host = parsedUri.Host();
|
||||
// If no hostname was provided or if the hostname was "localhost", Host() will return an empty string
|
||||
// and we allow it
|
||||
if (host == L"")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// TODO: by the OSC 8 spec, if a hostname (other than localhost) is provided, we _should_ be
|
||||
// comparing that value against what is returned by GetComputerNameExW and making sure they match.
|
||||
// However, ShellExecute does not seem to be happy with file URIs of the form
|
||||
// file://{hostname}/path/to/file.ext
|
||||
// and so while we could do the hostname matching, we do not know how to actually open the URI
|
||||
// if its given in that form. So for now we ignore all hostnames other than localhost
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TerminalPage::_ControlNoticeRaisedHandler(const IInspectable /*sender*/, const Microsoft::Terminal::TerminalControl::NoticeEventArgs eventArgs)
|
||||
{
|
||||
winrt::hstring message = eventArgs.Message();
|
||||
@@ -3182,8 +3303,15 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (_shouldMouseVanish && !_isMouseHidden)
|
||||
{
|
||||
CoreWindow::GetForCurrentThread().PointerCursor(nullptr);
|
||||
_isMouseHidden = true;
|
||||
if (auto window{ CoreWindow::GetForCurrentThread() })
|
||||
{
|
||||
try
|
||||
{
|
||||
window.PointerCursor(nullptr);
|
||||
_isMouseHidden = true;
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3195,8 +3323,15 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (_isMouseHidden)
|
||||
{
|
||||
CoreWindow::GetForCurrentThread().PointerCursor(_defaultPointerCursor);
|
||||
_isMouseHidden = false;
|
||||
if (auto window{ CoreWindow::GetForCurrentThread() })
|
||||
{
|
||||
try
|
||||
{
|
||||
window.PointerCursor(_defaultPointerCursor);
|
||||
_isMouseHidden = false;
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,8 @@ namespace winrt::TerminalApp::implementation
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, TerminalApp::TerminalSettings settings);
|
||||
|
||||
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
bool _displayingCloseDialog{ false };
|
||||
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
@@ -176,7 +178,8 @@ namespace winrt::TerminalApp::implementation
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::Profile> profiles,
|
||||
Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::ColorScheme> schemes);
|
||||
|
||||
void _DuplicateTabViewItem();
|
||||
void _DuplicateFocusedTab();
|
||||
void _DuplicateTab(const TerminalTab& tab);
|
||||
void _RemoveTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
|
||||
winrt::Windows::Foundation::IAsyncAction _RemoveTab(winrt::TerminalApp::TabBase tab);
|
||||
winrt::fire_and_forget _RemoveTabs(const std::vector<winrt::TerminalApp::TabBase> tabs);
|
||||
@@ -217,6 +220,8 @@ namespace winrt::TerminalApp::implementation
|
||||
const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs);
|
||||
|
||||
void _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::TerminalControl::OpenHyperlinkEventArgs eventArgs);
|
||||
bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
|
||||
|
||||
void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri);
|
||||
bool _CopyText(const bool singleLine, const Windows::Foundation::IReference<Microsoft::Terminal::TerminalControl::CopyFormat>& formats);
|
||||
|
||||
@@ -315,7 +320,9 @@ namespace winrt::TerminalApp::implementation
|
||||
void _HandleOpenTabSearch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
void _HandleMoveTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
void _HandleBreakIntoDebugger(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
void _HandleFindMatch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
void _HandleTogglePaneReadOnly(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
void _HandleNewWindow(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
|
||||
// Make sure to hook new actions up in _RegisterActionCallbacks!
|
||||
#pragma endregion
|
||||
|
||||
@@ -104,6 +104,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
settings.StartingTabColor(static_cast<uint32_t>(til::color(newTerminalArgs.TabColor().Value())));
|
||||
}
|
||||
if (newTerminalArgs.SuppressApplicationTitle())
|
||||
{
|
||||
settings.SuppressApplicationTitle(newTerminalArgs.SuppressApplicationTitle().Value());
|
||||
}
|
||||
}
|
||||
|
||||
return { profileGuid, settings };
|
||||
|
||||
@@ -50,23 +50,24 @@ namespace winrt::TerminalApp::implementation
|
||||
void ColorTable(std::array<uint32_t, 16> colors);
|
||||
std::array<uint32_t, 16> ColorTable();
|
||||
|
||||
GETSET_SETTING(uint32_t, DefaultForeground, DEFAULT_FOREGROUND_WITH_ALPHA);
|
||||
GETSET_SETTING(uint32_t, DefaultBackground, DEFAULT_BACKGROUND_WITH_ALPHA);
|
||||
GETSET_SETTING(uint32_t, SelectionBackground, DEFAULT_FOREGROUND);
|
||||
GETSET_SETTING(int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
|
||||
GETSET_SETTING(int32_t, InitialRows, 30);
|
||||
GETSET_SETTING(int32_t, InitialCols, 80);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, DefaultForeground, DEFAULT_FOREGROUND_WITH_ALPHA);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, DefaultBackground, DEFAULT_BACKGROUND_WITH_ALPHA);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, SelectionBackground, DEFAULT_FOREGROUND);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, InitialRows, 30);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, InitialCols, 80);
|
||||
|
||||
GETSET_SETTING(bool, SnapOnInput, true);
|
||||
GETSET_SETTING(bool, AltGrAliasing, true);
|
||||
GETSET_SETTING(uint32_t, CursorColor, DEFAULT_CURSOR_COLOR);
|
||||
GETSET_SETTING(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage);
|
||||
GETSET_SETTING(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
|
||||
GETSET_SETTING(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
|
||||
GETSET_SETTING(bool, CopyOnSelect, false);
|
||||
GETSET_SETTING(bool, FocusFollowMouse, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, SnapOnInput, true);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, AltGrAliasing, true);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, CursorColor, DEFAULT_CURSOR_COLOR);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Vintage);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, CopyOnSelect, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, InputServiceWarning, true);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, FocusFollowMouse, false);
|
||||
|
||||
GETSET_SETTING(Windows::Foundation::IReference<uint32_t>, TabColor, nullptr);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Windows::Foundation::IReference<uint32_t>, TabColor, nullptr);
|
||||
|
||||
// When set, StartingTabColor allows to create a terminal with a "sticky" tab color.
|
||||
// This color is prioritized above the TabColor (that is usually initialized based on profile settings).
|
||||
@@ -76,50 +77,44 @@ namespace winrt::TerminalApp::implementation
|
||||
// TODO: to ensure that this property is not populated during settings reload,
|
||||
// we should consider moving this property to a separate interface,
|
||||
// passed to the terminal only upon creation.
|
||||
GETSET_SETTING(Windows::Foundation::IReference<uint32_t>, StartingTabColor, nullptr);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Windows::Foundation::IReference<uint32_t>, StartingTabColor, nullptr);
|
||||
|
||||
// ------------------------ End of Core Settings -----------------------
|
||||
|
||||
GETSET_SETTING(hstring, ProfileName);
|
||||
GETSET_SETTING(bool, UseAcrylic, false);
|
||||
GETSET_SETTING(double, TintOpacity, 0.5);
|
||||
GETSET_SETTING(hstring, Padding, DEFAULT_PADDING);
|
||||
GETSET_SETTING(hstring, FontFace, DEFAULT_FONT_FACE);
|
||||
GETSET_SETTING(int32_t, FontSize, DEFAULT_FONT_SIZE);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, ProfileName);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, UseAcrylic, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, double, TintOpacity, 0.5);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, Padding, DEFAULT_PADDING);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, FontFace, DEFAULT_FONT_FACE);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, int32_t, FontSize, DEFAULT_FONT_SIZE);
|
||||
|
||||
GETSET_SETTING(winrt::Windows::UI::Text::FontWeight, FontWeight);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Text::FontWeight, FontWeight);
|
||||
|
||||
GETSET_SETTING(hstring, BackgroundImage);
|
||||
GETSET_SETTING(double, BackgroundImageOpacity, 1.0);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, BackgroundImage);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, double, BackgroundImageOpacity, 1.0);
|
||||
|
||||
GETSET_SETTING(winrt::Windows::UI::Xaml::Media::Stretch,
|
||||
BackgroundImageStretchMode,
|
||||
winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill);
|
||||
GETSET_SETTING(winrt::Windows::UI::Xaml::HorizontalAlignment,
|
||||
BackgroundImageHorizontalAlignment,
|
||||
winrt::Windows::UI::Xaml::HorizontalAlignment::Center);
|
||||
GETSET_SETTING(winrt::Windows::UI::Xaml::VerticalAlignment,
|
||||
BackgroundImageVerticalAlignment,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Center);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center);
|
||||
|
||||
GETSET_SETTING(Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::IKeyBindings, KeyBindings, nullptr);
|
||||
|
||||
GETSET_SETTING(hstring, Commandline);
|
||||
GETSET_SETTING(hstring, StartingDirectory);
|
||||
GETSET_SETTING(hstring, StartingTitle);
|
||||
GETSET_SETTING(bool, SuppressApplicationTitle);
|
||||
GETSET_SETTING(hstring, EnvironmentVariables);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, Commandline);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, StartingDirectory);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, StartingTitle);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, SuppressApplicationTitle);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, EnvironmentVariables);
|
||||
|
||||
GETSET_SETTING(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
|
||||
|
||||
GETSET_SETTING(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
|
||||
|
||||
GETSET_SETTING(bool, RetroTerminalEffect, false);
|
||||
GETSET_SETTING(bool, ForceFullRepaintRendering, false);
|
||||
GETSET_SETTING(bool, SoftwareRendering, false);
|
||||
GETSET_SETTING(bool, ForceVTInput, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, RetroTerminalEffect, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, ForceFullRepaintRendering, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, SoftwareRendering, false);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, bool, ForceVTInput, false);
|
||||
|
||||
GETSET_PROPERTY(hstring, PixelShaderPath);
|
||||
INHERITABLE_SETTING(TerminalApp::TerminalSettings, hstring, PixelShaderPath);
|
||||
|
||||
private:
|
||||
std::optional<std::array<uint32_t, COLOR_TABLE_SIZE>> _ColorTable;
|
||||
|
||||
@@ -104,15 +104,21 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
|
||||
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
|
||||
try
|
||||
{
|
||||
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
|
||||
// Make sure to try/catch this, because the LocalTests won't be
|
||||
// able to use this helper.
|
||||
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
|
||||
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
|
||||
{
|
||||
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +359,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Update the control to reflect the changed title
|
||||
_headerControl.Title(activeTitle);
|
||||
Automation::AutomationProperties::SetName(tab->TabViewItem(), activeTitle);
|
||||
_UpdateToolTip();
|
||||
}
|
||||
}
|
||||
@@ -372,7 +379,7 @@ namespace winrt::TerminalApp::implementation
|
||||
co_await winrt::resume_foreground(control.Dispatcher());
|
||||
|
||||
const auto currentOffset = control.GetScrollOffset();
|
||||
control.ScrollViewport(currentOffset + delta);
|
||||
control.ScrollViewport(::base::ClampAdd(currentOffset, delta));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -609,6 +616,19 @@ namespace winrt::TerminalApp::implementation
|
||||
tab->_RecalculateAndApplyReadOnly();
|
||||
}
|
||||
});
|
||||
|
||||
control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_focusState != FocusState::Unfocused)
|
||||
{
|
||||
if (const auto termControl{ sender.try_as<winrt::Microsoft::Terminal::TerminalControl::TermControl>() })
|
||||
{
|
||||
termControl.Focus(FocusState::Pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -722,25 +742,26 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
// Add a PaneRaiseVisualBell event handler to the Pane. When the pane emits this event,
|
||||
// we need to bubble it all the way to app host. In this part of the chain we bubble it
|
||||
// from the hosting tab to the page.
|
||||
// Add a PaneRaiseBell event handler to the Pane
|
||||
pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (visual)
|
||||
{
|
||||
// If visual is set, we need to bubble this event all the way to app host to flash the taskbar
|
||||
// In this part of the chain we bubble it from the hosting tab to the page
|
||||
tab->_TabRaiseVisualBellHandlers();
|
||||
}
|
||||
|
||||
tab->ShowBellIndicator(true);
|
||||
// Show the bell indicator in the tab header
|
||||
tab->ShowBellIndicator(true);
|
||||
|
||||
// If this tab is focused, activate the bell indicator timer, which will
|
||||
// remove the bell indicator once it fires
|
||||
// (otherwise, the indicator is removed when the tab gets focus)
|
||||
if (tab->_focusState != WUX::FocusState::Unfocused)
|
||||
{
|
||||
tab->ActivateBellIndicatorTimer();
|
||||
}
|
||||
// If this tab is focused, activate the bell indicator timer, which will
|
||||
// remove the bell indicator once it fires
|
||||
// (otherwise, the indicator is removed when the tab gets focus)
|
||||
if (tab->_focusState != WUX::FocusState::Unfocused)
|
||||
{
|
||||
tab->ActivateBellIndicatorTimer();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -820,11 +841,29 @@ namespace winrt::TerminalApp::implementation
|
||||
renameTabMenuItem.Icon(renameTabSymbol);
|
||||
}
|
||||
|
||||
Controls::MenuFlyoutItem duplicateTabMenuItem;
|
||||
{
|
||||
// "Duplicate Tab"
|
||||
Controls::FontIcon duplicateTabSymbol;
|
||||
duplicateTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
duplicateTabSymbol.Glyph(L"\xF5ED");
|
||||
|
||||
duplicateTabMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_DuplicateRequestedHandlers();
|
||||
}
|
||||
});
|
||||
duplicateTabMenuItem.Text(RS_(L"DuplicateTabText"));
|
||||
duplicateTabMenuItem.Icon(duplicateTabSymbol);
|
||||
}
|
||||
|
||||
// Build the menu
|
||||
Controls::MenuFlyout newTabFlyout;
|
||||
Controls::MenuFlyoutSeparator menuSeparator;
|
||||
newTabFlyout.Items().Append(chooseColorMenuItem);
|
||||
newTabFlyout.Items().Append(renameTabMenuItem);
|
||||
newTabFlyout.Items().Append(duplicateTabMenuItem);
|
||||
newTabFlyout.Items().Append(menuSeparator);
|
||||
newTabFlyout.Items().Append(_CreateCloseSubMenu());
|
||||
newTabFlyout.Items().Append(closeTabMenuItem);
|
||||
@@ -1182,4 +1221,5 @@ namespace winrt::TerminalApp::implementation
|
||||
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ namespace winrt::TerminalApp::implementation
|
||||
DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
|
||||
FORWARDED_TYPED_EVENT(TabRenamerDeactivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, (&_headerControl), RenameEnded);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pane> _rootPane{ nullptr };
|
||||
@@ -142,6 +144,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _RecalculateAndApplyReadOnly();
|
||||
|
||||
void _DuplicateTab();
|
||||
|
||||
friend class ::TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ namespace winrt::TerminalApp::implementation
|
||||
TerminalTabStatus() = default;
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
public:
|
||||
CursorPositionEventArgs() = default;
|
||||
|
||||
GETSET_PROPERTY(Windows::Foundation::Point, CurrentPosition);
|
||||
WINRT_PROPERTY(Windows::Foundation::Point, CurrentPosition);
|
||||
};
|
||||
|
||||
struct FontInfoEventArgs :
|
||||
@@ -24,11 +24,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
public:
|
||||
FontInfoEventArgs() = default;
|
||||
|
||||
GETSET_PROPERTY(Windows::Foundation::Size, FontSize);
|
||||
WINRT_PROPERTY(Windows::Foundation::Size, FontSize);
|
||||
|
||||
GETSET_PROPERTY(winrt::hstring, FontFace);
|
||||
WINRT_PROPERTY(winrt::hstring, FontFace);
|
||||
|
||||
GETSET_PROPERTY(Windows::UI::Text::FontWeight, FontWeight);
|
||||
WINRT_PROPERTY(Windows::UI::Text::FontWeight, FontWeight);
|
||||
};
|
||||
|
||||
struct TSFInputControl : TSFInputControlT<TSFInputControl>
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_blinkTimer{},
|
||||
_lastMouseClickTimestamp{},
|
||||
_lastMouseClickPos{},
|
||||
_lastMouseClickPosNoSelection{},
|
||||
_selectionNeedsToBeCopied{ false },
|
||||
_searchBox{ nullptr }
|
||||
{
|
||||
@@ -87,6 +88,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
|
||||
// GH#8969: pre-seed working directory to prevent potential races
|
||||
_terminal->SetWorkingDirectory(_settings.StartingDirectory());
|
||||
|
||||
auto pfnWarningBell = std::bind(&TermControl::_TerminalWarningBell, this);
|
||||
_terminal->SetWarningBellCallback(pfnWarningBell);
|
||||
|
||||
@@ -189,7 +193,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
|
||||
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
|
||||
|
||||
_ApplyUISettings();
|
||||
_ApplyUISettings(_settings);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -225,6 +229,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TermControl::SearchMatch(const bool goForward)
|
||||
{
|
||||
if (!_searchBox)
|
||||
{
|
||||
CreateSearchBoxControl();
|
||||
}
|
||||
else
|
||||
{
|
||||
_Search(_searchBox->TextBox().Text(), goForward, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Search text in text buffer. This is triggered if the user click
|
||||
// search button or press enter.
|
||||
@@ -234,7 +250,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - caseSensitive: boolean that represents if the current search is case sensitive
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
|
||||
void TermControl::_Search(const winrt::hstring& text,
|
||||
const bool goForward,
|
||||
const bool caseSensitive)
|
||||
{
|
||||
if (text.size() == 0 || _closing)
|
||||
{
|
||||
@@ -267,7 +285,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - RoutedEventArgs: not used
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
|
||||
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
RoutedEventArgs const& /*args*/)
|
||||
{
|
||||
_searchBox->Visibility(Visibility::Collapsed);
|
||||
|
||||
@@ -277,10 +296,72 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
// Method Description:
|
||||
// - Given new settings for this profile, applies the settings to the current terminal.
|
||||
// - This method is separate from UpdateSettings because there is an apparent optimizer
|
||||
// issue that causes one of our hstring -> wstring_view conversions to result in garbage,
|
||||
// but only from a coroutine context. See GH#8723.
|
||||
// - INVARIANT: This method must be called from the UI thread.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_UpdateSettingsOnUIThread()
|
||||
{
|
||||
if (_closing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
|
||||
// Update our control settings
|
||||
_ApplyUISettings(_settings);
|
||||
|
||||
// Update the terminal core with its new Core settings
|
||||
_terminal->UpdateSettings(_settings);
|
||||
|
||||
if (!_initializedTerminal)
|
||||
{
|
||||
// If we haven't initialized, there's no point in continuing.
|
||||
// Initialization will handle the renderer settings.
|
||||
return;
|
||||
}
|
||||
|
||||
// Update DxEngine settings under the lock
|
||||
_renderEngine->SetSelectionBackground(_settings.SelectionBackground());
|
||||
|
||||
_renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
|
||||
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
|
||||
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
|
||||
switch (_settings.AntialiasingMode())
|
||||
{
|
||||
case TextAntialiasingMode::Cleartype:
|
||||
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
||||
break;
|
||||
case TextAntialiasingMode::Aliased:
|
||||
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
||||
break;
|
||||
case TextAntialiasingMode::Grayscale:
|
||||
default:
|
||||
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
||||
break;
|
||||
}
|
||||
|
||||
// Refresh our font with the renderer
|
||||
const auto actualFontOldSize = _actualFont.GetSize();
|
||||
_UpdateFont();
|
||||
const auto actualFontNewSize = _actualFont.GetSize();
|
||||
if (actualFontNewSize != actualFontOldSize)
|
||||
{
|
||||
_RefreshSizeUnderLock();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Given Settings having been updated, applies the settings to the current terminal.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TermControl::UpdateSettings()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
@@ -292,49 +373,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// If 'weakThis' is locked, then we can safely work with 'this'
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
if (_closing)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Update our control settings
|
||||
_ApplyUISettings();
|
||||
|
||||
// Update the terminal core with its new Core settings
|
||||
_terminal->UpdateSettings(_settings);
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
|
||||
// Update DxEngine settings under the lock
|
||||
_renderEngine->SetSelectionBackground(_settings.SelectionBackground());
|
||||
|
||||
_renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
|
||||
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
|
||||
_renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings.SoftwareRendering());
|
||||
|
||||
switch (_settings.AntialiasingMode())
|
||||
{
|
||||
case TextAntialiasingMode::Cleartype:
|
||||
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
||||
break;
|
||||
case TextAntialiasingMode::Aliased:
|
||||
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
||||
break;
|
||||
case TextAntialiasingMode::Grayscale:
|
||||
default:
|
||||
_renderEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
||||
break;
|
||||
}
|
||||
|
||||
// Refresh our font with the renderer
|
||||
const auto actualFontOldSize = _actualFont.GetSize();
|
||||
_UpdateFont();
|
||||
const auto actualFontNewSize = _actualFont.GetSize();
|
||||
if (actualFontNewSize != actualFontOldSize)
|
||||
{
|
||||
_RefreshSizeUnderLock();
|
||||
}
|
||||
_UpdateSettingsOnUIThread();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,21 +423,21 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TermControl::_ApplyUISettings()
|
||||
void TermControl::_ApplyUISettings(const IControlSettings& newSettings)
|
||||
{
|
||||
_InitializeBackgroundBrush();
|
||||
|
||||
COLORREF bg = _settings.DefaultBackground();
|
||||
COLORREF bg = newSettings.DefaultBackground();
|
||||
_BackgroundColorChanged(bg);
|
||||
|
||||
// Apply padding as swapChainPanel's margin
|
||||
auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
|
||||
auto newMargin = _ParseThicknessFromPadding(newSettings.Padding());
|
||||
SwapChainPanel().Margin(newMargin);
|
||||
|
||||
// Initialize our font information.
|
||||
const auto fontFace = _settings.FontFace();
|
||||
const short fontHeight = gsl::narrow_cast<short>(_settings.FontSize());
|
||||
const auto fontWeight = _settings.FontWeight();
|
||||
const auto fontFace = newSettings.FontFace();
|
||||
const short fontHeight = gsl::narrow_cast<short>(newSettings.FontSize());
|
||||
const auto fontWeight = newSettings.FontWeight();
|
||||
// The font width doesn't terribly matter, we'll only be using the
|
||||
// height to look it up
|
||||
// The other params here also largely don't matter.
|
||||
@@ -410,12 +449,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
// set TSF Foreground
|
||||
Media::SolidColorBrush foregroundBrush{};
|
||||
foregroundBrush.Color(static_cast<til::color>(_settings.DefaultForeground()));
|
||||
foregroundBrush.Color(static_cast<til::color>(newSettings.DefaultForeground()));
|
||||
TSFInputControl().Foreground(foregroundBrush);
|
||||
TSFInputControl().Margin(newMargin);
|
||||
|
||||
// Apply settings for scrollbar
|
||||
if (_settings.ScrollState() == ScrollbarState::Hidden)
|
||||
if (newSettings.ScrollState() == ScrollbarState::Hidden)
|
||||
{
|
||||
// In the scenario where the user has turned off the OS setting to automatically hide scrollbars, the
|
||||
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
|
||||
@@ -1098,7 +1137,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// modifier key. We'll wait for a real keystroke to dismiss the
|
||||
// GH #7395 - don't dismiss selection when taking PrintScreen
|
||||
// selection.
|
||||
if (_terminal->IsSelectionActive() && !KeyEvent::IsModifierKey(vkey) && vkey != VK_SNAPSHOT)
|
||||
// GH#8522, GH#3758 - Only dismiss the selection on key _down_. If we
|
||||
// dismiss on key up, then there's chance that we'll immediately dismiss
|
||||
// a selection created by an action bound to a keydown.
|
||||
if (_terminal->IsSelectionActive() &&
|
||||
!KeyEvent::IsModifierKey(vkey) &&
|
||||
vkey != VK_SNAPSHOT &&
|
||||
keyDown)
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto leftWinKeyState = window.GetKeyState(VirtualKey::LeftWindows);
|
||||
@@ -1301,35 +1346,55 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
mode = ::Terminal::SelectionExpansionMode::Line;
|
||||
}
|
||||
|
||||
// Update the selection appropriately
|
||||
if (ctrlEnabled && multiClickMapper == 1 &&
|
||||
!(_terminal->GetHyperlinkAtPosition(terminalPosition).empty()))
|
||||
{
|
||||
_HyperlinkHandler(_terminal->GetHyperlinkAtPosition(terminalPosition));
|
||||
}
|
||||
else if (shiftEnabled && _terminal->IsSelectionActive())
|
||||
{
|
||||
// Shift+Click: only set expand on the "end" selection point
|
||||
_terminal->SetSelectionEnd(terminalPosition, mode);
|
||||
_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
else if (mode == ::Terminal::SelectionExpansionMode::Cell && !shiftEnabled)
|
||||
{
|
||||
// Single Click: reset the selection and begin a new one
|
||||
_terminal->ClearSelection();
|
||||
_singleClickTouchdownPos = cursorPosition;
|
||||
_selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multi-Click Selection: expand both "start" and "end" selection points
|
||||
_terminal->MultiClickSelection(terminalPosition, mode);
|
||||
_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
// Update the selection appropriately
|
||||
|
||||
_lastMouseClickTimestamp = point.Timestamp();
|
||||
_lastMouseClickPos = cursorPosition;
|
||||
_renderer->TriggerSelection();
|
||||
// Capture the position of the first click when no selection is active
|
||||
if (mode == ::Terminal::SelectionExpansionMode::Cell && !_terminal->IsSelectionActive())
|
||||
{
|
||||
_singleClickTouchdownPos = cursorPosition;
|
||||
_lastMouseClickPosNoSelection = cursorPosition;
|
||||
}
|
||||
|
||||
// We reset the active selection if one of the conditions apply:
|
||||
// - shift is not held
|
||||
// - GH#9384: the position is the same as of the first click starting the selection
|
||||
// (we need to reset selection on double-click or triple-click, so it captures the word or the line,
|
||||
// rather than extending the selection)
|
||||
if (_terminal->IsSelectionActive() && (!shiftEnabled || _lastMouseClickPosNoSelection == cursorPosition))
|
||||
{
|
||||
// Reset the selection
|
||||
_terminal->ClearSelection();
|
||||
_selectionNeedsToBeCopied = false; // there's no selection, so there's nothing to update
|
||||
}
|
||||
|
||||
if (shiftEnabled)
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
// If there is a selection we extend it using the selection mode
|
||||
// (expand the "end"selection point)
|
||||
_terminal->SetSelectionEnd(terminalPosition, mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no selection we establish it using the selected mode
|
||||
// (expand both "start" and "end" selection points)
|
||||
_terminal->MultiClickSelection(terminalPosition, mode);
|
||||
}
|
||||
_selectionNeedsToBeCopied = true;
|
||||
}
|
||||
|
||||
_lastMouseClickTimestamp = point.Timestamp();
|
||||
_lastMouseClickPos = cursorPosition;
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
}
|
||||
else if (point.Properties().IsRightButtonPressed())
|
||||
{
|
||||
@@ -1376,7 +1441,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
if (!_focused && _settings.FocusFollowMouse())
|
||||
{
|
||||
Focus(FocusState::Pointer);
|
||||
_FocusFollowMouseRequestedHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
@@ -1435,48 +1500,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
}
|
||||
|
||||
if (terminalPosition != _lastHoveredCell)
|
||||
{
|
||||
const auto uri = _terminal->GetHyperlinkAtPosition(terminalPosition);
|
||||
if (!uri.empty())
|
||||
{
|
||||
// Update the tooltip with the URI
|
||||
HoveredUri().Text(uri);
|
||||
|
||||
// Set the border thickness so it covers the entire cell
|
||||
const auto charSizeInPixels = CharacterDimensions();
|
||||
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
|
||||
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
|
||||
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
|
||||
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
||||
const til::point startPos{ terminalPosition.X, terminalPosition.Y };
|
||||
const til::size fontSize{ _actualFont.GetSize() };
|
||||
const til::point posInPixels{ startPos * fontSize };
|
||||
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
|
||||
const til::point locationInDIPs{ posInDIPs + marginsInDips };
|
||||
|
||||
// Move the border to the top left corner of the cell
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), (locationInDIPs.x() - SwapChainPanel().ActualOffset().x));
|
||||
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), (locationInDIPs.y() - SwapChainPanel().ActualOffset().y));
|
||||
}
|
||||
_lastHoveredCell = terminalPosition;
|
||||
|
||||
const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPosition);
|
||||
const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPosition);
|
||||
// If the hyperlink ID changed or the interval changed, trigger a redraw all
|
||||
// (so this will happen both when we move onto a link and when we move off a link)
|
||||
if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval))
|
||||
{
|
||||
_lastHoveredId = newId;
|
||||
_lastHoveredInterval = newInterval;
|
||||
_renderEngine->UpdateHyperlinkHoveredId(newId);
|
||||
_renderer->UpdateLastHoveredInterval(newInterval);
|
||||
_renderer->TriggerRedrawAll();
|
||||
}
|
||||
}
|
||||
_UpdateHoveredCell(terminalPosition);
|
||||
}
|
||||
else if (_focused && ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor)
|
||||
{
|
||||
@@ -3303,6 +3327,74 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Updates last hovered cell, renders / removes rendering of hyper-link if required
|
||||
// Arguments:
|
||||
// - terminalPosition: The terminal position of the pointer
|
||||
void TermControl::_UpdateHoveredCell(const std::optional<COORD>& terminalPosition)
|
||||
{
|
||||
if (terminalPosition == _lastHoveredCell)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastHoveredCell = terminalPosition;
|
||||
|
||||
if (terminalPosition.has_value())
|
||||
{
|
||||
const auto uri = _terminal->GetHyperlinkAtPosition(*terminalPosition);
|
||||
if (!uri.empty())
|
||||
{
|
||||
// Update the tooltip with the URI
|
||||
HoveredUri().Text(uri);
|
||||
|
||||
// Set the border thickness so it covers the entire cell
|
||||
const auto charSizeInPixels = CharacterDimensions();
|
||||
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
|
||||
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
|
||||
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
|
||||
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
||||
const til::point startPos{ terminalPosition->X, terminalPosition->Y };
|
||||
const til::size fontSize{ _actualFont.GetSize() };
|
||||
const til::point posInPixels{ startPos * fontSize };
|
||||
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
|
||||
const til::point locationInDIPs{ posInDIPs + marginsInDips };
|
||||
|
||||
// Move the border to the top left corner of the cell
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), (locationInDIPs.x() - SwapChainPanel().ActualOffset().x));
|
||||
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), (locationInDIPs.y() - SwapChainPanel().ActualOffset().y));
|
||||
}
|
||||
}
|
||||
|
||||
const uint16_t newId = terminalPosition.has_value() ? _terminal->GetHyperlinkIdAtPosition(*terminalPosition) : 0u;
|
||||
const auto newInterval = terminalPosition.has_value() ? _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition) : std::nullopt;
|
||||
|
||||
// If the hyperlink ID changed or the interval changed, trigger a redraw all
|
||||
// (so this will happen both when we move onto a link and when we move off a link)
|
||||
if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval))
|
||||
{
|
||||
_lastHoveredId = newId;
|
||||
_lastHoveredInterval = newInterval;
|
||||
_renderEngine->UpdateHyperlinkHoveredId(newId);
|
||||
_renderer->UpdateLastHoveredInterval(newInterval);
|
||||
_renderer->TriggerRedrawAll();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handle a mouse exited event, specifically clearing last hovered cell
|
||||
// and removing selection from hyper link if exists
|
||||
// Arguments:
|
||||
// - sender: not used
|
||||
// - args: event data
|
||||
void TermControl::_PointerExitedHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& /*e*/)
|
||||
{
|
||||
_UpdateHoveredCell(std::nullopt);
|
||||
}
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// Winrt events need a method for adding a callback to the event and removing the callback.
|
||||
// These macros will define them both for you.
|
||||
|
||||
@@ -133,6 +133,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
void CreateSearchBoxControl();
|
||||
|
||||
void SearchMatch(const bool goForward);
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
|
||||
@@ -186,6 +188,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
TYPED_EVENT(HidePointerCursor, IInspectable, IInspectable);
|
||||
TYPED_EVENT(RestorePointerCursor, IInspectable, IInspectable);
|
||||
TYPED_EVENT(ReadOnlyChanged, IInspectable, IInspectable);
|
||||
TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
@@ -244,7 +247,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
std::optional<winrt::Windows::Foundation::Point> _touchAnchor;
|
||||
|
||||
// Track the last cell we hovered over (used in pointerMovedHandler)
|
||||
COORD _lastHoveredCell;
|
||||
std::optional<COORD> _lastHoveredCell;
|
||||
// Track the last hyperlink ID we hovered over
|
||||
uint16_t _lastHoveredId;
|
||||
|
||||
@@ -258,6 +261,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
unsigned int _multiClickCounter;
|
||||
Timestamp _lastMouseClickTimestamp;
|
||||
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPos;
|
||||
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPosNoSelection;
|
||||
std::optional<winrt::Windows::Foundation::Point> _singleClickTouchdownPos;
|
||||
// This field tracks whether the selection has changed meaningfully
|
||||
// since it was last copied. It's generally used to prevent copyOnSelect
|
||||
@@ -269,7 +273,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
bool _isReadOnly{ false };
|
||||
|
||||
void _ApplyUISettings();
|
||||
void _ApplyUISettings(const IControlSettings&);
|
||||
void _UpdateSettingsOnUIThread();
|
||||
void _UpdateSystemParameterSettings() noexcept;
|
||||
void _InitializeBackgroundBrush();
|
||||
winrt::fire_and_forget _BackgroundColorChanged(const COLORREF color);
|
||||
@@ -283,6 +288,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _PointerPressedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
void _PointerMovedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
void _PointerReleasedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
void _PointerExitedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
void _MouseWheelHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
void _ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& e);
|
||||
void _GotFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
@@ -341,10 +347,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _CompositionCompleted(winrt::hstring text);
|
||||
void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs);
|
||||
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
|
||||
|
||||
winrt::fire_and_forget _AsyncCloseConnection();
|
||||
|
||||
winrt::fire_and_forget _RaiseReadOnlyWarning();
|
||||
|
||||
void _UpdateHoveredCell(const std::optional<COORD>& terminalPosition);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
|
||||
void CreateSearchBoxControl();
|
||||
|
||||
void SearchMatch(Boolean goForward);
|
||||
|
||||
void AdjustFontSize(Int32 fontSizeDelta);
|
||||
void ResetFontSize();
|
||||
|
||||
@@ -120,5 +122,6 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
Boolean ReadOnly { get; };
|
||||
void ToggleReadOnly();
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
Background="Transparent"
|
||||
PointerPressed="_PointerPressedHandler"
|
||||
PointerMoved="_PointerMovedHandler"
|
||||
PointerReleased="_PointerReleasedHandler">
|
||||
PointerReleased="_PointerReleasedHandler"
|
||||
PointerExited="_PointerExitedHandler">
|
||||
|
||||
<SwapChainPanel x:Name="SwapChainPanel"
|
||||
SizeChanged="_SwapChainSizeChanged"
|
||||
|
||||
@@ -70,6 +70,9 @@ namespace Microsoft::Terminal::Core
|
||||
virtual bool SetWorkingDirectory(std::wstring_view uri) noexcept = 0;
|
||||
virtual std::wstring_view GetWorkingDirectory() noexcept = 0;
|
||||
|
||||
virtual bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept = 0;
|
||||
virtual bool PopGraphicsRendition() noexcept = 0;
|
||||
|
||||
protected:
|
||||
ITerminalApi() = default;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <conattrs.hpp>
|
||||
|
||||
#include "../../buffer/out/textBuffer.hpp"
|
||||
#include "../../types/inc/sgrStack.hpp"
|
||||
#include "../../renderer/inc/BlinkingState.hpp"
|
||||
#include "../../terminal/parser/StateMachine.hpp"
|
||||
#include "../../terminal/input/terminalInput.hpp"
|
||||
@@ -124,6 +125,10 @@ public:
|
||||
bool SetTaskbarProgress(const size_t state, const size_t progress) noexcept override;
|
||||
bool SetWorkingDirectory(std::wstring_view uri) noexcept override;
|
||||
std::wstring_view GetWorkingDirectory() noexcept override;
|
||||
|
||||
bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override;
|
||||
bool PopGraphicsRendition() noexcept override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ITerminalInput
|
||||
@@ -347,6 +352,8 @@ private:
|
||||
COORD _ConvertToBufferCell(const COORD viewportPos) const;
|
||||
#pragma endregion
|
||||
|
||||
Microsoft::Console::VirtualTerminal::SgrStack _sgrStack;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TerminalCoreUnitTests::TerminalBufferTests;
|
||||
friend class TerminalCoreUnitTests::TerminalApiTest;
|
||||
|
||||
@@ -640,3 +640,31 @@ std::wstring_view Terminal::GetWorkingDirectory() noexcept
|
||||
{
|
||||
return _workingDirectory;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Saves the current text attributes to an internal stack.
|
||||
// Arguments:
|
||||
// - options, cOptions: if present, specify which portions of the current text attributes
|
||||
// should be saved. Only a small subset of GraphicsOptions are actually supported;
|
||||
// others are ignored. If no options are specified, all attributes are stored.
|
||||
// Return Value:
|
||||
// - true
|
||||
bool Terminal::PushGraphicsRendition(const VTParameters options) noexcept
|
||||
{
|
||||
_sgrStack.Push(_buffer->GetCurrentAttributes(), options);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Restores text attributes from the internal stack. If only portions of text attributes
|
||||
// were saved, combines those with the current attributes.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true
|
||||
bool Terminal::PopGraphicsRendition() noexcept
|
||||
{
|
||||
const TextAttribute current = _buffer->GetCurrentAttributes();
|
||||
_buffer->SetCurrentAttributes(_sgrStack.Pop(current));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ public:
|
||||
|
||||
bool SetGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override;
|
||||
|
||||
bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override;
|
||||
bool PopGraphicsRendition() noexcept override;
|
||||
|
||||
bool CursorPosition(const size_t line,
|
||||
const size_t column) noexcept override; // CUP
|
||||
|
||||
|
||||
@@ -276,3 +276,13 @@ bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) noexcept
|
||||
_terminalApi.SetTextAttributes(attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminalDispatch::PushGraphicsRendition(const VTParameters options) noexcept
|
||||
{
|
||||
return _terminalApi.PushGraphicsRendition(options);
|
||||
}
|
||||
|
||||
bool TerminalDispatch::PopGraphicsRendition() noexcept
|
||||
{
|
||||
return _terminalApi.PopGraphicsRendition();
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const noexcept
|
||||
|
||||
try
|
||||
{
|
||||
return _buffer->GetTextRects(_selection->start, _selection->end, _blockSelection);
|
||||
return _buffer->GetTextRects(_selection->start, _selection->end, _blockSelection, false);
|
||||
}
|
||||
CATCH_LOG();
|
||||
return result;
|
||||
|
||||
61
src/cascadia/TerminalSettingsEditor/Actions.cpp
Normal file
61
src/cascadia/TerminalSettingsEditor/Actions.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Actions.h"
|
||||
#include "Actions.g.cpp"
|
||||
#include "ActionsPageNavigationState.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
Actions::Actions()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_filteredActions = winrt::single_threaded_observable_vector<winrt::Microsoft::Terminal::Settings::Model::Command>();
|
||||
}
|
||||
|
||||
void Actions::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_State = e.Parameter().as<Editor::ActionsPageNavigationState>();
|
||||
|
||||
for (const auto& [k, command] : _State.Settings().GlobalSettings().Commands())
|
||||
{
|
||||
// Filter out nested commands, and commands that aren't bound to a
|
||||
// key. This page is currently just for displaying the actions that
|
||||
// _are_ bound to keys.
|
||||
if (command.HasNestedCommands() || command.KeyChordText().empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_filteredActions.Append(command);
|
||||
}
|
||||
}
|
||||
|
||||
Collections::IObservableVector<Command> Actions::FilteredActions()
|
||||
{
|
||||
return _filteredActions;
|
||||
}
|
||||
|
||||
void Actions::_OpenSettingsClick(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
|
||||
|
||||
_State.RequestOpenJson(target);
|
||||
}
|
||||
|
||||
}
|
||||
49
src/cascadia/TerminalSettingsEditor/Actions.h
Normal file
49
src/cascadia/TerminalSettingsEditor/Actions.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Actions.g.h"
|
||||
#include "ActionsPageNavigationState.g.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct ActionsPageNavigationState : ActionsPageNavigationStateT<ActionsPageNavigationState>
|
||||
{
|
||||
public:
|
||||
ActionsPageNavigationState(const Model::CascadiaSettings& settings) :
|
||||
_Settings{ settings } {}
|
||||
|
||||
void RequestOpenJson(const Model::SettingsTarget target)
|
||||
{
|
||||
_OpenJsonHandlers(nullptr, target);
|
||||
}
|
||||
|
||||
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
|
||||
TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget);
|
||||
};
|
||||
|
||||
struct Actions : ActionsT<Actions>
|
||||
{
|
||||
public:
|
||||
Actions();
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> FilteredActions();
|
||||
|
||||
WINRT_PROPERTY(Editor::ActionsPageNavigationState, State, nullptr);
|
||||
|
||||
private:
|
||||
friend struct ActionsT<Actions>; // for Xaml to bind events
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> _filteredActions{ nullptr };
|
||||
|
||||
void _OpenSettingsClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(Actions);
|
||||
}
|
||||
23
src/cascadia/TerminalSettingsEditor/Actions.idl
Normal file
23
src/cascadia/TerminalSettingsEditor/Actions.idl
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "EnumEntry.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass ActionsPageNavigationState
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
|
||||
void RequestOpenJson(Microsoft.Terminal.Settings.Model.SettingsTarget target);
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Actions : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
Actions();
|
||||
ActionsPageNavigationState State { get; };
|
||||
|
||||
IObservableVector<Microsoft.Terminal.Settings.Model.Command> FilteredActions { get; };
|
||||
|
||||
}
|
||||
}
|
||||
190
src/cascadia/TerminalSettingsEditor/Actions.xaml
Normal file
190
src/cascadia/TerminalSettingsEditor/Actions.xaml
Normal file
@@ -0,0 +1,190 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Page
|
||||
x:Class="Microsoft.Terminal.Settings.Editor.Actions"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<local:StringIsEmptyConverter x:Key="CommandKeyChordVisibilityConverter"/>
|
||||
|
||||
<!-- Template for actions. This is _heavily_ copied from the command
|
||||
palette, with modifications:
|
||||
* We don't need to use a HighlightedTextControl, because we're
|
||||
not filtering this list
|
||||
* We don't need the chevron for nested commands
|
||||
* We're not displaying the icon
|
||||
* We're binding directly to a Command, not a FilteredCommand
|
||||
|
||||
If we wanted to reuse the command palette's list more directly,
|
||||
that's theoretically possible, but then it would need to be
|
||||
lifted out of TerminalApp and either moved into the
|
||||
TerminalSettingsEditor or moved to it's own project consumed by
|
||||
both TSE and TerminalApp.
|
||||
-->
|
||||
<DataTemplate x:Key="GeneralItemTemplate" x:DataType="SettingsModel:Command">
|
||||
|
||||
<!-- This HorizontalContentAlignment="Stretch" is important
|
||||
to make sure it takes the entire width of the line -->
|
||||
<ListViewItem HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind KeyChordText, Mode=OneWay}">
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" ColumnSpacing="8" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<!-- icon -->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<!-- command label -->
|
||||
<ColumnDefinition Width="*"/>
|
||||
<!-- key chord -->
|
||||
<ColumnDefinition Width="32"/>
|
||||
<!-- gutter for scrollbar -->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Text="{x:Bind Name, Mode=OneWay}"/>
|
||||
|
||||
<!-- The block for the key chord is only visible
|
||||
when there's actual text set as the label. See
|
||||
CommandKeyChordVisibilityConverter for details.
|
||||
Inexplicably, we don't need to set the
|
||||
AutomationProperties to Raw here, unlike in the
|
||||
CommandPalette. We're not quite sure why.-->
|
||||
<Border Grid.Column="2"
|
||||
Visibility="{x:Bind KeyChordText,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource CommandKeyChordVisibilityConverter}}"
|
||||
Style="{ThemeResource KeyChordBorderStyle}"
|
||||
Padding="2,0,2,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center">
|
||||
|
||||
<TextBlock Style="{ThemeResource KeyChordTextBlockStyle}"
|
||||
FontSize="12"
|
||||
Text="{x:Bind KeyChordText, Mode=OneWay}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- These resources again, HEAVILY copied from the command palette -->
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<!-- TextBox colors !-->
|
||||
<SolidColorBrush x:Key="TextControlBackground" Color="#333333"/>
|
||||
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush" Color="#B5B5B5"/>
|
||||
<SolidColorBrush x:Key="TextControlForeground" Color="#B5B5B5"/>
|
||||
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#404040"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonForeground" Color="#B5B5B5"/>
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#404040"/>
|
||||
<SolidColorBrush x:Key="TextControlForegroundPointerOver" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#404040"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver" Color="#FF4343"/>
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#333333"/>
|
||||
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#404040"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPressed" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed" Color="#FF4343"/>
|
||||
|
||||
<!-- KeyChordText styles -->
|
||||
<Style x:Key="KeyChordBorderStyle" TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- TextBox colors !-->
|
||||
<SolidColorBrush x:Key="TextControlBackground" Color="#CCCCCC"/>
|
||||
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush" Color="#636363"/>
|
||||
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#636363"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonForeground" Color="#636363"/>
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#DADADA"/>
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#636363"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver" Color="#FF4343"/>
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#CCCCCC"/>
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#636363"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPressed" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed" Color="#FF4343"/>
|
||||
|
||||
<!-- KeyChordText styles -->
|
||||
<Style x:Key="KeyChordBorderStyle" TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
|
||||
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
|
||||
<Style x:Key="KeyChordBorderStyle" TargetType="Border"/>
|
||||
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock"/>
|
||||
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<TextBlock x:Uid="Globals_KeybindingsDisclaimer"
|
||||
Style="{StaticResource DisclaimerStyle}"/>
|
||||
|
||||
<!-- The Nav_OpenJSON resource just so happens to have a .Content
|
||||
and .Tooltip that are _exactly_ what we're looking for here. -->
|
||||
|
||||
<HyperlinkButton x:Uid="Nav_OpenJSON"
|
||||
Click="_OpenSettingsClick" />
|
||||
|
||||
<!-- Keybindings -->
|
||||
|
||||
<!-- NOTE: Globals_Keybindings.Header is not defined, because that
|
||||
would result in the page having "Keybindings" displayed twice, which
|
||||
looks quite redundant -->
|
||||
<ContentPresenter x:Uid="Globals_Keybindings" Margin="0">
|
||||
|
||||
<ListView HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
SelectionMode="None"
|
||||
IsItemClickEnabled="False"
|
||||
CanReorderItems="False"
|
||||
AllowDrop="False"
|
||||
ItemsSource="{x:Bind FilteredActions, Mode=OneWay}"
|
||||
ItemTemplate="{StaticResource GeneralItemTemplate}">
|
||||
</ListView>
|
||||
|
||||
</ContentPresenter>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</ScrollViewer>
|
||||
</Page>
|
||||
@@ -20,6 +20,14 @@ using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// The first 8 entries of the color table are non-bright colors, whereas the rest are bright.
|
||||
static constexpr uint8_t ColorTableDivider{ 8 };
|
||||
|
||||
static constexpr std::wstring_view ForegroundColorTag{ L"Foreground" };
|
||||
static constexpr std::wstring_view BackgroundColorTag{ L"Background" };
|
||||
static constexpr std::wstring_view CursorColorTag{ L"CursorColor" };
|
||||
static constexpr std::wstring_view SelectionBackgroundColorTag{ L"SelectionBackground" };
|
||||
|
||||
static const std::array<hstring, 16> TableColorNames = {
|
||||
RS_(L"ColorScheme_Black/Header"),
|
||||
RS_(L"ColorScheme_Red/Header"),
|
||||
@@ -53,9 +61,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
ColorSchemes::ColorSchemes() :
|
||||
_ColorSchemeList{ single_threaded_observable_vector<Model::ColorScheme>() },
|
||||
_CurrentColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() }
|
||||
_CurrentNonBrightColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() },
|
||||
_CurrentBrightColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() }
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Automation::AutomationProperties::SetName(ColorSchemeComboBox(), RS_(L"ColorScheme_Name/Header"));
|
||||
Automation::AutomationProperties::SetFullDescription(ColorSchemeComboBox(), RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
|
||||
ToolTipService::SetToolTip(ColorSchemeComboBox(), box_value(RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip")));
|
||||
|
||||
Automation::AutomationProperties::SetName(RenameButton(), RS_(L"Rename/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
|
||||
|
||||
Automation::AutomationProperties::SetName(NameBox(), RS_(L"ColorScheme_Name/Header"));
|
||||
Automation::AutomationProperties::SetFullDescription(NameBox(), RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
|
||||
ToolTipService::SetToolTip(NameBox(), box_value(RS_(L"ColorScheme_Name/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip")));
|
||||
|
||||
Automation::AutomationProperties::SetName(RenameAcceptButton(), RS_(L"RenameAccept/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
|
||||
Automation::AutomationProperties::SetName(RenameCancelButton(), RS_(L"RenameCancel/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
|
||||
Automation::AutomationProperties::SetName(AddNewButton(), RS_(L"ColorScheme_AddNewButton/Text"));
|
||||
Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"ColorScheme_DeleteButton/Text"));
|
||||
}
|
||||
|
||||
void ColorSchemes::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
@@ -70,9 +94,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// very accurately.
|
||||
for (uint8_t i = 0; i < TableColorNames.size(); ++i)
|
||||
{
|
||||
auto entry = winrt::make<ColorTableEntry>(i, Windows::UI::Color{ 0, 0, 0, 0 });
|
||||
_CurrentColorTable.Append(entry);
|
||||
const auto& entry{ winrt::make<ColorTableEntry>(i, Windows::UI::Color{ 0, 0, 0, 0 }) };
|
||||
if (i < ColorTableDivider)
|
||||
{
|
||||
_CurrentNonBrightColorTable.Append(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
_CurrentBrightColorTable.Append(entry);
|
||||
}
|
||||
}
|
||||
_CurrentForegroundColor = winrt::make<ColorTableEntry>(ForegroundColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
|
||||
_CurrentBackgroundColor = winrt::make<ColorTableEntry>(BackgroundColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
|
||||
_CurrentCursorColor = winrt::make<ColorTableEntry>(CursorColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
|
||||
_CurrentSelectionBackgroundColor = winrt::make<ColorTableEntry>(SelectionBackgroundColorTag, Windows::UI::Color{ 0, 0, 0, 0 });
|
||||
|
||||
// Try to look up the scheme that was navigated to. If we find it, immediately select it.
|
||||
const std::wstring lastNameFromNav{ _State.LastSelectedScheme() };
|
||||
@@ -85,6 +120,41 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
auto scheme = *it;
|
||||
ColorSchemeComboBox().SelectedItem(scheme);
|
||||
}
|
||||
|
||||
// populate color table grid
|
||||
const auto colorLabelStyle{ Resources().Lookup(winrt::box_value(L"ColorLabelStyle")).as<Windows::UI::Xaml::Style>() };
|
||||
const auto colorControlStyle{ Resources().Lookup(winrt::box_value(L"ColorControlStyle")).as<Windows::UI::Xaml::Style>() };
|
||||
const auto colorTableEntryTemplate{ Resources().Lookup(winrt::box_value(L"ColorTableEntryTemplate")).as<DataTemplate>() };
|
||||
auto setupColorControl = [colorTableEntryTemplate, colorControlStyle, colorTableGrid{ ColorTableGrid() }](const auto&& colorRef, const uint32_t& row, const uint32_t& col) {
|
||||
ContentControl colorControl{};
|
||||
colorControl.ContentTemplate(colorTableEntryTemplate);
|
||||
colorControl.Style(colorControlStyle);
|
||||
|
||||
Data::Binding binding{};
|
||||
binding.Source(colorRef);
|
||||
binding.Mode(Data::BindingMode::TwoWay);
|
||||
colorControl.SetBinding(ContentControl::ContentProperty(), binding);
|
||||
|
||||
colorTableGrid.Children().Append(colorControl);
|
||||
Grid::SetRow(colorControl, row);
|
||||
Grid::SetColumn(colorControl, col);
|
||||
};
|
||||
for (uint32_t row = 0; row < ColorTableGrid().RowDefinitions().Size(); ++row)
|
||||
{
|
||||
// color label
|
||||
TextBlock label{};
|
||||
label.Text(TableColorNames[row]);
|
||||
label.Style(colorLabelStyle);
|
||||
ColorTableGrid().Children().Append(label);
|
||||
Grid::SetRow(label, row);
|
||||
Grid::SetColumn(label, 0);
|
||||
|
||||
// regular color
|
||||
setupColorControl(_CurrentNonBrightColorTable.GetAt(row), row, 1);
|
||||
|
||||
// bright color
|
||||
setupColorControl(_CurrentBrightColorTable.GetAt(row), row, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -149,20 +219,52 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void ColorSchemes::ColorPickerChanged(IInspectable const& sender,
|
||||
ColorChangedEventArgs const& args)
|
||||
{
|
||||
if (auto picker = sender.try_as<ColorPicker>())
|
||||
if (const auto& picker{ sender.try_as<ColorPicker>() })
|
||||
{
|
||||
if (auto tag = picker.Tag())
|
||||
if (const auto& tag{ picker.Tag() })
|
||||
{
|
||||
auto index = winrt::unbox_value<uint8_t>(tag);
|
||||
CurrentColorScheme().SetColorTableEntry(index, args.NewColor());
|
||||
_CurrentColorTable.GetAt(index).Color(args.NewColor());
|
||||
if (const auto index{ tag.try_as<uint8_t>() })
|
||||
{
|
||||
CurrentColorScheme().SetColorTableEntry(*index, args.NewColor());
|
||||
if (index < ColorTableDivider)
|
||||
{
|
||||
_CurrentNonBrightColorTable.GetAt(*index).Color(args.NewColor());
|
||||
}
|
||||
else
|
||||
{
|
||||
_CurrentBrightColorTable.GetAt(*index - ColorTableDivider).Color(args.NewColor());
|
||||
}
|
||||
}
|
||||
else if (const auto stringTag{ tag.try_as<hstring>() })
|
||||
{
|
||||
if (stringTag == ForegroundColorTag)
|
||||
{
|
||||
CurrentColorScheme().Foreground(args.NewColor());
|
||||
_CurrentForegroundColor.Color(args.NewColor());
|
||||
}
|
||||
else if (stringTag == BackgroundColorTag)
|
||||
{
|
||||
CurrentColorScheme().Background(args.NewColor());
|
||||
_CurrentBackgroundColor.Color(args.NewColor());
|
||||
}
|
||||
else if (stringTag == CursorColorTag)
|
||||
{
|
||||
CurrentColorScheme().CursorColor(args.NewColor());
|
||||
_CurrentCursorColor.Color(args.NewColor());
|
||||
}
|
||||
else if (stringTag == SelectionBackgroundColorTag)
|
||||
{
|
||||
CurrentColorScheme().SelectionBackground(args.NewColor());
|
||||
_CurrentSelectionBackgroundColor.Color(args.NewColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ColorSchemes::CanDeleteCurrentScheme() const
|
||||
{
|
||||
if (const auto scheme{ CurrentColorScheme() })
|
||||
if (const auto& scheme{ CurrentColorScheme() })
|
||||
{
|
||||
// Only allow this color scheme to be deleted if it's not provided in-box
|
||||
const std::wstring myName{ scheme.Name() };
|
||||
@@ -295,14 +397,32 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
for (uint8_t i = 0; i < TableColorNames.size(); ++i)
|
||||
{
|
||||
_CurrentColorTable.GetAt(i).Color(colorScheme.Table()[i]);
|
||||
if (i < ColorTableDivider)
|
||||
{
|
||||
_CurrentNonBrightColorTable.GetAt(i).Color(colorScheme.Table()[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
_CurrentBrightColorTable.GetAt(i - ColorTableDivider).Color(colorScheme.Table()[i]);
|
||||
}
|
||||
}
|
||||
_CurrentForegroundColor.Color(colorScheme.Foreground());
|
||||
_CurrentBackgroundColor.Color(colorScheme.Background());
|
||||
_CurrentCursorColor.Color(colorScheme.CursorColor());
|
||||
_CurrentSelectionBackgroundColor.Color(colorScheme.SelectionBackground());
|
||||
}
|
||||
|
||||
ColorTableEntry::ColorTableEntry(uint8_t index, Windows::UI::Color color)
|
||||
{
|
||||
Name(TableColorNames[index]);
|
||||
Index(winrt::box_value<uint8_t>(index));
|
||||
Tag(winrt::box_value<uint8_t>(index));
|
||||
Color(color);
|
||||
}
|
||||
|
||||
ColorTableEntry::ColorTableEntry(std::wstring_view tag, Windows::UI::Color color)
|
||||
{
|
||||
Name(LocalizedNameForEnumName(L"ColorScheme_", tag, L"Text"));
|
||||
Tag(winrt::box_value(tag));
|
||||
Color(color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
ColorSchemesPageNavigationState(const Model::CascadiaSettings& settings) :
|
||||
_Settings{ settings } {}
|
||||
|
||||
GETSET_PROPERTY(Model::CascadiaSettings, Settings, nullptr);
|
||||
GETSET_PROPERTY(winrt::hstring, LastSelectedScheme, L"");
|
||||
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr);
|
||||
WINRT_PROPERTY(winrt::hstring, LastSelectedScheme, L"");
|
||||
};
|
||||
|
||||
struct ColorSchemes : ColorSchemesT<ColorSchemes>
|
||||
@@ -38,13 +38,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
bool CanDeleteCurrentScheme() const;
|
||||
void DeleteConfirmation_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
GETSET_PROPERTY(Editor::ColorSchemesPageNavigationState, State, nullptr);
|
||||
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Editor::ColorTableEntry>, CurrentColorTable, nullptr);
|
||||
GETSET_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::ColorScheme>, ColorSchemeList, nullptr);
|
||||
WINRT_PROPERTY(Editor::ColorSchemesPageNavigationState, State, nullptr);
|
||||
WINRT_PROPERTY(Model::ColorScheme, CurrentColorScheme, nullptr);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Editor::ColorTableEntry>, CurrentNonBrightColorTable, nullptr);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Editor::ColorTableEntry>, CurrentBrightColorTable, nullptr);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::ColorScheme>, ColorSchemeList, nullptr);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::ColorScheme, CurrentColorScheme, _PropertyChangedHandlers, nullptr);
|
||||
OBSERVABLE_GETSET_PROPERTY(bool, IsRenaming, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsRenaming, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentForegroundColor, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentBackgroundColor, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentCursorColor, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::ColorTableEntry, CurrentSelectionBackgroundColor, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
void _UpdateColorTable(const winrt::Microsoft::Terminal::Settings::Model::ColorScheme& colorScheme);
|
||||
@@ -56,11 +61,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
public:
|
||||
ColorTableEntry(uint8_t index, Windows::UI::Color color);
|
||||
ColorTableEntry(std::wstring_view tag, Windows::UI::Color color);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(IInspectable, Index, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(Windows::UI::Color, Color, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(IInspectable, Tag, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(Windows::UI::Color, Color, _PropertyChangedHandlers);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,24 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Boolean CanDeleteCurrentScheme { get; };
|
||||
Boolean IsRenaming { get; };
|
||||
|
||||
Microsoft.Terminal.Settings.Model.ColorScheme CurrentColorScheme { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<ColorTableEntry> CurrentColorTable;
|
||||
// Terminal Colors
|
||||
Windows.Foundation.Collections.IVector<ColorTableEntry> CurrentNonBrightColorTable { get; };
|
||||
Windows.Foundation.Collections.IVector<ColorTableEntry> CurrentBrightColorTable { get; };
|
||||
|
||||
// System Colors
|
||||
ColorTableEntry CurrentForegroundColor;
|
||||
ColorTableEntry CurrentBackgroundColor;
|
||||
ColorTableEntry CurrentCursorColor;
|
||||
ColorTableEntry CurrentSelectionBackgroundColor;
|
||||
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.ColorScheme> ColorSchemeList { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ColorTableEntry : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
String Name { get; };
|
||||
IInspectable Index;
|
||||
IInspectable Tag;
|
||||
Windows.UI.Color Color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,18 +17,25 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<ResourceDictionary Source="CommonResources.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Thickness x:Key="ColorSchemesStandardControlMargin">0,0,10,20</Thickness>
|
||||
|
||||
<Style x:Key="ColorStackPanelStyle" TargetType="StackPanel">
|
||||
<Setter Property="Margin" Value="{StaticResource ColorSchemesStandardControlMargin}"/>
|
||||
<Setter Property="Height" Value="59"/>
|
||||
<Style x:Key="GroupHeaderStyle" TargetType="TextBlock" BasedOn="{StaticResource SubtitleTextBlockStyle}">
|
||||
<Setter Property="Margin" Value="0,0,0,4"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ColorHeaderStyle" TargetType="TextBlock">
|
||||
<Setter Property="Margin" Value="0,0,0,5"/>
|
||||
<Style x:Key="ColorLabelStyle" TargetType="TextBlock">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
|
||||
<Style x:Key="ColorTableGridStyle" TargetType="Grid">
|
||||
<Setter Property="RowSpacing" Value="10"/>
|
||||
<Setter Property="ColumnSpacing" Value="10"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ColorControlStyle" TargetType="ContentControl">
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ColorButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource SystemBaseLowColor}"/>
|
||||
<Setter Property="Height" Value="30"/>
|
||||
<Setter Property="Width" Value="100"/>
|
||||
@@ -42,6 +49,36 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Setter Property="IsAlphaTextInputVisible" Value="True"/>
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="ColorTableEntryTemplate" x:DataType="local:ColorTableEntry">
|
||||
<Button Background="{x:Bind Color, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind Name}"
|
||||
AutomationProperties.Name="{x:Bind Name}"
|
||||
Style="{StaticResource ColorButtonStyle}">
|
||||
<Button.Resources>
|
||||
<!-- Resources to colorize hover/pressed states based on the color of the button -->
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<!-- No High contrast dictionary, let's just leave that unchanged. -->
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ColorPicker Tag="{x:Bind Tag, Mode=OneWay}"
|
||||
Color="{x:Bind Color, Mode=OneWay}"
|
||||
ColorChanged="ColorPickerChanged"/>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
|
||||
<local:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
|
||||
<local:ColorToHexConverter x:Key="ColorToHexConverter"/>
|
||||
<local:InvertedBooleanToVisibilityConverter x:Key="InvertedBooleanToVisibilityConverter"/>
|
||||
@@ -51,22 +88,27 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<Grid Margin="{StaticResource StandardIndentMargin}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" MaxWidth="480"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState>
|
||||
<VisualState.StateTriggers>
|
||||
<!--Official guidance states that 640 is the breakpoint between small and medium devices.
|
||||
Since MinWindowWidth is an inclusive range, we need to add 1 to it.-->
|
||||
<AdaptiveTrigger MinWindowWidth="641"/>
|
||||
</VisualState.StateTriggers>
|
||||
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ColorPanel.Orientation" Value="Horizontal"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<StackPanel Margin="{StaticResource StandardIndentMargin}"
|
||||
Spacing="24">
|
||||
|
||||
<!--Select Color and Add New Button-->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Visibility="{x:Bind IsRenaming, Converter={StaticResource InvertedBooleanToVisibilityConverter}, Mode=OneWay}">
|
||||
@@ -111,6 +153,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
|
||||
<!--Accept rename button-->
|
||||
<Button x:Uid="RenameAccept"
|
||||
x:Name="RenameAcceptButton"
|
||||
Style="{StaticResource AccentSmallButtonStyle}"
|
||||
Click="RenameAccept_Click">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@@ -120,6 +163,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</Button>
|
||||
<!--Cancel rename button-->
|
||||
<Button x:Uid="RenameCancel"
|
||||
x:Name="RenameCancelButton"
|
||||
Style="{StaticResource SmallButtonStyle}"
|
||||
Click="RenameCancel_Click">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@@ -130,7 +174,8 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</StackPanel>
|
||||
|
||||
<!--Add new button-->
|
||||
<Button Click="AddNew_Click"
|
||||
<Button x:Name="AddNewButton"
|
||||
Click="AddNew_Click"
|
||||
Style="{StaticResource BrowseButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon Glyph=""
|
||||
@@ -141,181 +186,109 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Color Table (Left Column)-->
|
||||
<ItemsControl x:Name="ColorTableControl"
|
||||
ItemsSource="{x:Bind CurrentColorTable, Mode=OneWay}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource ItemsControlStyle}">
|
||||
<!-- Terminal Colors (Left Column)-->
|
||||
<StackPanel x:Name="ColorPanel"
|
||||
Spacing="48">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="ColorScheme_TerminalColorsHeader"
|
||||
Style="{StaticResource GroupHeaderStyle}"/>
|
||||
<Grid x:Name="ColorTableGrid"
|
||||
Style="{StaticResource ColorTableGridStyle}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<!--Labels-->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsWrapGrid Orientation="Horizontal"
|
||||
MaximumRowsOrColumns="4"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:ColorTableEntry">
|
||||
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}"
|
||||
Style="{StaticResource ColorHeaderStyle}"/>
|
||||
<Button Background="{x:Bind Color, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
|
||||
<!--Regular Colors-->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
|
||||
<!--Bright Colors-->
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button.Resources>
|
||||
<!-- Resources to colorize hover/pressed states based on the color of the button -->
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{x:Bind Color, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<!-- No High contrast dictionary, let's just leave that unchanged. -->
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ColorPicker Tag="{x:Bind Index, Mode=OneWay}"
|
||||
Color="{x:Bind Color, Mode=OneWay}"
|
||||
ColorChanged="ColorPickerChanged"/>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Additional Colors (Right Column) -->
|
||||
<ItemsControl Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource ItemsControlStyle}">
|
||||
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
|
||||
<TextBlock x:Uid="ColorScheme_Foreground"
|
||||
Style="{StaticResource ColorHeaderStyle}"/>
|
||||
<Button Background="{Binding Color, ElementName=ForegroundPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
|
||||
|
||||
|
||||
<Button.Resources>
|
||||
<!-- Resources to colorize hover/pressed states based on the color of the button -->
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=ForegroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=ForegroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<!-- No High contrast dictionary, let's just leave that unchanged. -->
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ColorPicker x:Name="ForegroundPicker" Color="{x:Bind CurrentColorScheme.Foreground, Mode=TwoWay}"/>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
|
||||
<TextBlock x:Uid="ColorScheme_Background"
|
||||
Style="{StaticResource ColorHeaderStyle}"/>
|
||||
<Button Background="{Binding Color, ElementName=BackgroundPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
|
||||
|
||||
<!-- System Colors (Right Column) -->
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="ColorScheme_SystemColorsHeader"
|
||||
Style="{StaticResource GroupHeaderStyle}"/>
|
||||
<Grid Style="{StaticResource ColorTableGridStyle}">
|
||||
|
||||
<Button.Resources>
|
||||
<!-- Resources to colorize hover/pressed states based on the color of the button -->
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=BackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=BackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<!-- No High contrast dictionary, let's just leave that unchanged. -->
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ColorPicker x:Name="BackgroundPicker"
|
||||
Color="{x:Bind CurrentColorScheme.Background, Mode=TwoWay}"/>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!--Foreground-->
|
||||
<TextBlock x:Uid="ColorScheme_Foreground"
|
||||
Style="{StaticResource ColorLabelStyle}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"/>
|
||||
<ContentControl x:Name="ForegroundButton"
|
||||
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
|
||||
Content="{x:Bind CurrentForegroundColor, Mode=TwoWay}"
|
||||
Style="{StaticResource ColorControlStyle}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"/>
|
||||
|
||||
<!--Background-->
|
||||
<TextBlock x:Uid="ColorScheme_Background"
|
||||
Style="{StaticResource ColorLabelStyle}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"/>
|
||||
<ContentControl x:Name="BackgroundButton"
|
||||
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
|
||||
Content="{x:Bind CurrentBackgroundColor, Mode=TwoWay}"
|
||||
Style="{StaticResource ColorControlStyle}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
|
||||
<!--Cursor Color-->
|
||||
<TextBlock x:Uid="ColorScheme_CursorColor"
|
||||
Style="{StaticResource ColorLabelStyle}"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"/>
|
||||
<ContentControl x:Name="CursorColorButton"
|
||||
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
|
||||
Content="{x:Bind CurrentCursorColor, Mode=TwoWay}"
|
||||
Style="{StaticResource ColorControlStyle}"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"/>
|
||||
|
||||
<!--Selection Background-->
|
||||
<TextBlock x:Uid="ColorScheme_SelectionBackground"
|
||||
Style="{StaticResource ColorLabelStyle}"
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"/>
|
||||
<ContentControl x:Name="SelectionBackgroundButton"
|
||||
ContentTemplate="{StaticResource ColorTableEntryTemplate}"
|
||||
Content="{x:Bind CurrentSelectionBackgroundColor, Mode=TwoWay}"
|
||||
Style="{StaticResource ColorControlStyle}"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
|
||||
<TextBlock x:Uid="ColorScheme_CursorColor"
|
||||
Style="{StaticResource ColorHeaderStyle}"/>
|
||||
<Button Background="{Binding Color, ElementName=CursorColorPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
|
||||
|
||||
|
||||
<Button.Resources>
|
||||
<!-- Resources to colorize hover/pressed states based on the color of the button -->
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=CursorColorPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=CursorColorPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<!-- No High contrast dictionary, let's just leave that unchanged. -->
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ColorPicker x:Name="CursorColorPicker"
|
||||
Color="{x:Bind CurrentColorScheme.CursorColor, Mode=TwoWay}"/>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Style="{StaticResource ColorStackPanelStyle}">
|
||||
<TextBlock x:Uid="ColorScheme_SelectionBackground"
|
||||
Style="{StaticResource ColorHeaderStyle}"/>
|
||||
<Button Background="{Binding Color, ElementName=SelectionBackgroundPicker, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
|
||||
|
||||
|
||||
<Button.Resources>
|
||||
<!-- Resources to colorize hover/pressed states based on the color of the button -->
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=SelectionBackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="{Binding Color, ElementName=SelectionBackgroundPicker, Converter={StaticResource ColorLightenConverter}, Mode=OneWay}"/>
|
||||
</ResourceDictionary>
|
||||
<!-- No High contrast dictionary, let's just leave that unchanged. -->
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<ColorPicker x:Name="SelectionBackgroundPicker"
|
||||
Color="{x:Bind CurrentColorScheme.SelectionBackground, Mode=TwoWay}"/>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!--Delete Button-->
|
||||
<StackPanel Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Style="{StaticResource PivotStackStyle}">
|
||||
<StackPanel Style="{StaticResource PivotStackStyle}">
|
||||
<Button x:Name="DeleteButton"
|
||||
IsEnabled="{x:Bind CanDeleteCurrentScheme, Mode=OneWay}"
|
||||
Style="{StaticResource DeleteButtonStyle}">
|
||||
@@ -371,6 +344,6 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
Style="{StaticResource DisclaimerStyle}"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Page>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user