Compare commits

..

4 Commits

171 changed files with 3522 additions and 6722 deletions

View File

@@ -1,9 +1,8 @@
# Microsoft Open Source Code of Conduct
# Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code].
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
[conduct-email]: mailto:opencode@microsoft.com

View File

@@ -294,12 +294,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{D3EF
build\scripts\Test-WindowsTerminalPackage.ps1 = build\scripts\Test-WindowsTerminalPackage.ps1
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dx.Unit.Tests", "src\renderer\dx\ut_dx\Dx.Unit.Tests.vcxproj", "{95B136F9-B238-490C-A7C5-5843C1FECAC4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.Tests.Feature", "src\winconpty\ft_pty\winconpty.FeatureTests.vcxproj", "{024052DE-83FB-4653-AEA4-90790D29D5BD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj", "{067F0A06-FCB7-472C-96E9-B03B54E8E18D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@@ -1425,66 +1419,6 @@ Global
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x64.Build.0 = Release|x64
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.ActiveCfg = Release|Win32
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.Build.0 = Release|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x64.ActiveCfg = Release|x64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.AuditMode|x86.Build.0 = AuditMode|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|Any CPU.ActiveCfg = Debug|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|ARM64.ActiveCfg = Debug|ARM64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|ARM64.Build.0 = Debug|ARM64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x64.ActiveCfg = Debug|x64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x64.Build.0 = Debug|x64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x86.ActiveCfg = Debug|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Debug|x86.Build.0 = Debug|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|Any CPU.ActiveCfg = Release|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|ARM64.ActiveCfg = Release|ARM64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|ARM64.Build.0 = Release|ARM64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x64.ActiveCfg = Release|x64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x64.Build.0 = Release|x64
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x86.ActiveCfg = Release|Win32
{95B136F9-B238-490C-A7C5-5843C1FECAC4}.Release|x86.Build.0 = Release|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x64.ActiveCfg = Release|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.AuditMode|x86.Build.0 = AuditMode|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|Any CPU.ActiveCfg = Debug|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.Build.0 = Debug|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x64.ActiveCfg = Debug|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x64.Build.0 = Debug|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x86.ActiveCfg = Debug|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x86.Build.0 = Debug|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|Any CPU.ActiveCfg = Release|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|ARM64.ActiveCfg = Release|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|ARM64.Build.0 = Release|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.ActiveCfg = Release|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.Build.0 = Release|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.ActiveCfg = Release|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.Build.0 = Release|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x64.ActiveCfg = Release|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.AuditMode|x86.Build.0 = AuditMode|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.Build.0 = Debug|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x64.ActiveCfg = Debug|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x64.Build.0 = Debug|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x86.ActiveCfg = Debug|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x86.Build.0 = Debug|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|Any CPU.ActiveCfg = Release|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|ARM64.ActiveCfg = Release|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|ARM64.Build.0 = Release|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x64.ActiveCfg = Release|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x64.Build.0 = Release|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x86.ActiveCfg = Release|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1559,9 +1493,6 @@ Global
{53DD5520-E64C-4C06-B472-7CE62CA539C9} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{6B5A44ED-918D-4747-BFB1-2472A1FCA173} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{D3EF7B96-CD5E-47C9-B9A9-136259563033} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{95B136F9-B238-490C-A7C5-5843C1FECAC4} = {05500DEF-2294-41E3-AF9A-24E580B82836}
{024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}

View File

@@ -17,15 +17,7 @@ Related repositories include:
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
### Microsoft Store [Recommended]
Install the [Windows Terminal from the Microsoft Store][store-install-link]. This allows you to always be on the latest version when we release new builds with automatic upgrades.
This is our preferred method.
### Other install methods
#### Via GitHub
### Manually installing builds from this repository
For users who are unable to install Terminal from the Microsoft Store, Terminal builds can be manually downloaded from this repository's [Releases page](https://github.com/microsoft/terminal/releases).
@@ -34,7 +26,7 @@ For users who are unable to install Terminal from the Microsoft Store, Terminal
> * Be sure to install the [Desktop Bridge VC++ v14 Redistributable Package](https://www.microsoft.com/en-us/download/details.aspx?id=53175) otherwise Terminal may not install and/or run and may crash at startup
> * Terminal will not auto-update when new builds are released so you will need to regularly install the latest Terminal release to receive all the latest fixes and improvements!
#### Via Chocolatey (unofficial)
### Install via Chocolatey (unofficial)
[Chocolatey](https://chocolatey.org) users can download and install the latest Terminal release by installing the `microsoft-windows-terminal` package:
@@ -227,4 +219,3 @@ For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [open
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
[conduct-email]: mailto:opencode@microsoft.com
[store-install-link]: https://aka.ms/windowsterminal

View File

@@ -6,7 +6,6 @@ jobs:
steps:
- checkout: self
fetchDepth: 1
submodules: false
clean: true

View File

@@ -1,38 +1,13 @@
# How to build OpenConsole
# How to build Openconsole
This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for some of its dependencies. To make sure submodules are restored or updated, be sure to run the following prior to building:
```shell
git submodule update --init --recursive
```
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and PowerShell in /tools.
When using Visual Studio, be sure to set up the path for code formatting. This can be done in Visual Studio by going to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" and choosing the clang-format.exe in the repository at /dep/llvm/clang-format.exe by clicking "browse" right under the check box.
### Building in PowerShell
## Building with cmd
```powershell
Import-Module .\tools\OpenConsole.psm1
Set-MsBuildDevEnvironment
Invoke-OpenConsoleBuild
```
There are a few additional exported functions (look at their documentation for further details):
- `Invoke-OpenConsoleBuild` - builds the solution. Can be passed msbuild arguments.
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
- `Invoke-CodeFormat` - uses clang-format to format all c++ files to match our coding style.
### Building in Cmd
```shell
.\tools\razzle.cmd
bcz
```
The cmd scripts are set up to emulate a portion of the OS razzle build environment. razzle.cmd is the first script that should be run. bcz.cmd will build clean and bz.cmd should build incrementally.
There are also scripts for running the tests:
- `runut.cmd` - run the unit tests
@@ -40,13 +15,15 @@ There are also scripts for running the tests:
- `runuia.cmd` - run the UIA tests
- `runformat` - uses clang-format to format all c++ files to match our coding style.
## Running & Debugging
## Build with Powershell
To debug the Windows Terminal in VS, right click on `CascadiaPackage` (in the Solution Explorer) and go to properties. In the Debug menu, change "Application process" and "Background task process" to "Native Only".
Openconsole.psm1 should be loaded with `Import-Module`. From there `Set-MsbuildDevEnvironment` will set up environment variables required to build. There are a few exported functions (look at their documentation for further details):
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
> 👉 You will _not_ be able to launch the Terminal directly by running the WindowsTerminal.exe. For more details on why, see [#926](https://github.com/microsoft/terminal/issues/926), [#4043](https://github.com/microsoft/terminal/issues/4043)
- `Invoke-OpenConsolebuild` - builds the solution. Can be passed msbuild arguments.
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
- `Invoke-CodeFormat` - uses clang-format to format all c++ files to match our coding style.
## Configuration Types

View File

@@ -29,7 +29,6 @@ Properties listed below are specific to each unique profile.
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
| `name` | _Required_ | String | | Name of the profile. Displays in the dropdown menu. <br>Additionally, this value will be used as the "title" to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. This "title" behavior can be overridden by using `tabTitle`. |
| `acrylicOpacity` | Optional | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
| `antialiasingMode` | Optional | String | `"grayscale"` | Controls how text is antialiased in the renderer. Possible values are "grayscale", "cleartype" and "aliased". Note that changing this setting will require starting a new terminal instance. |
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
| `backgroundImage` | Optional | String | | Sets the file location of the Image to draw over the window background. |
| `backgroundImageAlignment` | Optional | String | `center` | Sets how the background image aligns to the boundaries of the window. Possible values: `"center"`, `"left"`, `"top"`, `"right"`, `"bottom"`, `"topLeft"`, `"topRight"`, `"bottomLeft"`, `"bottomRight"` |

View File

@@ -3,10 +3,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Microsoft's Windows Terminal Settings Profile Schema'",
"definitions": {
"KeyChordSegment": {
"pattern": "^(?<modifier>(ctrl|alt|shift)\\+?((ctrl|alt|shift)(?<!\\2)\\+?)?((ctrl|alt|shift)(?<!\\2|\\4))?\\+?)?(?<key>[^+\\s]+?)?(?<=[^+\\s])$",
"type": "string"
},
"Color": {
"default": "#",
"pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
@@ -248,18 +244,12 @@
},
"keys": {
"description": "Defines the key combinations used to call the command.",
"oneOf": [
{
"$ref": "#/definitions/KeyChordSegment"
},
{
"items": {
"$ref": "#/definitions/KeyChordSegment"
},
"minItems": 1,
"type": "array"
}
]
"items": {
"pattern": "^(?<modifier>(ctrl|alt|shift)\\+?((ctrl|alt|shift)(?<!\\2)\\+?)?((ctrl|alt|shift)(?<!\\2|\\4))?\\+?)?(?<key>[^+\\s]+?)?(?<=[^+\\s])$",
"type": "string"
},
"minItems": 1,
"type": "array"
}
},
"required": [
@@ -388,16 +378,6 @@
"minimum": 0,
"type": "number"
},
"antialiasingMode": {
"default": "grayscale",
"description": "Controls how text is antialiased in the renderer. Possible values are \"grayscale\", \"cleartype\" and \"aliased\". Note that changing this setting will require starting a new terminal instance.",
"enum": [
"grayscale",
"cleartype",
"aliased"
],
"type": "string"
},
"background": {
"$ref": "#/definitions/Color",
"default": "#0c0c0c",

View File

@@ -34,7 +34,7 @@ Ultimately, we're aiming for Terminal v1.0 to be feature-complete by Dec 2019, a
| 2020-01-28 | Beta 1 | Pri 0/1/2 Bug fixes & polish |
| 2020-02-25 | Beta 2 | Pri 0/1 Bug fixes & polish |
| 2020-03-24 | RC | Pri 0 bug fixes |
| 2020-05 | v1.0 | Terminal v1.0 Release |
| 2020-04-01 ? | v1.0 | Terminal v1.0 Release |
## GitHub Milestones

View File

@@ -15,7 +15,7 @@ Assuming that you've installed Anaconda into `%USERPROFILE%\Anaconda3`:
```json
{
"commandline" : "cmd.exe /k \"%USERPROFILE%\\Anaconda3\\Scripts\\activate.bat %USERPROFILE%\\Anaconda3\"",
"commandline" : "cmd.exe /K %USERPROFILE%\\Anaconda3\\Scripts\\activate.bat %USERPROFILE%\\Anaconda3",
"icon" : "%USERPROFILE%/Anaconda3/Menu/anaconda-navigator.ico",
"name" : "Anaconda3",
"startingDirectory" : "%USERPROFILE%"
@@ -28,7 +28,7 @@ Assuming that you've installed cmder into `%CMDER_ROOT%`:
```json
{
"commandline" : "cmd.exe /k \"%CMDER_ROOT%\\vendor\\init.bat\"",
"commandline" : "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
"name" : "cmder",
"startingDirectory" : "%USERPROFILE%"
}

View File

@@ -22,7 +22,6 @@ pass, and gives some examples of how to use the `wt` commandline.
### Options
#### `--help,-h,-?,/?,`
Display the help message.
## Subcommands
@@ -36,9 +35,9 @@ opens a new window. Subsequent `new-tab` commands will all open new tabs in the
same window.
**Parameters**:
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
#### `split-pane`
`split-pane [--target,-t target-pane] [-H]|[-V] [terminal_parameters]`
@@ -47,7 +46,6 @@ Creates a new pane in the currently focused tab by splitting the given pane
vertically or horizontally.
**Parameters**:
* `--target,-t target-pane`: Creates a new split in the given `target-pane`.
Each pane has a unique index (per-tab) which can be used to identify them.
These indicies are assigned in the order the panes were created. If omitted,
@@ -74,6 +72,7 @@ Moves focus to a given tab.
* `-p,--previous`: Move focus to the previous tab. Will display an error if
combined with either of `--next` or `--target`.
#### `[terminal_parameters]`
Some of the preceding commands are used to create a new terminal instance.
@@ -93,11 +92,12 @@ following:
selected profile. If the user wants to use a `;` in this commandline, it
should be escaped as `\;`.
## Examples
### Open Windows Terminal in the current directory
```powershell
```
wt -d .
```
@@ -114,12 +114,11 @@ the `split-pane` command to create new panes.
Consider the following commandline:
```powershell
```
wt ; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe
```
This creates a new Windows Terminal window with one tab, and 3 panes:
* `wt`: Creates the new tab with the default profile
* `split-pane -p "Windows PowerShell"`: This will create a new pane, split from
the parent with the default profile. This pane will open with the "Windows

View File

@@ -54,8 +54,8 @@ object under a root property `"globals"`.
This is an array of key chords and shortcuts to invoke various commands.
Each command can have more than one key binding.
> 👉 **Note**: Key bindings is a subfield of the global settings and
> key bindings apply to all profiles in the same manner.
NOTE: Key bindings is a subfield of the global settings and
key bindings apply to all profiles in the same manner.
For example, here's a sample of the default keybindings:
@@ -69,26 +69,9 @@ For example, here's a sample of the default keybindings:
// etc.
]
}
```
You can also use a single key chord string as the value of `"keys"`.
It will be treated as a chord of length one.
This will allow you to simplify the above snippet as follows:
```json
{
"keybindings":
[
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": "copy", "keys": "ctrl+shift+c" },
{ "command": "newTab", "keys": "ctrl+shift+t" },
// etc.
]
}
```
### Unbinding keys
If you ever come across a key binding that you're unhappy with, it's possible to
@@ -153,7 +136,7 @@ the property `"hidden": true` to the profile's json. This can also be used to
remove the default `cmd` and PowerShell profiles, if the user does not wish to
see them.
## Color Schemes
## Color Schemes
Each scheme defines the color values to be used for various terminal escape sequences.
Each schema is identified by the name field. Examples include
@@ -176,7 +159,6 @@ The schema name can then be referenced in one or more profiles.
## Settings layering
The runtime settings are actually constructed from _three_ sources:
* The default settings, which are hardcoded into the application, and available
in `defaults.json`. This includes the default keybindings, color schemes, and
profiles for both Windows PowerShell and Command Prompt (`cmd.exe`).
@@ -287,7 +269,7 @@ properties for all your profiles, like so:
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe"
"commandline": "powershell.exe",
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
@@ -299,13 +281,12 @@ properties for all your profiles, like so:
"name" : "cmder",
"startingDirectory" : "%USERPROFILE%"
}
]
},
],
}
```
Note that the `profiles` property has changed in this example from a _list_ of
profiles, to an _object_ with two properties:
* a `list` that contains the list of all the profiles
* the new `defaults` object, which contains all the settings that should apply to
every profile.
@@ -328,7 +309,7 @@ could achieve that with the following:
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe"
"commandline": "powershell.exe",
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
@@ -341,18 +322,19 @@ could achieve that with the following:
"name" : "cmder",
"startingDirectory" : "%USERPROFILE%"
}
]
},
],
}
```
In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
`"fontFace"` from the `defaults`.
## Configuration Examples
## Configuration Examples:
### Add a custom background to the WSL Debian terminal profile
1. Download the [Debian JPG logo](https://www.debian.org/logos/openlogo-100.jpg)
1. Download the Debian JPG logo https://www.debian.org/logos/openlogo-100.jpg
2. Put the image in the
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\LocalState\`
directory (same directory as your `profiles.json` file).
@@ -360,20 +342,17 @@ In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
__NOTE__: You can put the image anywhere you like, the above suggestion happens to be convenient.
3. Open your WT json properties file.
4. Under the Debian Linux profile, add the following fields:
```json
"backgroundImage": "ms-appdata:///Local/openlogo-100.jpg",
"backgroundImageOpacity": 1,
"backgroundImageStretchMode" : "none",
"backgroundImageAlignment" : "topRight",
```
5. Make sure that `useAcrylic` is `false`.
6. Save the file.
7. Jump over to WT and verify your changes.
Notes:
1. You will need to experiment with different color settings
and schemes to make your terminal text visible on top of your image
2. If you store the image in the UWP directory (the same directory as your profiles.json file),
@@ -435,6 +414,7 @@ no text selection. Additionally, if you set `paste` to `"ctrl+v"`, commandline
applications won't be able to read a ctrl+v from the input. For these reasons,
we suggest `"ctrl+shift+c"` and `"ctrl+shift+v"`
### Setting the `startingDirectory` of WSL Profiles to `~`
By default, the `startingDirectory` of a profile is `%USERPROFILE%`

View File

@@ -75,16 +75,15 @@ To open the settings file from Windows Terminal:
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [profiles.json documentation](../cascadia/SettingsSchema.md) section.
## Tips and Tricks
## Tips and Tricks:
1. In PowerShell you can discover if the Windows Terminal is being used by checking for the existence of the environment variable `WT_SESSION`.
Under pwsh you can also use
`(Get-Process -Id $pid).Parent.ProcessName -eq 'WindowsTerminal'`
(ref [https://twitter.com/r_keith_hill/status/1142871145852440576](https://twitter.com/r_keith_hill/status/1142871145852440576))
(ref https://twitter.com/r_keith_hill/status/1142871145852440576)
2. Terminal zoom can be changed by holding <kbd>Ctrl</kbd> and scrolling with mouse.
3. If `useAcrylic` is enabled in profiles.json, background opacity can be changed by holding <kbd>Ctrl</kbd>+<kbd>Shift</kbd> and scrolling with mouse. Note that acrylic transparency is limited by the OS only to focused windows.
4. Open Windows Terminal in current directory by typing `wt -d .` in the address bar.
5. Please add more Tips and Tricks.
4. Please add more Tips and Tricks

View File

@@ -1,104 +0,0 @@
<WindowsPerformanceRecorder Version="1.0" Comments="Test" Company="Microsoft Corporation" Copyright="Microsoft Corporation">
<Profiles>
<SystemCollector Id="SystemCollector_ConsolePerfSystemCollectorInFileLarge">
<BufferSize Value="1024"/>
<Buffers Value="100"/>
</SystemCollector>
<!-- Leave this system provider alone. It's for environmental statistics by the plugin only -->
<SystemProvider Id="SystemProvider_Winperf_Environment_Light">
<Keywords Operation="Add">
<Keyword Value="CpuConfig"/>
</Keywords>
</SystemProvider>
<!-- This one is our system provider to customize as we see fit. -->
<SystemProvider Id="SystemProvider_ConsolePerf_Verbose" Base="SystemProvider_Base">
<Keywords Operation="Add">
<!-- Add additional kernel system collection here if we decide we need it for the regions of interest file -->
<Keyword Value="CSwitch"/>
<Keyword Value="MemoryInfo"/>
<Keyword Value="MemoryInfoWS"/>
<Keyword Value="VirtualAllocation"/>
</Keywords>
</SystemProvider>
<!-- Then add all the event providers we might possibly need. The first batch up here is boilerplate ones -->
<EventProvider Id="EventProvider-Microsoft-Windows-WinPerf" Name="7dba59f2-a1ff-40f0-b9f1-34b7531e9580"/>
<EventProvider Id="EventProvider-Microsoft.Windows.WinPerf" Name="BE6F04EA-3488-4543-8082-24843EAEC303"/>
<EventProvider Id="EventProvider-Census" Name="262CDE7A-5C84-46CF-9420-94963791EF69"/>
<!-- These providers are relevant to specific Regions of Interest -->
<EventProvider Id="EventProvider-Microsoft-Windows-Kernel-Memory" Name="Microsoft-Windows-Kernel-Memory"/>
<EventProvider Id="EventProvider_Microsoft-Windows-Kernel-Power" Name="331c3b3a-2005-44c2-ac5e-77220c37d6b4" NonPagedMemory="true">
<Keywords>
<Keyword Value="0x8000000000000000" />
</Keywords>
<CaptureStateOnSave>
<Keyword Value="0x80"/>
</CaptureStateOnSave>
</EventProvider>
<!-- Add console providers here -->
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Launcher" Name="770aa552-671a-5e97-579b-151709ec0dbd"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Host" Name="fe1ff234-1f09-50a8-d38d-c44fab43e818"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Server" Name="1A541C01-589A-496E-85A7-A9E02170166D"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser" Name="c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Terminal.App" Name="24a1622f-7da7-5c77-3303-d850bd1ab2ed"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Terminal.Control" Name="28c82e50-57af-5a86-c25b-e39cd990032b"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Terminal.Connection" Name="e912fe7b-eeb6-52a5-c628-abe388e5f792"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Terminal.Win32Host" Name="56c06166-2e2e-5f4d-7ff3-74f4b78c87d6"/>
<!-- Now define some profiles. We'll call them by ID when collecting. Also, the Base is where it is inheriting from and is a .wprpi file built... -->
<!-- ... into WPR automatically. Go look in the WPR install directory or in the documentation to find it. -->
<Profile Id="ConsolePerfProfile.Verbose.File" Base="GeneralProfile.Light.File" LoggingMode="File" Name="ConsolePerfProfile" DetailLevel="Verbose" Description="Console Performance default profile">
<Collectors Operation="Add">
<SystemCollectorId Value="SystemCollector_ConsolePerfSystemCollectorInFileLarge">
<!-- You don't have to change anything here when system keywords are added above. -->
<!-- Kernel collection is a bit more unified than user collection of events. -->
<SystemProviderId Value="SystemProvider_ConsolePerf_Verbose"/>
</SystemCollectorId>
<EventCollectorId Value="EventCollector_WPREventCollectorInFile">
<EventProviders Operation="Add">
<!-- Anything we need to collect for our perf test has to be referenced again here. -->
<EventProviderId Value="EventProvider-Microsoft-Windows-WinPerf"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.WinPerf"/>
<EventProviderId Value="EventProvider-Microsoft-Windows-Kernel-Memory"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Launcher"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Host"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Render.VtEngine"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Terminal.App"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Terminal.Control"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Terminal.Connection"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.Terminal.Win32Host"/>
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
<!-- Leave everything below here alone. They're needed for WinPerf to collect environmental census information -->
<Profile Id="WinPerfEnvironment.Light.File" Base="" LoggingMode="File" Name="WinPerfEnvironment" DetailLevel="Light" Description="WinPerf environment profile">
<Collectors Operation="Add">
<SystemCollectorId Value="SystemCollector_WPRSystemCollectorInFile">
<SystemProviderId Value="SystemProvider_Winperf_Environment_Light" />
</SystemCollectorId>
<EventCollectorId Value="EventCollector_WPREventCollectorInFile">
<EventProviders Operation="Add">
<EventProviderId Value="EventProvider-Census"/>
<EventProviderId Value="EventProvider_Microsoft-Windows-Kernel-Power"/>
<EventProviderId Value="EventProvider-Microsoft-Windows-WinPerf"/>
<EventProviderId Value="EventProvider-Microsoft.Windows.WinPerf"/>
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
<Profile Id="WinPerfEnvironment.Light.Memory" Base="WinPerfEnvironment.Light.File" LoggingMode="Memory" Name="WinPerfEnvironment" DetailLevel="Light" Description="WinPerf environment profile">
<Collectors Operation="Add">
<SystemCollectorId Value="SystemCollector_WPRSystemCollectorInFile">
<BufferSize Value="1024"/>
<Buffers Value="5" PercentageOfTotalMemory="true"/>
</SystemCollectorId>
<EventCollectorId Value="EventCollector_WPREventCollectorInFile">
<BufferSize Value="1024"/>
<Buffers Value="3" PercentageOfTotalMemory="true"/>
</EventCollectorId>
</Collectors>
</Profile>
</Profiles>
</WindowsPerformanceRecorder>

View File

@@ -269,33 +269,6 @@ std::wstring CharRow::GetText() const
return wstr;
}
// Method Description:
// - get delimiter class for a position in the char row
// - used for double click selection and uia word navigation
// Arguments:
// - column: column to get text data for
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass CharRow::DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const
{
THROW_HR_IF(E_INVALIDARG, column >= _data.size());
const auto glyph = *GlyphAt(column).begin();
if (glyph <= UNICODE_SPACE)
{
return DelimiterClass::ControlChar;
}
else if (wordDelimiters.find(glyph) != std::wstring_view::npos)
{
return DelimiterClass::DelimiterChar;
}
else
{
return DelimiterClass::RegularChar;
}
}
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
{
return _pParent->GetUnicodeStorage();

View File

@@ -27,13 +27,6 @@ Revision History:
class ROW;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
// the characters of one row of screen buffer
// we keep the following values so that we don't write
// more pixels to the screen than we have to:
@@ -71,8 +64,6 @@ public:
void ClearGlyph(const size_t column);
std::wstring GetText() const;
const DelimiterClass DelimiterClassAt(const size_t column, const std::wstring_view wordDelimiters) const;
// working with glyphs
const reference GlyphAt(const size_t column) const;
reference GlyphAt(const size_t column);

View File

@@ -951,19 +951,6 @@ Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcep
return _renderTarget;
}
// Method Description:
// - get delimiter class for buffer cell position
// - used for double click selection and uia word navigation
// Arguments:
// - pos: the buffer cell under observation
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const DelimiterClass TextBuffer::_GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const
{
return GetRowByOffset(pos.Y).GetCharRow().DelimiterClassAt(pos.X, wordDelimiters);
}
// Method Description:
// - Get the COORD for the beginning of the word you are on
// Arguments:
@@ -1014,11 +1001,16 @@ const COORD TextBuffer::_GetWordStartForAccessibility(const COORD target, const
COORD result = target;
const auto bufferSize = GetSize();
bool stayAtOrigin = false;
auto bufferIterator = GetTextDataAt(result);
// ignore left boundary. Continue until readable text found
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
while (_GetDelimiterClass(*bufferIterator, wordDelimiters) != DelimiterClass::RegularChar)
{
if (!bufferSize.DecrementInBounds(result))
if (bufferSize.DecrementInBounds(result))
{
--bufferIterator;
}
else
{
// first char in buffer is a DelimiterChar or ControlChar
// we can't move any further back
@@ -1028,9 +1020,13 @@ const COORD TextBuffer::_GetWordStartForAccessibility(const COORD target, const
}
// make sure we expand to the left boundary or the beginning of the word
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
while (_GetDelimiterClass(*bufferIterator, wordDelimiters) == DelimiterClass::RegularChar)
{
if (!bufferSize.DecrementInBounds(result))
if (bufferSize.DecrementInBounds(result))
{
--bufferIterator;
}
else
{
// first char in buffer is a RegularChar
// we can't move any further back
@@ -1039,7 +1035,7 @@ const COORD TextBuffer::_GetWordStartForAccessibility(const COORD target, const
}
// move off of delimiter and onto word start
if (!stayAtOrigin && _GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
if (!stayAtOrigin && _GetDelimiterClass(*bufferIterator, wordDelimiters) != DelimiterClass::RegularChar)
{
bufferSize.IncrementInBounds(result);
}
@@ -1058,16 +1054,17 @@ const COORD TextBuffer::_GetWordStartForSelection(const COORD target, const std:
{
COORD result = target;
const auto bufferSize = GetSize();
const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters);
auto bufferIterator = GetTextDataAt(result);
const auto initialDelimiter = _GetDelimiterClass(*bufferIterator, wordDelimiters);
// expand left until we hit the left boundary or a different delimiter class
while (result.X > bufferSize.Left() && (_GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter))
while (result.X > bufferSize.Left() && (_GetDelimiterClass(*bufferIterator, wordDelimiters) == initialDelimiter))
{
bufferSize.DecrementInBounds(result);
--bufferIterator;
}
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)
if (_GetDelimiterClass(*bufferIterator, wordDelimiters) != initialDelimiter)
{
// move off of delimiter
bufferSize.IncrementInBounds(result);
@@ -1119,20 +1116,31 @@ const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const st
{
const auto bufferSize = GetSize();
COORD result = target;
auto bufferIterator = GetTextDataAt(result);
// ignore right boundary. Continue through readable text found
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
while (_GetDelimiterClass(*bufferIterator, wordDelimiters) == DelimiterClass::RegularChar)
{
if (!bufferSize.IncrementInBounds(result, true))
if (bufferSize.IncrementInBounds(result, true))
{
++bufferIterator;
}
else
{
// last char in buffer is a RegularChar
// we can't move any further forward
break;
}
}
// make sure we expand to the beginning of the NEXT word
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
while (_GetDelimiterClass(*bufferIterator, wordDelimiters) != DelimiterClass::RegularChar)
{
if (!bufferSize.IncrementInBounds(result, true))
if (bufferSize.IncrementInBounds(result, true))
{
++bufferIterator;
}
else
{
// we are at the EndInclusive COORD
// this signifies that we must include the last char in the buffer
@@ -1154,23 +1162,25 @@ const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const st
const COORD TextBuffer::_GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const
{
const auto bufferSize = GetSize();
COORD result = target;
auto bufferIterator = GetTextDataAt(result);
// can't expand right
if (target.X == bufferSize.RightInclusive())
{
return target;
return result;
}
COORD result = target;
const auto initialDelimiter = _GetDelimiterClassAt(result, wordDelimiters);
const auto initialDelimiter = _GetDelimiterClass(*bufferIterator, wordDelimiters);
// expand right until we hit the right boundary or a different delimiter class
while (result.X < bufferSize.RightInclusive() && (_GetDelimiterClassAt(result, wordDelimiters) == initialDelimiter))
while (result.X < bufferSize.RightInclusive() && (_GetDelimiterClass(*bufferIterator, wordDelimiters) == initialDelimiter))
{
bufferSize.IncrementInBounds(result);
++bufferIterator;
}
if (_GetDelimiterClassAt(result, wordDelimiters) != initialDelimiter)
if (_GetDelimiterClass(*bufferIterator, wordDelimiters) != initialDelimiter)
{
// move off of delimiter
bufferSize.DecrementInBounds(result);
@@ -1193,8 +1203,11 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
auto copy = pos;
const auto bufferSize = GetSize();
auto text = GetTextDataAt(copy)->data();
auto delimiterClass = _GetDelimiterClass(text, wordDelimiters);
// started on a word, continue until the end of the word
while (_GetDelimiterClassAt(copy, wordDelimiters) == DelimiterClass::RegularChar)
while (delimiterClass == DelimiterClass::RegularChar)
{
if (!bufferSize.IncrementInBounds(copy))
{
@@ -1202,6 +1215,8 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
// thus there is no next word
return false;
}
text = GetTextDataAt(copy)->data();
delimiterClass = _GetDelimiterClass(text, wordDelimiters);
}
// we are already on/past the last RegularChar
@@ -1211,7 +1226,7 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
}
// on whitespace, continue until the beginning of the next word
while (_GetDelimiterClassAt(copy, wordDelimiters) != DelimiterClass::RegularChar)
while (delimiterClass != DelimiterClass::RegularChar)
{
if (!bufferSize.IncrementInBounds(copy))
{
@@ -1219,6 +1234,8 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
// there is no next word
return false;
}
text = GetTextDataAt(copy)->data();
delimiterClass = _GetDelimiterClass(text, wordDelimiters);
}
// successful move, copy result out
@@ -1239,8 +1256,11 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
auto copy = pos;
auto bufferSize = GetSize();
auto text = GetTextDataAt(copy)->data();
auto delimiterClass = _GetDelimiterClass(text, wordDelimiters);
// started on whitespace/delimiter, continue until the end of the previous word
while (_GetDelimiterClassAt(copy, wordDelimiters) != DelimiterClass::RegularChar)
while (delimiterClass != DelimiterClass::RegularChar)
{
if (!bufferSize.DecrementInBounds(copy))
{
@@ -1248,10 +1268,12 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
// there is no previous word
return false;
}
text = GetTextDataAt(copy)->data();
delimiterClass = _GetDelimiterClass(text, wordDelimiters);
}
// on a word, continue until the beginning of the word
while (_GetDelimiterClassAt(copy, wordDelimiters) == DelimiterClass::RegularChar)
while (delimiterClass == DelimiterClass::RegularChar)
{
if (!bufferSize.DecrementInBounds(copy))
{
@@ -1259,6 +1281,8 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
// there is no previous word
return false;
}
text = GetTextDataAt(copy)->data();
delimiterClass = _GetDelimiterClass(text, wordDelimiters);
}
// successful move, copy result out
@@ -1267,127 +1291,52 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
}
// Method Description:
// - Determines the line-by-line rectangles based on two COORDs
// - expands the rectangles to support wide glyphs
// - used for selection rects and UIA bounding rects
// - get delimiter class for buffer cell data
// - used for double click selection and uia word navigation
// Arguments:
// - start: a corner of the text region of interest (inclusive)
// - end: the other corner of the text region of interest (inclusive)
// - blockSelection: when enabled, only get the rectangular text region,
// as opposed to the text extending to the left/right
// buffer margins
// - cellChar: the char saved to the buffer cell under observation
// - wordDelimiters: the delimiters defined as a part of the DelimiterClass::DelimiterChar
// Return Value:
// - the delimiter class for the given char
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection) const
TextBuffer::DelimiterClass TextBuffer::_GetDelimiterClass(const std::wstring_view cellChar, const std::wstring_view wordDelimiters) const noexcept
{
std::vector<SMALL_RECT> textRects;
const auto bufferSize = GetSize();
// (0,0) is the top-left of the screen
// the physically "higher" coordinate is closer to the top-left
// the physically "lower" coordinate is closer to the bottom-right
const auto [higherCoord, lowerCoord] = bufferSize.CompareInBounds(start, end) <= 0 ?
std::make_tuple(start, end) :
std::make_tuple(end, start);
const auto textRectSize = base::ClampedNumeric<short>(1) + lowerCoord.Y - higherCoord.Y;
textRects.reserve(textRectSize);
for (auto row = higherCoord.Y; row <= lowerCoord.Y; row++)
if (cellChar.at(0) <= UNICODE_SPACE)
{
SMALL_RECT textRow;
textRow.Top = row;
textRow.Bottom = row;
if (blockSelection || higherCoord.Y == lowerCoord.Y)
{
// set the left and right margin to the left-/right-most respectively
textRow.Left = std::min(higherCoord.X, lowerCoord.X);
textRow.Right = std::max(higherCoord.X, lowerCoord.X);
}
else
{
textRow.Left = (row == higherCoord.Y) ? higherCoord.X : bufferSize.Left();
textRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : bufferSize.RightInclusive();
}
_ExpandTextRow(textRow);
textRects.emplace_back(textRow);
return DelimiterClass::ControlChar;
}
return textRects;
}
// Method Description:
// - Expand the selection row according to include wide glyphs fully
// - this is particularly useful for box selections (ALT + selection)
// Arguments:
// - selectionRow: the selection row to be expanded
// Return Value:
// - modifies selectionRow's Left and Right values to expand properly
void TextBuffer::_ExpandTextRow(SMALL_RECT& textRow) const
{
const auto bufferSize = GetSize();
// expand left side of rect
COORD targetPoint{ textRow.Left, textRow.Top };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsTrailing())
else if (wordDelimiters.find(cellChar) != std::wstring_view::npos)
{
if (targetPoint.X == bufferSize.Left())
{
bufferSize.IncrementInBounds(targetPoint);
}
else
{
bufferSize.DecrementInBounds(targetPoint);
}
textRow.Left = targetPoint.X;
return DelimiterClass::DelimiterChar;
}
// expand right side of rect
targetPoint = { textRow.Right, textRow.Bottom };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsLeading())
else
{
if (targetPoint.X == bufferSize.RightInclusive())
{
bufferSize.DecrementInBounds(targetPoint);
}
else
{
bufferSize.IncrementInBounds(targetPoint);
}
textRow.Right = targetPoint.X;
return DelimiterClass::RegularChar;
}
}
// Routine Description:
// - Retrieves the text data from the selected region and presents it in a clipboard-ready format (given little post-processing).
// Arguments:
// - includeCRLF - inject CRLF pairs to the end of each line
// - trimTrailingWhitespace - remove the trailing whitespace at the end of each line
// - textRects - the rectangular regions from which the data will be extracted from the buffer (i.e.: selection rects)
// - GetForegroundColor - function used to map TextAttribute to RGB COLORREF for foreground color. If null, only extract the text.
// - GetBackgroundColor - function used to map TextAttribute to RGB COLORREF for background color. If null, only extract the text.
// - lineSelection - true if entire line is being selected. False otherwise (box selection)
// - trimTrailingWhitespace - setting flag removes trailing whitespace at the end of each row in selection
// - selectionRects - the selection regions from which the data will be extracted from the buffer
// - GetForegroundColor - function used to map TextAttribute to RGB COLORREF for foreground color
// - GetBackgroundColor - function used to map TextAttribute to RGB COLORREF for foreground color
// Return Value:
// - The text, background color, and foreground color data of the selected region of the text buffer.
const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
const bool trimTrailingWhitespace,
const std::vector<SMALL_RECT>& selectionRects,
std::function<COLORREF(TextAttribute&)> GetForegroundColor,
std::function<COLORREF(TextAttribute&)> GetBackgroundColor) const
const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSelection,
const bool trimTrailingWhitespace,
const std::vector<SMALL_RECT>& selectionRects,
std::function<COLORREF(TextAttribute&)> GetForegroundColor,
std::function<COLORREF(TextAttribute&)> GetBackgroundColor) const
{
TextAndColor data;
const bool copyTextColor = GetForegroundColor && GetBackgroundColor;
// preallocate our vectors to reduce reallocs
size_t const rows = selectionRects.size();
data.text.reserve(rows);
if (copyTextColor)
{
data.FgAttr.reserve(rows);
data.BkAttr.reserve(rows);
}
data.FgAttr.reserve(rows);
data.BkAttr.reserve(rows);
// for each row in the selection
for (UINT i = 0; i < rows; i++)
@@ -1406,31 +1355,24 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
// preallocate to avoid reallocs
selectionText.reserve(gsl::narrow<size_t>(highlight.Width()) + 2); // + 2 for \r\n if we munged it
if (copyTextColor)
{
selectionFgAttr.reserve(gsl::narrow<size_t>(highlight.Width()) + 2);
selectionBkAttr.reserve(gsl::narrow<size_t>(highlight.Width()) + 2);
}
selectionFgAttr.reserve(gsl::narrow<size_t>(highlight.Width()) + 2);
selectionBkAttr.reserve(gsl::narrow<size_t>(highlight.Width()) + 2);
// copy char data into the string buffer, skipping trailing bytes
while (it)
{
const auto& cell = *it;
auto cellData = cell.TextAttr();
COLORREF const CellFgAttr = GetForegroundColor(cellData);
COLORREF const CellBkAttr = GetBackgroundColor(cellData);
if (!cell.DbcsAttr().IsTrailing())
{
selectionText.append(cell.Chars());
if (copyTextColor)
for (const wchar_t wch : cell.Chars())
{
auto cellData = cell.TextAttr();
COLORREF const CellFgAttr = GetForegroundColor(cellData);
COLORREF const CellBkAttr = GetBackgroundColor(cellData);
for (const wchar_t wch : cell.Chars())
{
selectionFgAttr.push_back(CellFgAttr);
selectionBkAttr.push_back(CellBkAttr);
}
selectionFgAttr.push_back(CellFgAttr);
selectionBkAttr.push_back(CellBkAttr);
}
}
#pragma warning(suppress : 26444)
@@ -1438,41 +1380,35 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
it++;
}
const bool forcedWrap = GetRowByOffset(iRow).GetCharRow().WasWrapForced();
// trim trailing spaces if SHIFT key not held
if (trimTrailingWhitespace)
{
// if the row was NOT wrapped...
if (!forcedWrap)
const ROW& Row = GetRowByOffset(iRow);
// FOR LINE SELECTION ONLY: if the row was wrapped, don't remove the spaces at the end.
if (!lineSelection || !Row.GetCharRow().WasWrapForced())
{
// remove the spaces at the end (aka trim the trailing whitespace)
while (!selectionText.empty() && selectionText.back() == UNICODE_SPACE)
{
selectionText.pop_back();
if (copyTextColor)
{
selectionFgAttr.pop_back();
selectionBkAttr.pop_back();
}
selectionFgAttr.pop_back();
selectionBkAttr.pop_back();
}
}
}
// apply CR/LF to the end of the final string, unless we're the last line.
// a.k.a if we're earlier than the bottom, then apply CR/LF.
if (includeCRLF && i < selectionRects.size() - 1)
{
// if the row was NOT wrapped...
if (!forcedWrap)
// apply CR/LF to the end of the final string, unless we're the last line.
// a.k.a if we're earlier than the bottom, then apply CR/LF.
if (i < selectionRects.size() - 1)
{
// then we can assume a CR/LF is proper
selectionText.push_back(UNICODE_CARRIAGERETURN);
selectionText.push_back(UNICODE_LINEFEED);
if (copyTextColor)
// FOR LINE SELECTION ONLY: if the row was wrapped, do not apply CR/LF.
// a.k.a. if the row was NOT wrapped, then we can assume a CR/LF is proper
// always apply \r\n for box selection
if (!lineSelection || !GetRowByOffset(iRow).GetCharRow().WasWrapForced())
{
// cant see CR/LF so just use black FG & BK
COLORREF const Blackness = RGB(0x00, 0x00, 0x00);
COLORREF const Blackness = RGB(0x00, 0x00, 0x00); // cant see CR/LF so just use black FG & BK
selectionText.push_back(UNICODE_CARRIAGERETURN);
selectionText.push_back(UNICODE_LINEFEED);
selectionFgAttr.push_back(Blackness);
selectionFgAttr.push_back(Blackness);
selectionBkAttr.push_back(Blackness);
@@ -1482,11 +1418,8 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF,
}
data.text.emplace_back(std::move(selectionText));
if (copyTextColor)
{
data.FgAttr.emplace_back(std::move(selectionFgAttr));
data.BkAttr.emplace_back(std::move(selectionBkAttr));
}
data.FgAttr.emplace_back(std::move(selectionFgAttr));
data.BkAttr.emplace_back(std::move(selectionBkAttr));
}
return data;

View File

@@ -135,8 +135,6 @@ public:
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const;
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
class TextAndColor
{
public:
@@ -145,11 +143,11 @@ public:
std::vector<std::vector<COLORREF>> BkAttr;
};
const TextAndColor GetText(const bool lineSelection,
const bool trimTrailingWhitespace,
const std::vector<SMALL_RECT>& textRects,
std::function<COLORREF(TextAttribute&)> GetForegroundColor = nullptr,
std::function<COLORREF(TextAttribute&)> GetBackgroundColor = nullptr) const;
const TextAndColor GetTextForClipboard(const bool lineSelection,
const bool trimTrailingWhitespace,
const std::vector<SMALL_RECT>& selectionRects,
std::function<COLORREF(TextAttribute&)> GetForegroundColor,
std::function<COLORREF(TextAttribute&)> GetBackgroundColor) const;
static std::string GenHTML(const TextAndColor& rows,
const int fontHeightPoints,
@@ -195,9 +193,13 @@ private:
ROW& _GetFirstRow();
ROW& _GetPrevRowNoWrap(const ROW& row);
void _ExpandTextRow(SMALL_RECT& selectionRow) const;
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
enum class DelimiterClass
{
ControlChar,
DelimiterChar,
RegularChar
};
DelimiterClass _GetDelimiterClass(const std::wstring_view cellChar, const std::wstring_view wordDelimiters) const noexcept;
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;

View File

@@ -43,8 +43,7 @@
<!-- Resources -->
<!-- This resw only defines things that are used in this package's AppxManifest,
so it's not in the common resource items. -->
<PRIResource Include="Resources\Resources.language-en.resw" />
<PRIResource Include="Resources\Resources.resw" />
<PRIResource Include="Resources\en-US\Resources.resw" />
</ItemGroup>
<PropertyGroup Condition="'$(WindowsTerminalReleaseBuild)'!='true'">
<!-- This is picked up by CascadiaResources.build.items. -->
@@ -55,7 +54,6 @@
<ItemGroup>
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
<ProjectReference Include="..\TerminalAzBridge\TerminalAzBridge.vcxproj" />
</ItemGroup>
<Target Name="OpenConsoleStompSourceProjectForWapProject" BeforeTargets="_ConvertItems">
<ItemGroup>
@@ -93,7 +91,9 @@
roll up our subproject resources. We have to suppress that rule but keep part of its logic, because that rule is
where the AppxPackagePayload items are created. -->
<PropertyGroup>
<WapProjBeforeGenerateAppxManifestDependsOn>
<!-- Only for MSBuild versions <= 16.4.0 -->
<!-- TODO: Change this to hard less than once the 16.4.0 previews fix the bug. -->
<WapProjBeforeGenerateAppxManifestDependsOn Condition="$(MSBuildVersion) &lt;= '16.4.0'">
$([MSBuild]::Unescape('$(WapProjBeforeGenerateAppxManifestDependsOn.Replace('_RemoveAllNonWapUWPItems', '_OpenConsoleRemoveAllNonWapUWPItems'))'))
</WapProjBeforeGenerateAppxManifestDependsOn>
</PropertyGroup>

View File

@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AppName" xml:space="preserve">
<value>Windows Terminal (Preview)</value>
</data>
<data name="AppNameDev" xml:space="preserve">
<value>Windows Terminal (Dev Build)</value>
</data>
<data name="AppShortName" xml:space="preserve">
<value>Terminal</value>
</data>
<data name="AppShortNameDev" xml:space="preserve">
<value>Terminal (Dev)</value>
</data>
</root>

View File

@@ -120,7 +120,19 @@
<data name="AppDescription" xml:space="preserve">
<value>The New Windows Terminal</value>
</data>
<data name="AppName" xml:space="preserve">
<value>Windows Terminal (Preview)</value>
</data>
<data name="AppDescriptionDev" xml:space="preserve">
<value>The Windows Terminal, but Unofficial</value>
</data>
</root>
<data name="AppNameDev" xml:space="preserve">
<value>Windows Terminal (Dev Build)</value>
</data>
<data name="AppShortName" xml:space="preserve">
<value>Terminal</value>
</data>
<data name="AppShortNameDev" xml:space="preserve">
<value>Terminal (Dev)</value>
</data>
</root>

View File

@@ -42,8 +42,6 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestArbitraryArgs);
TEST_METHOD(TestSplitPaneArgs);
TEST_METHOD(TestStringOverload);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
@@ -460,27 +458,4 @@ namespace TerminalAppLocalTests
}
}
void KeyBindingsTests::TestStringOverload()
{
const std::string bindings0String{ R"([
{ "command": "copy", "keys": "ctrl+c" }
])" };
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto appKeyBindings = winrt::make_self<implementation::AppKeyBindings>();
VERIFY_IS_NOT_NULL(appKeyBindings);
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
appKeyBindings->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
}
}
}

View File

@@ -70,8 +70,6 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestTerminalArgsForBinding);
TEST_METHOD(ValidateKeybindingsWarnings);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
@@ -2091,48 +2089,4 @@ namespace TerminalAppLocalTests
VERIFY_ARE_EQUAL(2, termSettings.HistorySize());
}
}
void SettingsTests::ValidateKeybindingsWarnings()
{
const std::string badSettings{ R"(
{
"globals": {
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
},
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
},
{
"name" : "profile1",
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
}
],
"keybindings": [
{ "command": { "action": "splitPane", "split":"auto" }, "keys": [ "ctrl+alt+t", "ctrl+a" ] },
{ "command": { "action": "moveFocus" }, "keys": [ "ctrl+a" ] },
{ "command": { "action": "resizePane" }, "keys": [ "ctrl+b" ] }
]
})" };
const auto settingsObject = VerifyParseSucceeded(badSettings);
auto settings = CascadiaSettings::FromJson(settingsObject);
VERIFY_ARE_EQUAL(0u, settings->_globals._keybindings->_keyShortcuts.size());
VERIFY_ARE_EQUAL(3u, settings->_globals._keybindingsWarnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord, settings->_globals._keybindingsWarnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals._keybindingsWarnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_globals._keybindingsWarnings.at(2));
settings->_ValidateKeybindings();
VERIFY_ARE_EQUAL(4u, settings->_warnings.size());
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning, settings->_warnings.at(0));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord, settings->_warnings.at(1));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(2));
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter, settings->_warnings.at(3));
}
}

View File

@@ -3,8 +3,6 @@
#include "pch.h"
#include "HwndTerminal.hpp"
#include <windowsx.h>
#include "../../types/TermControlUiaProvider.hpp"
#include <DefaultSettings.h>
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
@@ -16,54 +14,13 @@ using namespace ::Microsoft::Terminal::Core;
static LPCWSTR term_window_class = L"HwndTerminalClass";
LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
static LRESULT CALLBACK HwndTerminalWndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) noexcept
{
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
HwndTerminal* terminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (terminal)
{
switch (uMsg)
{
case WM_GETOBJECT:
if (lParam == UiaRootObjectId)
{
return UiaReturnRawElementProvider(hwnd, wParam, lParam, terminal->_GetUiaProvider());
}
break;
case WM_LBUTTONDOWN:
LOG_IF_FAILED(terminal->_StartSelection(lParam));
return 0;
case WM_MOUSEMOVE:
if (WI_IsFlagSet(wParam, MK_LBUTTON))
{
LOG_IF_FAILED(terminal->_MoveSelection(lParam));
return 0;
}
break;
case WM_RBUTTONDOWN:
if (terminal->_terminal->IsSelectionActive())
{
try
{
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
terminal->_terminal->ClearSelection();
}
CATCH_LOG();
}
else
{
terminal->_PasteTextFromClipboard();
}
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static bool RegisterTermClass(HINSTANCE hInstance) noexcept
@@ -75,7 +32,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
}
wc.style = 0;
wc.lpfnWndProc = HwndTerminal::HwndTerminalWndProc;
wc.lpfnWndProc = HwndTerminalWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
@@ -90,11 +47,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
HwndTerminal::HwndTerminal(HWND parentHwnd) :
_desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false },
_uiaProvider{ nullptr },
_uiaProviderInitialized{ false },
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
_pfnWriteCallback{ nullptr }
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false }
{
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
@@ -116,9 +69,6 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
nullptr,
hInstance,
nullptr));
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
SetWindowLongPtr(_hwnd.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}
}
@@ -157,7 +107,7 @@ HRESULT HwndTerminal::Initialize()
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
_terminal->SetDefaultBackground(RGB(5, 27, 80));
_terminal->SetDefaultForeground(RGB(255, 255, 255));
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept { _WriteTextToConnection(input); });
localPointerToThread->EnablePainting();
return S_OK;
@@ -168,39 +118,31 @@ void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> cal
_terminal->SetScrollPositionChangedCallback(callback);
}
void HwndTerminal::_WriteTextToConnection(const std::wstring& input) noexcept
{
if (!_pfnWriteCallback)
{
return;
}
try
{
auto callingText{ wil::make_cotaskmem_string(input.data(), input.size()) };
_pfnWriteCallback(callingText.release());
}
CATCH_LOG();
}
void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
{
_pfnWriteCallback = callback;
}
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept {
const wchar_t* text = input.c_str();
const size_t textChars = wcslen(text) + 1;
const size_t textBytes = textChars * sizeof(wchar_t);
wchar_t* callingText = nullptr;
::Microsoft::Console::Types::IUiaData* HwndTerminal::GetUiaData() const noexcept
{
return _terminal.get();
}
callingText = static_cast<wchar_t*>(::CoTaskMemAlloc(textBytes));
HWND HwndTerminal::GetHwnd() const noexcept
{
return _hwnd.get();
if (callingText == nullptr)
{
callback(nullptr);
}
else
{
wcscpy_s(callingText, textChars, text);
callback(callingText);
}
});
}
void HwndTerminal::_UpdateFont(int newDpi)
{
_currentDpi = newDpi;
auto lock = _terminal->LockForWriting();
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
@@ -208,33 +150,6 @@ void HwndTerminal::_UpdateFont(int newDpi)
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
}
IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
{
if (nullptr == _uiaProvider && !_uiaProviderInitialized)
{
std::unique_lock<std::shared_mutex> lock;
try
{
#pragma warning(suppress : 26441) // The lock is named, this appears to be a false positive
lock = _terminal->LockForWriting();
if (_uiaProviderInitialized)
{
return _uiaProvider.Get();
}
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, this->GetUiaData(), this));
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
_uiaProvider = nullptr;
}
_uiaProviderInitialized = true;
}
return _uiaProvider.Get();
}
HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions)
{
RETURN_HR_IF_NULL(E_INVALIDARG, dimensions);
@@ -271,29 +186,10 @@ void HwndTerminal::SendOutput(std::wstring_view data)
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
{
// In order for UIA to hook up properly there needs to be a "static" window hosting the
// inner win32 control. If the static window is not present then WM_GETOBJECT messages
// will not reach the child control, and the uia element will not be present in the tree.
auto _hostWindow = CreateWindowEx(
0,
L"static",
nullptr,
WS_CHILD |
WS_CLIPCHILDREN |
WS_CLIPSIBLINGS |
WS_VISIBLE,
0,
0,
0,
0,
parentHwnd,
nullptr,
nullptr,
0);
auto _terminal = std::make_unique<HwndTerminal>(_hostWindow);
auto _terminal = std::make_unique<HwndTerminal>(parentHwnd);
RETURN_IF_FAILED(_terminal->Initialize());
*hwnd = _hostWindow;
*hwnd = _terminal->_hwnd.get();
*terminal = _terminal.release();
return S_OK;
@@ -321,15 +217,6 @@ HRESULT _stdcall TerminalTriggerResize(void* terminal, double width, double heig
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(
publicTerminal->GetHwnd(),
nullptr,
0,
0,
static_cast<int>(width),
static_cast<int>(height),
0));
const SIZE windowSize{ static_cast<short>(width), static_cast<short>(height) };
return publicTerminal->Refresh(windowSize, dimensions);
}
@@ -346,54 +233,45 @@ void _stdcall TerminalUserScroll(void* terminal, int viewTop)
publicTerminal->_terminal->UserScrollViewport(viewTop);
}
HRESULT HwndTerminal::_StartSelection(LPARAM lParam) noexcept
try
HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed)
{
const bool altPressed = GetKeyState(VK_MENU) < 0;
COORD cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};
COORD terminalPosition = { cursorPosition };
const auto fontSize = this->_actualFont.GetSize();
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto fontSize = publicTerminal->_actualFont.GetSize();
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);
cursorPosition.X /= fontSize.X;
cursorPosition.Y /= fontSize.Y;
terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;
this->_terminal->SetSelectionAnchor(cursorPosition);
this->_terminal->SetBlockSelection(altPressed);
publicTerminal->_terminal->SetSelectionAnchor(terminalPosition);
publicTerminal->_terminal->SetBoxSelection(altPressed);
this->_renderer->TriggerSelection();
publicTerminal->_renderer->TriggerSelection();
return S_OK;
}
CATCH_RETURN();
HRESULT HwndTerminal::_MoveSelection(LPARAM lParam) noexcept
try
HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition)
{
COORD cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};
COORD terminalPosition = { cursorPosition };
const auto fontSize = this->_actualFont.GetSize();
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto fontSize = publicTerminal->_actualFont.GetSize();
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);
cursorPosition.X /= fontSize.X;
cursorPosition.Y /= fontSize.Y;
terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;
this->_terminal->SetSelectionEnd(cursorPosition);
this->_renderer->TriggerSelection();
publicTerminal->_terminal->SetEndSelectionPosition(terminalPosition);
publicTerminal->_renderer->TriggerSelection();
return S_OK;
}
CATCH_RETURN();
void _stdcall TerminalClearSelection(void* terminal)
{
@@ -535,173 +413,3 @@ void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
publicTerminal->_terminal->SetCursorVisible(visible);
}
// Routine Description:
// - Copies the text given onto the global system clipboard.
// Arguments:
// - rows - Rows of text data to copy
// - fAlsoCopyFormatting - true if the color and formatting should also be copied, false otherwise
HRESULT HwndTerminal::_CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting)
{
std::wstring finalString;
// Concatenate strings into one giant string to put onto the clipboard.
for (const auto& str : rows.text)
{
finalString += str;
}
// allocate the final clipboard data
const size_t cchNeeded = finalString.size() + 1;
const size_t cbNeeded = sizeof(wchar_t) * cchNeeded;
wil::unique_hglobal globalHandle(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbNeeded));
RETURN_LAST_ERROR_IF_NULL(globalHandle.get());
PWSTR pwszClipboard = static_cast<PWSTR>(GlobalLock(globalHandle.get()));
RETURN_LAST_ERROR_IF_NULL(pwszClipboard);
// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr = StringCchCopyW(pwszClipboard, cchNeeded, finalString.data());
GlobalUnlock(globalHandle.get());
RETURN_IF_FAILED(hr);
// Set global data to clipboard
RETURN_LAST_ERROR_IF(!OpenClipboard(_hwnd.get()));
{ // Clipboard Scope
auto clipboardCloser = wil::scope_exit([]() noexcept {
LOG_LAST_ERROR_IF(!CloseClipboard());
});
RETURN_LAST_ERROR_IF(!EmptyClipboard());
RETURN_LAST_ERROR_IF_NULL(SetClipboardData(CF_UNICODETEXT, globalHandle.get()));
if (fAlsoCopyFormatting)
{
const auto& fontData = _actualFont;
int const iFontHeightPoints = fontData.GetUnscaledSize().Y * 72 / this->_currentDpi;
const COLORREF bgColor = _terminal->GetBackgroundColor(_terminal->GetDefaultBrushColors());
std::string HTMLToPlaceOnClip = TextBuffer::GenHTML(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor, "Hwnd Console Host");
_CopyToSystemClipboard(HTMLToPlaceOnClip, L"HTML Format");
std::string RTFToPlaceOnClip = TextBuffer::GenRTF(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor);
_CopyToSystemClipboard(RTFToPlaceOnClip, L"Rich Text Format");
}
}
// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandle.release();
return S_OK;
}
// Routine Description:
// - Copies the given string onto the global system clipboard in the specified format
// Arguments:
// - stringToCopy - The string to copy
// - lpszFormat - the name of the format
HRESULT HwndTerminal::_CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat)
{
const size_t cbData = stringToCopy.size() + 1; // +1 for '\0'
if (cbData)
{
wil::unique_hglobal globalHandleData(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbData));
RETURN_LAST_ERROR_IF_NULL(globalHandleData.get());
PSTR pszClipboardHTML = static_cast<PSTR>(GlobalLock(globalHandleData.get()));
RETURN_LAST_ERROR_IF_NULL(pszClipboardHTML);
// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr2 = StringCchCopyA(pszClipboardHTML, cbData, stringToCopy.data());
GlobalUnlock(globalHandleData.get());
RETURN_IF_FAILED(hr2);
UINT const CF_FORMAT = RegisterClipboardFormatW(lpszFormat);
RETURN_LAST_ERROR_IF(0 == CF_FORMAT);
RETURN_LAST_ERROR_IF_NULL(SetClipboardData(CF_FORMAT, globalHandleData.get()));
// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandleData.release();
}
return S_OK;
}
void HwndTerminal::_PasteTextFromClipboard() noexcept
{
// Get paste data from clipboard
if (!OpenClipboard(_hwnd.get()))
{
return;
}
HANDLE ClipboardDataHandle = GetClipboardData(CF_UNICODETEXT);
if (ClipboardDataHandle == nullptr)
{
CloseClipboard();
return;
}
PCWCH pwstr = static_cast<PCWCH>(GlobalLock(ClipboardDataHandle));
_StringPaste(pwstr);
GlobalUnlock(ClipboardDataHandle);
CloseClipboard();
}
void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
{
if (pData == nullptr)
{
return;
}
try
{
std::wstring text(pData);
_WriteTextToConnection(text);
}
CATCH_LOG();
}
COORD HwndTerminal::GetFontSize() const
{
return _actualFont.GetSize();
}
RECT HwndTerminal::GetBounds() const noexcept
{
RECT windowRect;
GetWindowRect(_hwnd.get(), &windowRect);
return windowRect;
}
RECT HwndTerminal::GetPadding() const noexcept
{
return { 0 };
}
double HwndTerminal::GetScaleFactor() const noexcept
{
return static_cast<double>(_currentDpi) / static_cast<double>(USER_DEFAULT_SCREEN_DPI);
}
void HwndTerminal::ChangeViewport(const SMALL_RECT NewWindow)
{
_terminal->UserScrollViewport(NewWindow.Top);
}
HRESULT HwndTerminal::GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept
{
return UiaHostProviderFromHwnd(_hwnd.get(), provider);
}

View File

@@ -6,9 +6,6 @@
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include <UIAutomationCore.h>
#include "../../types/IControlAccessibilityInfo.h"
#include "../../types/TermControlUiaProvider.hpp"
using namespace Microsoft::Console::VirtualTerminal;
@@ -28,6 +25,8 @@ __declspec(dllexport) HRESULT _stdcall TerminalTriggerResize(void* terminal, dou
__declspec(dllexport) HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
__declspec(dllexport) void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
__declspec(dllexport) void _stdcall TerminalUserScroll(void* terminal, int viewTop);
__declspec(dllexport) HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
__declspec(dllexport) HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
__declspec(dllexport) void _stdcall TerminalClearSelection(void* terminal);
__declspec(dllexport) const wchar_t* _stdcall TerminalGetSelection(void* terminal);
__declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
@@ -40,35 +39,20 @@ __declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
};
struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
struct HwndTerminal
{
public:
HwndTerminal(HWND hwnd);
HwndTerminal(const HwndTerminal&) = default;
HwndTerminal(HwndTerminal&&) = default;
HwndTerminal& operator=(const HwndTerminal&) = default;
HwndTerminal& operator=(HwndTerminal&&) = default;
~HwndTerminal() = default;
HRESULT Initialize();
void SendOutput(std::wstring_view data);
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
void RegisterWriteCallback(const void _stdcall callback(wchar_t*));
::Microsoft::Console::Types::IUiaData* GetUiaData() const noexcept;
HWND GetHwnd() const noexcept;
static LRESULT CALLBACK HwndTerminalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
private:
wil::unique_hwnd _hwnd;
FontInfoDesired _desiredFont;
FontInfo _actualFont;
int _currentDpi;
bool _uiaProviderInitialized;
std::function<void(wchar_t*)> _pfnWriteCallback;
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
@@ -79,6 +63,8 @@ private:
friend HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
friend void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
friend void _stdcall TerminalUserScroll(void* terminal, int viewTop);
friend HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
friend HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
friend void _stdcall TerminalClearSelection(void* terminal);
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
@@ -87,23 +73,5 @@ private:
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
void _UpdateFont(int newDpi);
void _WriteTextToConnection(const std::wstring& text) noexcept;
HRESULT _CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting);
HRESULT _CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat);
void _PasteTextFromClipboard() noexcept;
void _StringPaste(const wchar_t* const pData) noexcept;
HRESULT _StartSelection(LPARAM lParam) noexcept;
HRESULT _MoveSelection(LPARAM lParam) noexcept;
IRawElementProviderSimple* _GetUiaProvider() noexcept;
// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const override;
RECT GetBounds() const noexcept override;
double GetScaleFactor() const noexcept override;
void ChangeViewport(const SMALL_RECT NewWindow) override;
HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept override;
RECT GetPadding() const noexcept override;
};

View File

@@ -54,7 +54,7 @@
instead of APISet forwarders for easier Windows 7 compatibility. -->
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@@ -1,8 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // If this is not defined, windows.h includes commdlg.h which defines FindText globally and conflicts with UIAutomation ITextRangeProvider.
#endif
#include <LibraryIncludes.h>

View File

@@ -17,7 +17,6 @@
#include "../../cascadia/inc/cppwinrt_utils.h"
#include "Utils.h"
#include "TerminalWarnings.h"
// Notes on defining ActionArgs and ActionEventArgs:
// * All properties specific to an action should be defined as an ActionArgs
@@ -27,8 +26,6 @@
namespace winrt::TerminalApp::implementation
{
using FromJsonResult = std::tuple<winrt::TerminalApp::IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
struct ActionEventArgs : public ActionEventArgsT<ActionEventArgs>
{
ActionEventArgs() = default;
@@ -107,7 +104,7 @@ namespace winrt::TerminalApp::implementation
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<CopyTextArgs>();
@@ -115,7 +112,7 @@ namespace winrt::TerminalApp::implementation
{
args->_TrimWhitespace = trimWhitespace.asBool();
}
return { *args, {} };
return *args;
}
};
@@ -134,12 +131,12 @@ namespace winrt::TerminalApp::implementation
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<NewTabArgs>();
args->_TerminalArgs = NewTerminalArgs::FromJson(json);
return { *args, {} };
return *args;
}
};
@@ -160,7 +157,7 @@ namespace winrt::TerminalApp::implementation
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<SwitchToTabArgs>();
@@ -168,7 +165,7 @@ namespace winrt::TerminalApp::implementation
{
args->_TabIndex = tabIndex.asUInt();
}
return { *args, {} };
return *args;
}
};
@@ -224,7 +221,7 @@ namespace winrt::TerminalApp::implementation
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<ResizePaneArgs>();
@@ -232,14 +229,7 @@ namespace winrt::TerminalApp::implementation
{
args->_Direction = ParseDirection(directionString.asString());
}
if (args->_Direction == TerminalApp::Direction::None)
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
else
{
return { *args, {} };
}
return *args;
}
};
@@ -260,7 +250,7 @@ namespace winrt::TerminalApp::implementation
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<MoveFocusArgs>();
@@ -268,14 +258,7 @@ namespace winrt::TerminalApp::implementation
{
args->_Direction = ParseDirection(directionString.asString());
}
if (args->_Direction == TerminalApp::Direction::None)
{
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
}
else
{
return { *args, {} };
}
return *args;
}
};
@@ -296,7 +279,7 @@ namespace winrt::TerminalApp::implementation
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<AdjustFontSizeArgs>();
@@ -304,7 +287,7 @@ namespace winrt::TerminalApp::implementation
{
args->_Delta = jsonDelta.asInt();
}
return { *args, {} };
return *args;
}
};
@@ -331,26 +314,13 @@ namespace winrt::TerminalApp::implementation
return TerminalApp::SplitState::None;
};
// Possible SplitType values
static constexpr std::string_view DuplicateKey{ "duplicate" };
static TerminalApp::SplitType ParseSplitModeState(const std::string& stateString)
{
if (stateString == DuplicateKey)
{
return TerminalApp::SplitType::Duplicate;
}
return TerminalApp::SplitType::Manual;
}
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
{
SplitPaneArgs() = default;
GETSET_PROPERTY(winrt::TerminalApp::SplitState, SplitStyle, winrt::TerminalApp::SplitState::None);
GETSET_PROPERTY(winrt::TerminalApp::NewTerminalArgs, TerminalArgs, nullptr);
GETSET_PROPERTY(winrt::TerminalApp::SplitType, SplitMode, winrt::TerminalApp::SplitType::Manual);
static constexpr std::string_view SplitKey{ "split" };
static constexpr std::string_view SplitModeKey{ "splitMode" };
public:
bool Equals(const IActionArgs& other)
@@ -359,12 +329,11 @@ namespace winrt::TerminalApp::implementation
if (otherAsUs)
{
return otherAsUs->_SplitStyle == _SplitStyle &&
otherAsUs->_TerminalArgs == _TerminalArgs &&
otherAsUs->_SplitMode == _SplitMode;
otherAsUs->_TerminalArgs == _TerminalArgs;
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<SplitPaneArgs>();
@@ -373,11 +342,7 @@ namespace winrt::TerminalApp::implementation
{
args->_SplitStyle = ParseSplitState(jsonStyle.asString());
}
if (auto jsonStyle{ json[JsonKey(SplitModeKey)] })
{
args->_SplitMode = ParseSplitModeState(jsonStyle.asString());
}
return { *args, {} };
return *args;
}
};
}

View File

@@ -31,12 +31,6 @@ namespace TerminalApp
Horizontal = 2
};
enum SplitType
{
Manual = 0,
Duplicate = 1
};
[default_interface] runtimeclass NewTerminalArgs {
NewTerminalArgs();
String Commandline;
@@ -88,6 +82,5 @@ namespace TerminalApp
{
SplitState SplitStyle { get; };
NewTerminalArgs TerminalArgs { get; };
SplitType SplitMode { get; };
};
}

View File

@@ -98,7 +98,7 @@ namespace winrt::TerminalApp::implementation
}
else if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SplitPaneArgs>())
{
_SplitPane(realArgs.SplitStyle(), realArgs.SplitMode(), realArgs.TerminalArgs());
_SplitPane(realArgs.SplitStyle(), realArgs.TerminalArgs());
args.Handled(true);
}
}

View File

@@ -53,7 +53,7 @@ namespace winrt::TerminalApp::implementation
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
// Defined in AppKeyBindingsSerialization.cpp
std::vector<::TerminalApp::SettingsLoadWarnings> LayerJson(const Json::Value& json);
void LayerJson(const Json::Value& json);
Json::Value ToJson();
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);

View File

@@ -146,9 +146,6 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
{ FindKey, ShortcutAction::Find },
};
using ParseResult = std::tuple<IActionArgs, std::vector<TerminalApp::SettingsLoadWarnings>>;
using ParseActionFunction = std::function<ParseResult(const Json::Value&)>;
// Function Description:
// - Creates a function that can be used to generate a SplitPaneArgs for the
// legacy Split[SplitState] actions. These actions don't accept args from
@@ -160,12 +157,12 @@ using ParseActionFunction = std::function<ParseResult(const Json::Value&)>;
// Return Value:
// - A function that can be used to "parse" json into one of the legacy
// Split[SplitState] args.
ParseActionFunction LegacyParseSplitPaneArgs(SplitState style)
std::function<IActionArgs(const Json::Value&)> LegacyParseSplitPaneArgs(SplitState style)
{
auto pfn = [style](const Json::Value & /*value*/) -> ParseResult {
auto pfn = [style](const Json::Value & /*value*/) -> IActionArgs {
auto args = winrt::make_self<winrt::TerminalApp::implementation::SplitPaneArgs>();
args->SplitStyle(style);
return { *args, {} };
return *args;
};
return pfn;
}
@@ -181,12 +178,12 @@ ParseActionFunction LegacyParseSplitPaneArgs(SplitState style)
// Return Value:
// - A function that can be used to "parse" json into one of the legacy
// MoveFocus[Direction] args.
ParseActionFunction LegacyParseMoveFocusArgs(Direction direction)
std::function<IActionArgs(const Json::Value&)> LegacyParseMoveFocusArgs(Direction direction)
{
auto pfn = [direction](const Json::Value & /*value*/) -> ParseResult {
auto pfn = [direction](const Json::Value & /*value*/) -> IActionArgs {
auto args = winrt::make_self<winrt::TerminalApp::implementation::MoveFocusArgs>();
args->Direction(direction);
return { *args, {} };
return *args;
};
return pfn;
}
@@ -202,12 +199,12 @@ ParseActionFunction LegacyParseMoveFocusArgs(Direction direction)
// Return Value:
// - A function that can be used to "parse" json into one of the legacy
// ResizePane[Direction] args.
ParseActionFunction LegacyParseResizePaneArgs(Direction direction)
std::function<IActionArgs(const Json::Value&)> LegacyParseResizePaneArgs(Direction direction)
{
auto pfn = [direction](const Json::Value & /*value*/) -> ParseResult {
auto pfn = [direction](const Json::Value & /*value*/) -> IActionArgs {
auto args = winrt::make_self<winrt::TerminalApp::implementation::ResizePaneArgs>();
args->Direction(direction);
return { *args, {} };
return *args;
};
return pfn;
}
@@ -223,14 +220,14 @@ ParseActionFunction LegacyParseResizePaneArgs(Direction direction)
// Return Value:
// - A function that can be used to "parse" json into one of the legacy
// NewTabWithProfile[Index] args.
ParseActionFunction LegacyParseNewTabWithProfileArgs(int index)
std::function<IActionArgs(const Json::Value&)> LegacyParseNewTabWithProfileArgs(int index)
{
auto pfn = [index](const Json::Value & /*value*/) -> ParseResult {
auto pfn = [index](const Json::Value & /*value*/) -> IActionArgs {
auto args = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
newTerminalArgs->ProfileIndex(index);
args->TerminalArgs(*newTerminalArgs);
return { *args, {} };
return *args;
};
return pfn;
}
@@ -246,12 +243,12 @@ ParseActionFunction LegacyParseNewTabWithProfileArgs(int index)
// Return Value:
// - A function that can be used to "parse" json into one of the legacy
// SwitchToTab[Index] args.
ParseActionFunction LegacyParseSwitchToTabArgs(int index)
std::function<IActionArgs(const Json::Value&)> LegacyParseSwitchToTabArgs(int index)
{
auto pfn = [index](const Json::Value & /*value*/) -> ParseResult {
auto pfn = [index](const Json::Value & /*value*/) -> IActionArgs {
auto args = winrt::make_self<winrt::TerminalApp::implementation::SwitchToTabArgs>();
args->TabIndex(index);
return { *args, {} };
return *args;
};
return pfn;
}
@@ -264,11 +261,11 @@ ParseActionFunction LegacyParseSwitchToTabArgs(int index)
// - direction: the direction to create the parse function for.
// Return Value:
// - A CopyTextArgs with TrimWhitespace set to true, to emulate "CopyTextWithoutNewlines".
ParseResult LegacyParseCopyTextWithoutNewlinesArgs(const Json::Value& /*json*/)
IActionArgs LegacyParseCopyTextWithoutNewlinesArgs(const Json::Value& /*json*/)
{
auto args = winrt::make_self<winrt::TerminalApp::implementation::CopyTextArgs>();
args->TrimWhitespace(false);
return { *args, {} };
return *args;
};
// Function Description:
@@ -279,12 +276,12 @@ ParseResult LegacyParseCopyTextWithoutNewlinesArgs(const Json::Value& /*json*/)
// - delta: the font size delta to create the parse function for.
// Return Value:
// - A function that can be used to "parse" json into an AdjustFontSizeArgs.
ParseActionFunction LegacyParseAdjustFontSizeArgs(int delta)
std::function<IActionArgs(const Json::Value&)> LegacyParseAdjustFontSizeArgs(int delta)
{
auto pfn = [delta](const Json::Value & /*value*/) -> ParseResult {
auto pfn = [delta](const Json::Value & /*value*/) -> IActionArgs {
auto args = winrt::make_self<winrt::TerminalApp::implementation::AdjustFontSizeArgs>();
args->Delta(delta);
return { *args, {} };
return *args;
};
return pfn;
}
@@ -294,7 +291,7 @@ ParseActionFunction LegacyParseAdjustFontSizeArgs(int delta)
// from json. Each type of IActionArgs that can accept arbitrary args should be
// placed into this map, with the corresponding deserializer function as the
// value.
static const std::map<ShortcutAction, ParseActionFunction, std::less<>> argParsers{
static const std::map<ShortcutAction, std::function<IActionArgs(const Json::Value&)>, std::less<>> argParsers{
{ ShortcutAction::CopyText, winrt::TerminalApp::implementation::CopyTextArgs::FromJson },
{ ShortcutAction::CopyTextWithoutNewlines, LegacyParseCopyTextWithoutNewlinesArgs },
@@ -430,14 +427,8 @@ static ShortcutAction GetActionFromString(const std::string_view actionString)
// `"unbound"`, then we'll clear the keybinding from the existing keybindings.
// Arguments:
// - json: and array of JsonObject's to deserialize into our _keyShortcuts mapping.
std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::Value& json)
void winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::Value& json)
{
// It's possible that the user provided keybindings have some warnings in
// them - problems that we should alert the user to, but we can recover
// from. Most of these warnings cannot be detected later in the Validate
// settings phase, so we'll collect them now.
std::vector<::TerminalApp::SettingsLoadWarnings> warnings;
for (const auto& value : json)
{
if (!value.isObject())
@@ -450,22 +441,11 @@ std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementat
if (keys)
{
const auto validString = keys.isString();
const auto validArray = keys.isArray() && keys.size() == 1;
// GH#4239 - If the user provided more than one key
// chord to a "keys" array, warn the user here.
// TODO: GH#1334 - remove this check.
if (keys.isArray() && keys.size() > 1)
{
warnings.push_back(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord);
}
if (!validString && !validArray)
if (!keys.isArray() || keys.size() != 1)
{
continue;
}
const auto keyChordString = keys.isString() ? winrt::to_hstring(keys.asString()) : winrt::to_hstring(keys[0].asString());
const auto keyChordString = winrt::to_hstring(keys[0].asString());
// Invalid is our placeholder that the action was not parsed.
ShortcutAction action = ShortcutAction::Invalid;
@@ -504,21 +484,13 @@ std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementat
// does, we'll try to deserialize any "args" that were provided with
// the binding.
IActionArgs args{ nullptr };
std::vector<::TerminalApp::SettingsLoadWarnings> parseWarnings;
const auto deserializersIter = argParsers.find(action);
if (deserializersIter != argParsers.end())
{
auto pfn = deserializersIter->second;
if (pfn)
{
std::tie(args, parseWarnings) = pfn(argsVal);
}
warnings.insert(warnings.end(), parseWarnings.begin(), parseWarnings.end());
// if an arg parser was registered, but failed, bail
if (args == nullptr)
{
continue;
args = pfn(argsVal);
}
}
@@ -549,6 +521,4 @@ std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementat
}
}
}
return warnings;
}

View File

@@ -27,17 +27,14 @@ namespace winrt
// !!! IMPORTANT !!!
// Make sure that these keys are in the same order as the
// SettingsLoadWarnings/Errors enum is!
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWarnings::WARNINGS_SIZE)> settingsLoadWarningsLabels {
static const std::array<std::wstring_view, 5> settingsLoadWarningsLabels {
USES_RESOURCE(L"MissingDefaultProfileText"),
USES_RESOURCE(L"DuplicateProfileText"),
USES_RESOURCE(L"UnknownColorSchemeText"),
USES_RESOURCE(L"InvalidBackgroundImage"),
USES_RESOURCE(L"InvalidIcon"),
USES_RESOURCE(L"AtLeastOneKeybindingWarning"),
USES_RESOURCE(L"TooManyKeysForChord"),
USES_RESOURCE(L"MissingRequiredParameter")
USES_RESOURCE(L"InvalidIcon")
};
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels {
static const std::array<std::wstring_view, 2> settingsLoadErrorsLabels {
USES_RESOURCE(L"NoProfilesText"),
USES_RESOURCE(L"AllProfilesHiddenText")
};
@@ -707,41 +704,6 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// - Implements the F7 handler (per GH#638)
// Return value:
// - whether F7 was handled
bool AppLogic::OnF7Pressed()
{
if (_root)
{
// Manually bubble the OnF7Pressed event up through the focus tree.
auto xamlRoot{ _root->XamlRoot() };
auto focusedObject{ Windows::UI::Xaml::Input::FocusManager::GetFocusedElement(xamlRoot) };
do
{
if (auto f7Listener{ focusedObject.try_as<IF7Listener>() })
{
if (f7Listener.OnF7Pressed())
{
return true;
}
// otherwise, keep walking. bubble the event manually.
}
if (auto focusedElement{ focusedObject.try_as<Windows::UI::Xaml::FrameworkElement>() })
{
focusedObject = focusedElement.Parent();
}
else
{
break; // we hit a non-FE object, stop bubbling.
}
} while (focusedObject);
}
return false;
}
// Method Description:
// - Used to tell the app that the 'X' button has been clicked and
// the user wants to close the app. We kick off the close warning

View File

@@ -38,7 +38,6 @@ namespace winrt::TerminalApp::implementation
hstring Title();
void TitlebarClicked();
bool OnF7Pressed();
void WindowCloseButtonClicked();

View File

@@ -3,7 +3,6 @@
import "../TerminalPage.idl";
import "../ShortcutActionDispatch.idl";
import "../IF7Listener.idl";
namespace TerminalApp
{
@@ -13,7 +12,7 @@ namespace TerminalApp
MaximizedMode,
};
[default_interface] runtimeclass AppLogic: IF7Listener {
[default_interface] runtimeclass AppLogic {
AppLogic();
// For your own sanity, it's better to do setup outside the ctor.

View File

@@ -206,11 +206,9 @@ void CascadiaSettings::_ValidateSettings()
// there's _NO_ keys bound to any actions. That's highly irregular, and
// likely an indication of an error somehow.
// GH#3522 - With variable args to keybindings, it's possible that a user
// TODO:GH#3522 With variable args to keybindings, it's possible that a user
// set a keybinding without all the required args for an action. Display a
// warning if an action didn't have a required arg.
// This will also catch other keybinding warnings, like from GH#4239
_ValidateKeybindings();
}
// Method Description:
@@ -653,23 +651,3 @@ GUID CascadiaSettings::_GetProfileForIndex(std::optional<int> index) const
}
return profileGuid;
}
// Method Description:
// - If there were any warnings we generated while parsing the user's
// keybindings, add them to the list of warnings here. If there were warnings
// generated in this way, we'll add a AtLeastOneKeybindingWarning, which will
// act as a header for the other warnings
// Arguments:
// - <none>
// Return Value:
// - <none>
void CascadiaSettings::_ValidateKeybindings()
{
auto keybindingWarnings = _globals.GetKeybindingsWarnings();
if (!keybindingWarnings.empty())
{
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning);
_warnings.insert(_warnings.end(), keybindingWarnings.begin(), keybindingWarnings.end());
}
}

View File

@@ -116,7 +116,6 @@ private:
void _RemoveHiddenProfiles();
void _ValidateAllSchemesExist();
void _ValidateMediaResources();
void _ValidateKeybindings();
friend class TerminalAppLocalTests::SettingsTests;
friend class TerminalAppLocalTests::ProfileTests;

View File

@@ -43,7 +43,6 @@ static constexpr std::wstring_view SystemThemeValue{ L"system" };
GlobalAppSettings::GlobalAppSettings() :
_keybindings{ winrt::make_self<winrt::TerminalApp::implementation::AppKeyBindings>() },
_keybindingsWarnings{},
_colorSchemes{},
_defaultProfile{},
_alwaysShowTabs{ true },
@@ -331,14 +330,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
if (auto keybindings{ json[JsonKey(KeybindingsKey)] })
{
auto warnings = _keybindings->LayerJson(keybindings);
// It's possible that the user provided keybindings have some warnings
// in them - problems that we should alert the user to, but we can
// recover from. Most of these warnings cannot be detected later in the
// Validate settings phase, so we'll collect them now. If there were any
// warnings generated from parsing these keybindings, add them to our
// list of warnings.
_keybindingsWarnings.insert(_keybindingsWarnings.end(), warnings.begin(), warnings.end());
_keybindings->LayerJson(keybindings);
}
if (auto snapToGridOnResize{ json[JsonKey(SnapToGridOnResizeKey)] })
@@ -545,17 +537,3 @@ void GlobalAppSettings::AddColorScheme(ColorScheme scheme)
std::wstring name{ scheme.GetName() };
_colorSchemes[name] = std::move(scheme);
}
// Method Description:
// - Return the warnings that we've collected during parsing the JSON for the
// keybindings. It's possible that the user provided keybindings have some
// warnings in them - problems that we should alert the user to, but we can
// recover from.
// Arguments:
// - <none>
// Return Value:
// - <none>
std::vector<TerminalApp::SettingsLoadWarnings> GlobalAppSettings::GetKeybindingsWarnings() const
{
return _keybindingsWarnings;
}

View File

@@ -83,14 +83,11 @@ public:
void ApplyToSettings(winrt::Microsoft::Terminal::Settings::TerminalSettings& settings) const noexcept;
std::vector<TerminalApp::SettingsLoadWarnings> GetKeybindingsWarnings() const;
GETSET_PROPERTY(bool, SnapToGridOnResize, true);
private:
GUID _defaultProfile;
winrt::com_ptr<winrt::TerminalApp::implementation::AppKeyBindings> _keybindings;
std::vector<::TerminalApp::SettingsLoadWarnings> _keybindingsWarnings;
std::unordered_map<std::wstring, ColorScheme> _colorSchemes;

View File

@@ -1,16 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace TerminalApp
{
// C++/winrt makes it difficult to share this idl between two projects,
// Instead, we just pin the uuid and include it in both TermControl and App
// If you update this one, please update the one in TerminalControl\TermControl.idl
// If you change this interface, please update the guid.
// If you press F7 and get a runtime error, go make sure both copies are the same.
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")]
interface IF7Listener
{
Boolean OnF7Pressed();
}
}

View File

@@ -131,24 +131,16 @@ the MIT License. See LICENSE in the project root for license information. -->
</ResourceDictionary>
</StackPanel.Resources>
<Button Height="36.0" MinWidth="46.0" Width="46.0"
x:Name="MinimizeButton"
x:Uid="WindowMinimizeButton"
Style="{StaticResource CaptionButton}"
Click="_MinimizeClick"
AutomationProperties.AccessibilityView="Raw">
<Button Height="36.0" MinWidth="46.0" Width="46.0" x:Name="MinimizeButton" Style="{StaticResource CaptionButton}" Click="_MinimizeClick"
AutomationProperties.Name="Minimize">
<Button.Resources>
<ResourceDictionary>
<x:String x:Key="CaptionButtonPath">M 0 0 H 10</x:String>
</ResourceDictionary>
</Button.Resources>
</Button>
<Button Height="36.0" MinWidth="46.0" Width="46.0"
x:Name="MaximizeButton"
x:Uid="WindowMaximizeButton"
Style="{StaticResource CaptionButton}"
Click="_MaximizeClick"
AutomationProperties.AccessibilityView="Raw">
<Button Height="36.0" MinWidth="46.0" Width="46.0" x:Name="MaximizeButton" Style="{StaticResource CaptionButton}" Click="_MaximizeClick"
AutomationProperties.Name="Maximize">
<Button.Resources>
<ResourceDictionary>
<x:String x:Key="CaptionButtonPath">M 0 0 H 10 V 10 H 0 V 0</x:String>
@@ -156,12 +148,8 @@ the MIT License. See LICENSE in the project root for license information. -->
</ResourceDictionary>
</Button.Resources>
</Button>
<Button Height="36.0" MinWidth="46.0" Width="46.0"
x:Name="CloseButton"
x:Uid="WindowCloseButton"
Style="{StaticResource CaptionButton}"
Click="_CloseClick"
AutomationProperties.AccessibilityView="Raw">
<Button Height="36.0" MinWidth="46.0" Width="46.0" x:Name="CloseButton" Style="{StaticResource CaptionButton}" Click="_CloseClick"
AutomationProperties.Name="Close">
<Button.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>

View File

@@ -49,8 +49,6 @@ static constexpr std::string_view BackgroundImageKey{ "backgroundImage" };
static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" };
static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImageStretchMode" };
static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" };
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" };
// Possible values for closeOnExit
static constexpr std::string_view CloseOnExitAlways{ "always" };
@@ -85,10 +83,8 @@ static constexpr std::string_view ImageAlignmentTopRight{ "topRight" };
static constexpr std::string_view ImageAlignmentBottomLeft{ "bottomLeft" };
static constexpr std::string_view ImageAlignmentBottomRight{ "bottomRight" };
// Possible values for TextAntialiasingMode
static constexpr std::wstring_view AntialiasingModeGrayscale{ L"grayscale" };
static constexpr std::wstring_view AntialiasingModeCleartype{ L"cleartype" };
static constexpr std::wstring_view AntialiasingModeAliased{ L"aliased" };
// Terminal effects
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
Profile::Profile() :
Profile(std::nullopt)
@@ -128,8 +124,7 @@ Profile::Profile(const std::optional<GUID>& guid) :
_backgroundImageOpacity{},
_backgroundImageStretchMode{},
_backgroundImageAlignment{},
_retroTerminalEffect{},
_antialiasingMode{ TextAntialiasingMode::Grayscale }
_retroTerminalEffect{}
{
}
@@ -183,7 +178,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
terminalSettings.CursorShape(_cursorShape);
// Fill in the remaining properties from the profile
terminalSettings.ProfileName(_name);
terminalSettings.UseAcrylic(_useAcrylic);
terminalSettings.TintOpacity(_acrylicTransparency);
@@ -263,8 +257,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
terminalSettings.RetroTerminalEffect(_retroTerminalEffect.value());
}
terminalSettings.AntialiasingMode(_antialiasingMode);
return terminalSettings;
}
@@ -385,8 +377,6 @@ Json::Value Profile::ToJson() const
root[JsonKey(RetroTerminalEffectKey)] = _retroTerminalEffect.value();
}
root[JsonKey(AntialiasingModeKey)] = SerializeTextAntialiasingMode(_antialiasingMode).data();
return root;
}
@@ -752,12 +742,6 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetOptionalValue(json, BackgroundImageAlignmentKey, _backgroundImageAlignment, &Profile::_ConvertJsonToAlignment);
JsonUtils::GetOptionalValue(json, RetroTerminalEffectKey, _retroTerminalEffect, Profile::_ConvertJsonToBool);
if (json.isMember(JsonKey(AntialiasingModeKey)))
{
auto antialiasingMode{ json[JsonKey(AntialiasingModeKey)] };
_antialiasingMode = ParseTextAntialiasingMode(GetWstringFromJson(antialiasingMode));
}
}
void Profile::SetFontFace(std::wstring fontFace) noexcept
@@ -1365,49 +1349,3 @@ void Profile::SetRetroTerminalEffect(bool value) noexcept
{
_retroTerminalEffect = value;
}
// Method Description:
// - Helper function for converting a user-specified antialiasing mode
// corresponding TextAntialiasingMode enum value
// Arguments:
// - antialiasingMode: The string value from the settings file to parse
// Return Value:
// - The corresponding enum value which maps to the string provided by the user
TextAntialiasingMode Profile::ParseTextAntialiasingMode(const std::wstring& antialiasingMode)
{
if (antialiasingMode == AntialiasingModeCleartype)
{
return TextAntialiasingMode::Cleartype;
}
else if (antialiasingMode == AntialiasingModeAliased)
{
return TextAntialiasingMode::Aliased;
}
else if (antialiasingMode == AntialiasingModeGrayscale)
{
return TextAntialiasingMode::Grayscale;
}
// default behavior for invalid data
return TextAntialiasingMode::Grayscale;
}
// Method Description:
// - Helper function for converting a TextAntialiasingMode to its corresponding
// string value.
// Arguments:
// - antialiasingMode: The enum value to convert to a string.
// Return Value:
// - The string value for the given TextAntialiasingMode
std::wstring_view Profile::SerializeTextAntialiasingMode(const TextAntialiasingMode antialiasingMode)
{
switch (antialiasingMode)
{
case TextAntialiasingMode::Cleartype:
return AntialiasingModeCleartype;
case TextAntialiasingMode::Aliased:
return AntialiasingModeAliased;
default:
case TextAntialiasingMode::Grayscale:
return AntialiasingModeGrayscale;
}
}

View File

@@ -123,9 +123,6 @@ private:
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
static std::wstring_view _SerializeCursorStyle(const winrt::Microsoft::Terminal::Settings::CursorStyle cursorShape);
static winrt::Microsoft::Terminal::Settings::TextAntialiasingMode ParseTextAntialiasingMode(const std::wstring& antialiasingMode);
static std::wstring_view SerializeTextAntialiasingMode(const winrt::Microsoft::Terminal::Settings::TextAntialiasingMode antialiasingMode);
static GUID _GenerateGuidForProfile(const std::wstring& name, const std::optional<std::wstring>& source) noexcept;
static bool _ConvertJsonToBool(const Json::Value& json);
@@ -169,8 +166,6 @@ private:
std::optional<std::wstring> _icon;
winrt::Microsoft::Terminal::Settings::TextAntialiasingMode _antialiasingMode;
friend class TerminalAppLocalTests::SettingsTests;
friend class TerminalAppLocalTests::ProfileTests;
friend class TerminalAppUnitTests::JsonTests;

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -223,18 +223,6 @@ Temporarily using the Windows Terminal default settings.
<data name="InvalidIcon" xml:space="preserve">
<value>Found a profile with an invalid "icon". Defaulting that profile to have no icon. Make sure that when setting an "icon", the value is a valid file path to an image.</value>
</data>
<data name="AtLeastOneKeybindingWarning" xml:space="preserve">
<value>Warnings were found while parsing your keybindings:
</value>
</data>
<data name="TooManyKeysForChord" xml:space="preserve">
<value> Found a keybinding with too many strings for the "keys" array. There should only be one string value in the "keys" array.
</value>
</data>
<data name="MissingRequiredParameter" xml:space="preserve">
<value> Found a keybinding that was missing a required parameter value. This keybinding will be ignored.
</value>
</data>
<data name="CmdCommandArgDesc" xml:space="preserve">
<value>An optional command, with arguments, to be spawned in the new tab or pane</value>
</data>
@@ -268,19 +256,4 @@ Temporarily using the Windows Terminal default settings.
<data name="CmdStartingDirArgDesc" xml:space="preserve">
<value>Open in the given directory instead of the profile's set startingDirectory</value>
</data>
<data name="NewTabSplitButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.HelpText" xml:space="preserve">
<value>Press the button to open a new terminal tab with your default profile. Open the flyout to select which profile you want to open.</value>
</data>
<data name="NewTabSplitButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>New Tab</value>
</data>
<data name="WindowCloseButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Close</value>
</data>
<data name="WindowMaximizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Maximize</value>
</data>
<data name="WindowMinimizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Minimize</value>
</data>
</root>

View File

@@ -22,7 +22,6 @@ the MIT License. See LICENSE in the project root for license information. -->
<mux:TabView.TabStripFooter>
<mux:SplitButton
x:Name="NewTabButton"
x:Uid="NewTabSplitButton"
Click="OnNewTabButtonClick"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
@@ -30,8 +29,7 @@ the MIT License. See LICENSE in the project root for license information. -->
UseLayoutRounding="true"
FontFamily="Segoe MDL2 Assets"
FontWeight="SemiLight"
FontSize="12"
AutomationProperties.AccessibilityView="Control">
FontSize="12">
<!-- U+E710 is the fancy plus icon. -->
<mux:SplitButton.Resources>
<!-- Override the SplitButton* resources to match the tab view's button's styles. -->

View File

@@ -372,7 +372,6 @@ namespace winrt::TerminalApp::implementation
WUX::Controls::IconSourceElement iconElement;
iconElement.IconSource(iconSource);
profileMenuItem.Icon(iconElement);
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
}
if (profile.GetGuid() == defaultProfileGuid)
@@ -603,10 +602,8 @@ namespace winrt::TerminalApp::implementation
profile->GetConnectionType() == AzureConnectionType &&
TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
{
// TODO GH#4661: Replace this with directly using the AzCon when our VT is better
std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
azBridgePath.replace_filename(L"TerminalAzBridge.exe");
connection = TerminalConnection::ConptyConnection(azBridgePath.wstring(), L".", L"Azure", settings.InitialRows(), settings.InitialCols(), winrt::guid());
connection = TerminalConnection::AzureConnection(settings.InitialRows(),
settings.InitialCols());
}
else if (profile->HasConnectionType() &&
@@ -1045,12 +1042,10 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - splitType: one value from the TerminalApp::SplitState enum, indicating how the
// new pane should be split from its parent.
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
// - newTerminalArgs: An object that may contain a blob of parameters to
// control which profile is created and with possible other
// configurations. See CascadiaSettings::BuildSettings for more details.
void TerminalPage::_SplitPane(const TerminalApp::SplitState splitType,
const TerminalApp::SplitType splitMode,
const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs)
{
// Do nothing if we're requesting no split.
@@ -1069,24 +1064,7 @@ namespace winrt::TerminalApp::implementation
auto focusedTab = _GetStrongTabImpl(*indexOpt);
winrt::Microsoft::Terminal::Settings::TerminalSettings controlSettings;
GUID realGuid;
bool profileFound = false;
if (splitMode == TerminalApp::SplitType::Duplicate)
{
std::optional<GUID> current_guid = focusedTab->GetFocusedProfile();
if (current_guid)
{
profileFound = true;
controlSettings = _settings->BuildSettings(current_guid.value());
realGuid = current_guid.value();
}
}
if (!profileFound)
{
std::tie(realGuid, controlSettings) = _settings->BuildSettings(newTerminalArgs);
}
const auto [realGuid, controlSettings] = _settings->BuildSettings(newTerminalArgs);
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings);
@@ -1345,7 +1323,7 @@ namespace winrt::TerminalApp::implementation
bool TerminalPage::_CopyText(const bool trimTrailingWhitespace)
{
const auto control = _GetActiveControl();
return control.CopySelectionToClipboard(!trimTrailingWhitespace);
return control.CopySelectionToClipboard(trimTrailingWhitespace);
}
// Method Description:

View File

@@ -122,7 +122,7 @@ namespace winrt::TerminalApp::implementation
// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
void _Scroll(int delta);
void _SplitPane(const winrt::TerminalApp::SplitState splitType, const winrt::TerminalApp::SplitType splitMode = winrt::TerminalApp::SplitType::Manual, const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs = nullptr);
void _SplitPane(const winrt::TerminalApp::SplitState splitType, const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs = nullptr);
void _ResizePane(const Direction& direction);
void _ScrollPage(int delta);
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);

View File

@@ -25,11 +25,7 @@ namespace TerminalApp
DuplicateProfile = 1,
UnknownColorScheme = 2,
InvalidBackgroundImage = 3,
InvalidIcon = 4,
AtLeastOneKeybindingWarning = 5,
TooManyKeysForChord = 6,
MissingRequiredParameter = 7,
WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
InvalidIcon = 4
};
// SettingsLoadWarnings are scenarios where the settings had invalid state
@@ -37,8 +33,7 @@ namespace TerminalApp
enum class SettingsLoadErrors : uint32_t
{
NoProfiles = 0,
AllProfilesHidden = 1,
ERRORS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
AllProfilesHidden = 1
};
// This is a helper class to wrap up a SettingsLoadErrors into a proper

View File

@@ -30,8 +30,7 @@
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
"padding": "8, 8, 8, 8",
"snapOnInput": true,
"useAcrylic": false,
"antialiasingMode": "grayscale"
"useAcrylic": false
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
@@ -49,8 +48,7 @@
"icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
"padding": "8, 8, 8, 8",
"snapOnInput": true,
"useAcrylic": false,
"antialiasingMode": "grayscale"
"useAcrylic": false
}
],
"schemes":
@@ -205,55 +203,55 @@
],
"keybindings":
[
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": "closeWindow", "keys": "alt+f4" },
{ "command": "copy", "keys": "ctrl+shift+c" },
{ "command": "copy", "keys": "ctrl+insert" },
{ "command": "decreaseFontSize", "keys": "ctrl+-" },
{ "command": "duplicateTab", "keys": "ctrl+shift+d" },
{ "command": "increaseFontSize", "keys": "ctrl+=" },
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down" },
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left" },
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right" },
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up" },
{ "command": "newTab", "keys": "ctrl+shift+t" },
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1" },
{ "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2" },
{ "command": { "action": "newTab", "index": 2 }, "keys": "ctrl+shift+3" },
{ "command": { "action": "newTab", "index": 3 }, "keys": "ctrl+shift+4" },
{ "command": { "action": "newTab", "index": 4 }, "keys": "ctrl+shift+5" },
{ "command": { "action": "newTab", "index": 5 }, "keys": "ctrl+shift+6" },
{ "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7" },
{ "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8" },
{ "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9" },
{ "command": "nextTab", "keys": "ctrl+tab" },
{ "command": "openNewTabDropdown", "keys": "ctrl+shift+space" },
{ "command": "openSettings", "keys": "ctrl+," },
{ "command": "paste", "keys": "ctrl+shift+v" },
{ "command": "paste", "keys": "shift+insert" },
{ "command": "prevTab", "keys": "ctrl+shift+tab" },
{ "command": "resetFontSize", "keys": "ctrl+0" },
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
{ "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up" },
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },
{ "command": "scrollUp", "keys": "ctrl+shift+up" },
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": "alt+shift+-" },
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": "alt+shift+plus" },
{ "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1" },
{ "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2" },
{ "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3" },
{ "command": { "action": "switchToTab", "index": 3 }, "keys": "ctrl+alt+4" },
{ "command": { "action": "switchToTab", "index": 4 }, "keys": "ctrl+alt+5" },
{ "command": { "action": "switchToTab", "index": 5 }, "keys": "ctrl+alt+6" },
{ "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7" },
{ "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8" },
{ "command": { "action": "switchToTab", "index": 8 }, "keys": "ctrl+alt+9" },
{ "command": "find", "keys": "ctrl+shift+f" },
{ "command": "toggleFullscreen", "keys": "alt+enter" },
{ "command": "toggleFullscreen", "keys": "f11" }
{ "command": "closePane", "keys": [ "ctrl+shift+w" ] },
{ "command": "closeWindow", "keys": [ "alt+f4" ] },
{ "command": "copy", "keys": [ "ctrl+shift+c" ] },
{ "command": "copy", "keys": [ "ctrl+insert" ] },
{ "command": "decreaseFontSize", "keys": [ "ctrl+-" ] },
{ "command": "duplicateTab", "keys": [ "ctrl+shift+d" ] },
{ "command": "increaseFontSize", "keys": [ "ctrl+=" ] },
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": [ "alt+down" ] },
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": [ "alt+left" ] },
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": [ "alt+right" ] },
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": [ "alt+up" ] },
{ "command": "newTab", "keys": [ "ctrl+shift+t" ] },
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+1"] },
{ "command": { "action": "newTab", "index": 1 }, "keys": ["ctrl+shift+2"] },
{ "command": { "action": "newTab", "index": 2 }, "keys": ["ctrl+shift+3"] },
{ "command": { "action": "newTab", "index": 3 }, "keys": ["ctrl+shift+4"] },
{ "command": { "action": "newTab", "index": 4 }, "keys": ["ctrl+shift+5"] },
{ "command": { "action": "newTab", "index": 5 }, "keys": ["ctrl+shift+6"] },
{ "command": { "action": "newTab", "index": 6 }, "keys": ["ctrl+shift+7"] },
{ "command": { "action": "newTab", "index": 7 }, "keys": ["ctrl+shift+8"] },
{ "command": { "action": "newTab", "index": 8 }, "keys": ["ctrl+shift+9"] },
{ "command": "nextTab", "keys": [ "ctrl+tab" ] },
{ "command": "openNewTabDropdown", "keys": [ "ctrl+shift+space" ] },
{ "command": "openSettings", "keys": [ "ctrl+," ] },
{ "command": "paste", "keys": [ "ctrl+shift+v" ] },
{ "command": "paste", "keys": [ "shift+insert" ] },
{ "command": "prevTab", "keys": [ "ctrl+shift+tab" ] },
{ "command": "resetFontSize", "keys": ["ctrl+0"]},
{ "command": { "action": "resizePane", "direction": "down" }, "keys": [ "alt+shift+down" ] },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": [ "alt+shift+left" ] },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": [ "alt+shift+right" ] },
{ "command": { "action": "resizePane", "direction": "up" }, "keys": [ "alt+shift+up" ] },
{ "command": "scrollDown", "keys": [ "ctrl+shift+down" ] },
{ "command": "scrollDownPage", "keys": [ "ctrl+shift+pgdn" ] },
{ "command": "scrollUp", "keys": [ "ctrl+shift+up" ] },
{ "command": "scrollUpPage", "keys": [ "ctrl+shift+pgup" ] },
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": [ "alt+shift+-" ] },
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": [ "alt+shift+plus" ] },
{ "command": { "action": "switchToTab", "index": 0 }, "keys": ["ctrl+alt+1"] },
{ "command": { "action": "switchToTab", "index": 1 }, "keys": ["ctrl+alt+2"] },
{ "command": { "action": "switchToTab", "index": 2 }, "keys": ["ctrl+alt+3"] },
{ "command": { "action": "switchToTab", "index": 3 }, "keys": ["ctrl+alt+4"] },
{ "command": { "action": "switchToTab", "index": 4 }, "keys": ["ctrl+alt+5"] },
{ "command": { "action": "switchToTab", "index": 5 }, "keys": ["ctrl+alt+6"] },
{ "command": { "action": "switchToTab", "index": 6 }, "keys": ["ctrl+alt+7"] },
{ "command": { "action": "switchToTab", "index": 7 }, "keys": ["ctrl+alt+8"] },
{ "command": { "action": "switchToTab", "index": 8 }, "keys": ["ctrl+alt+9"] },
{ "command": "find", "keys": [ "ctrl+shift+f" ] },
{ "command": "toggleFullscreen", "keys": [ "alt+enter" ] },
{ "command": "toggleFullscreen", "keys": [ "f11" ] }
]
}

View File

@@ -186,7 +186,6 @@
<ItemGroup>
<!-- If you add idl files here, make sure to include their implementation's
header in TerminalApp.vcxproj (as well as in this file) -->
<Midl Include="../IF7Listener.idl" />
<Midl Include="../App.idl">
<DependentUpon>../App.xaml</DependentUpon>
</Midl>
@@ -214,7 +213,7 @@
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<PRIResource Include="../Resources/Resources.language-en.resw" />
<PRIResource Include="../Resources/en-US/Resources.resw" />
<None Include="../packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
@@ -355,4 +354,4 @@
<Target Name="_TerminalAppGenerateUserSettingsH" Inputs="..\userDefaults.json" Outputs="Generated Files\userDefaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile ..\userDefaults.json -OutPath '&quot;Generated Files\userDefaults.h&quot;' -VariableName UserSettingsJson" />
</Target>
</Project>
</Project>

View File

@@ -36,7 +36,6 @@
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Documents.h"
#include "winrt/Windows.UI.Xaml.Automation.h"
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Microsoft.Toolkit.Win32.UI.XamlHost.h>

View File

@@ -1,87 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ConsoleInputReader.h"
#include "unicode.hpp"
ConsoleInputReader::ConsoleInputReader(HANDLE handle) :
_handle(handle)
{
_buffer.resize(BufferSize);
_convertedString.reserve(BufferSize);
}
void ConsoleInputReader::SetWindowSizeChangedCallback(std::function<void()> callback)
{
_windowSizeChangedCallback = std::move(callback);
}
std::optional<std::wstring_view> ConsoleInputReader::Read()
{
DWORD readCount{ 0 };
_convertedString.clear();
while (_convertedString.empty())
{
_buffer.resize(BufferSize);
BOOL succeeded =
ReadConsoleInputW(_handle, _buffer.data(), gsl::narrow_cast<DWORD>(_buffer.size()), &readCount);
if (!succeeded)
{
return std::nullopt;
}
_buffer.resize(readCount);
for (auto it = _buffer.begin(); it != _buffer.end(); ++it)
{
if (it->EventType == WINDOW_BUFFER_SIZE_EVENT && _windowSizeChangedCallback)
{
_windowSizeChangedCallback();
}
else if (it->EventType == KEY_EVENT)
{
const auto& keyEvent = it->Event.KeyEvent;
if (keyEvent.bKeyDown || (!keyEvent.bKeyDown && keyEvent.wVirtualKeyCode == VK_MENU))
{
// Got a high surrogate at the end of the buffer
if (IS_HIGH_SURROGATE(keyEvent.uChar.UnicodeChar))
{
_highSurrogate.emplace(keyEvent.uChar.UnicodeChar);
continue; // we've consumed it -- only dispatch it if we get a low
}
if (IS_LOW_SURROGATE(keyEvent.uChar.UnicodeChar))
{
// No matter what we do, we want to destructively consume the high surrogate
if (const auto oldHighSurrogate{ std::exchange(_highSurrogate, std::nullopt) })
{
_convertedString.push_back(*_highSurrogate);
}
else
{
// If we get a low without a high surrogate, we've done everything we can.
// This is an illegal state.
_convertedString.push_back(UNICODE_REPLACEMENT);
continue; // onto the next event
}
}
// (\0 with a scancode is probably a modifier key, not a VT input key)
if (keyEvent.uChar.UnicodeChar != L'\0' || keyEvent.wVirtualScanCode == 0)
{
if (_highSurrogate) // non-destructive: we don't want to set it to nullopt needlessly for every character
{
// If we get a high surrogate *here*, we didn't find a low surrogate.
// This state is also illegal.
_convertedString.push_back(UNICODE_REPLACEMENT);
_highSurrogate.reset();
}
_convertedString.push_back(keyEvent.uChar.UnicodeChar);
}
}
}
}
}
return _convertedString;
}

View File

@@ -1,33 +0,0 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
Module Name:
ConsoleInputReader.h
Abstract:
This file contains a class whose sole purpose is to
abstract away a bunch of details you usually need to
know to read VT from a console input handle.
--*/
class ConsoleInputReader
{
public:
ConsoleInputReader(HANDLE handle);
void SetWindowSizeChangedCallback(std::function<void()> callback);
std::optional<std::wstring_view> Read();
private:
static constexpr size_t BufferSize{ 128 };
HANDLE _handle;
std::wstring _convertedString;
std::vector<INPUT_RECORD> _buffer;
std::optional<wchar_t> _highSurrogate;
std::function<void()> _windowSizeChangedCallback;
};

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{067F0A06-FCB7-472C-96E9-B03B54E8E18D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>TerminalAzBridge</RootNamespace>
<ProjectName>TerminalAzBridge</ProjectName>
<TargetName>TerminalAzBridge</TargetName>
<ConfigurationType>Application</ConfigurationType>
<OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
<ApplicationType>Windows Store</ApplicationType>
<NoOutputRedirection>true</NoOutputRedirection>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<PropertyGroup>
<GenerateManifest>true</GenerateManifest>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<!-- Source Files -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="ConsoleInputReader.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="ConsoleInputReader.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<!-- Dependencies -->
<ItemGroup>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
<Project>{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
</ItemGroup>
<!--
This ItemGroup and the Globals PropertyGroup below it are required in order
to enable F5 debugging for the unpackaged application
-->
<ItemGroup>
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_general.xml" />
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_local_windows.xml" />
</ItemGroup>
<PropertyGroup Label="Globals">
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
</Project>

View File

@@ -1,104 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "winrt/Microsoft.Terminal.TerminalConnection.h"
#include "ConsoleInputReader.h"
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
static COORD GetConsoleScreenSize(HANDLE outputHandle)
{
CONSOLE_SCREEN_BUFFER_INFOEX csbiex{};
csbiex.cbSize = sizeof(csbiex);
GetConsoleScreenBufferInfoEx(outputHandle, &csbiex);
return {
(csbiex.srWindow.Right - csbiex.srWindow.Left) + 1,
(csbiex.srWindow.Bottom - csbiex.srWindow.Top) + 1
};
}
static ConnectionState RunConnectionToCompletion(const ITerminalConnection& connection, HANDLE outputHandle, HANDLE inputHandle)
{
connection.TerminalOutput([outputHandle](const winrt::hstring& output) {
WriteConsoleW(outputHandle, output.data(), output.size(), nullptr, nullptr);
});
// Detach a thread to spin the console read indefinitely.
// This application exits when the connection is closed, so
// the connection's lifetime will outlast this thread.
std::thread([connection, outputHandle, inputHandle] {
ConsoleInputReader reader{ inputHandle };
reader.SetWindowSizeChangedCallback([&]() {
const auto size = GetConsoleScreenSize(outputHandle);
connection.Resize(size.Y, size.X);
});
while (true)
{
auto input = reader.Read();
if (input)
{
connection.WriteInput(*input);
}
}
}).detach();
std::condition_variable stateChangeVar;
std::optional<ConnectionState> state;
std::mutex stateMutex;
connection.StateChanged([&](auto&& /*s*/, auto&& /*e*/) {
std::unique_lock<std::mutex> lg{ stateMutex };
state = connection.State();
stateChangeVar.notify_all();
});
connection.Start();
std::unique_lock<std::mutex> lg{ stateMutex };
stateChangeVar.wait(lg, [&]() {
if (!state.has_value())
{
return false;
}
return state.value() == ConnectionState::Closed || state.value() == ConnectionState::Failed;
});
return state.value();
}
int wmain(int /*argc*/, wchar_t** /*argv*/)
{
winrt::init_apartment(winrt::apartment_type::single_threaded);
DWORD inputMode{}, outputMode{};
HANDLE conIn{ GetStdHandle(STD_INPUT_HANDLE) }, conOut{ GetStdHandle(STD_OUTPUT_HANDLE) };
UINT codepage{ GetConsoleCP() }, outputCodepage{ GetConsoleOutputCP() };
RETURN_IF_WIN32_BOOL_FALSE(GetConsoleMode(conIn, &inputMode));
RETURN_IF_WIN32_BOOL_FALSE(GetConsoleMode(conOut, &outputMode));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleMode(conIn, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleMode(conOut, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT | DISABLE_NEWLINE_AUTO_RETURN));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleCP(CP_UTF8));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(CP_UTF8));
auto restoreConsoleModes = wil::scope_exit([&]() {
SetConsoleMode(conIn, inputMode);
SetConsoleMode(conOut, outputMode);
SetConsoleCP(codepage);
SetConsoleOutputCP(outputCodepage);
});
const auto size = GetConsoleScreenSize(conOut);
AzureConnection azureConn{ gsl::narrow_cast<uint32_t>(size.Y), gsl::narrow_cast<uint32_t>(size.X) };
const auto state = RunConnectionToCompletion(azureConn, conOut, conIn);
return state == ConnectionState::Closed ? 0 : 1;
}

View File

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

View File

@@ -1,4 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"

View File

@@ -1,36 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- pch.h
Abstract:
- Contains external headers to include in the precompile phase of console build process.
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
--*/
#pragma once
// Ignore checked iterators warning from VC compiler.
#define _SCL_SECURE_NO_WARNINGS
// Block minwindef.h min/max macros to prevent <algorithm> conflict
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <unknwn.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#include <windows.h>
#include "../inc/LibraryIncludes.h"
#include <wil/cppwinrt.h>
#include <winrt/Windows.system.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <wil/resource.h>
#include <wil/win32_helpers.h>

View File

@@ -12,7 +12,6 @@
#include <sstream>
#include <stdlib.h>
#include <LibraryResources.h>
#include <unicode.hpp>
#include "AzureConnection.g.cpp"
@@ -33,38 +32,6 @@ static constexpr int CurrentCredentialVersion = 1;
static constexpr auto PasswordVaultResourceName = L"Terminal";
static constexpr auto HttpUserAgent = L"Terminal/0.0";
#define FAILOUT_IF_OPTIONAL_EMPTY(optional) \
do \
{ \
if (!((optional).has_value())) \
{ \
return E_FAIL; \
} \
} while (0, 0)
static constexpr int USER_INPUT_COLOR = 93; // yellow - the color of something the user can type
static constexpr int USER_INFO_COLOR = 97; // white - the color of clarifying information
static inline std::wstring _colorize(const unsigned int colorCode, const std::wstring_view text)
{
return wil::str_printf<std::wstring>(L"\x1b[%um%.*s\x1b[m", colorCode, gsl::narrow_cast<size_t>(text.size()), text.data());
}
// Takes N resource names, loads the first one as a format string, and then
// loads all the remaining ones into the %s arguments in the first one after
// colorizing them in the USER_INPUT_COLOR.
// This is intended to be used to drop UserEntry resources into an existing string.
template<typename... Args>
static inline std::wstring _formatResWithColoredUserInputOptions(const std::wstring_view resourceKey, Args&&... args)
{
return wil::str_printf<std::wstring>(GetLibraryResourceString(resourceKey).data(), (_colorize(USER_INPUT_COLOR, GetLibraryResourceString(args)).data())...);
}
static inline std::wstring _formatTenantLine(int tenantNumber, const std::wstring_view tenantName, const std::wstring_view tenantID)
{
return wil::str_printf<std::wstring>(RS_(L"AzureIthTenant").data(), _colorize(USER_INPUT_COLOR, std::to_wstring(tenantNumber)).data(), _colorize(USER_INFO_COLOR, tenantName).data(), tenantID.data());
}
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
// This function exists because the clientID only gets added by the release pipelines
@@ -78,6 +45,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
AzureConnection::AzureConnection(const uint32_t initialRows, const uint32_t initialCols) :
_initialRows{ initialRows },
_initialCols{ initialCols },
_maxStored{},
_maxSize{},
_expiry{}
{
}
@@ -86,7 +55,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - helper that will write an unterminated string (generally, from a resource) to the output stream.
// Arguments:
// - str: the string to write.
void AzureConnection::_WriteStringWithNewline(const std::wstring_view str)
void AzureConnection::_WriteStringWithNewline(const winrt::hstring& str)
{
_TerminalOutputHandlers(str + L"\r\n");
}
@@ -110,35 +79,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_transitionToState(ConnectionState::Connecting);
}
std::optional<std::wstring> AzureConnection::_ReadUserInput(InputMode mode)
{
std::unique_lock<std::mutex> inputLock{ _inputMutex };
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
return std::nullopt;
}
_currentInputMode = mode;
_TerminalOutputHandlers(L"\x1b[92m"); // Make prompted user input green
_inputEvent.wait(inputLock, [this, mode]() {
return _currentInputMode != mode || _isStateAtOrBeyond(ConnectionState::Closing);
});
_TerminalOutputHandlers(L"\x1b[m");
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
return std::nullopt;
}
std::wstring readInput{};
_userInput.swap(readInput);
return readInput;
}
// Method description:
// - ascribes to the ITerminalConnection interface
// - handles the different possible inputs in the different states
@@ -152,46 +92,108 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return;
}
if (_state == AzureState::TermConnected)
// Parse the input differently depending on which state we're in
switch (_state)
{
// The user has stored connection settings, let them choose one of them, create a new one or remove all stored ones
case AzureState::AccessStored:
{
const auto s = winrt::to_string(data);
int storeNum = -1;
try
{
storeNum = std::stoi(s);
}
catch (...)
{
std::lock_guard<std::mutex> lg{ _commonMutex };
if (data == RS_(L"AzureUserEntry_RemoveStored"))
{
_removeOrNew = true;
}
else if (data == RS_(L"AzureUserEntry_NewLogin"))
{
_removeOrNew = false;
}
if (_removeOrNew.has_value())
{
_canProceed.notify_one();
}
else
{
_WriteStringWithNewline(RS_(L"AzureInvalidAccessInput"));
}
return;
}
if (storeNum >= _maxStored)
{
_WriteStringWithNewline(RS_(L"AzureNumOutOfBoundsError"));
return;
}
std::lock_guard<std::mutex> lg{ _commonMutex };
_storedNumber = storeNum;
_canProceed.notify_one();
return;
}
// The user has multiple tenants in their Azure account, let them choose one of them
case AzureState::TenantChoice:
{
int tenantNum = -1;
try
{
tenantNum = std::stoi(winrt::to_string(data));
}
catch (...)
{
_WriteStringWithNewline(RS_(L"AzureNonNumberError"));
return;
}
if (tenantNum >= _maxSize)
{
_WriteStringWithNewline(RS_(L"AzureNumOutOfBoundsError"));
return;
}
std::lock_guard<std::mutex> lg{ _commonMutex };
_tenantNumber = tenantNum;
_canProceed.notify_one();
return;
}
// User has the option to save their connection settings for future logins
case AzureState::StoreTokens:
{
std::lock_guard<std::mutex> lg{ _commonMutex };
if (data == RS_(L"AzureUserEntry_Yes"))
{
_store = true;
}
else if (data == RS_(L"AzureUserEntry_No"))
{
_store = false;
}
if (_store.has_value())
{
_canProceed.notify_one();
}
else
{
_WriteStringWithNewline(RS_(L"AzureInvalidStoreInput"));
}
return;
}
// We are connected, send user's input over the websocket
case AzureState::TermConnected:
{
// If we're connected, we don't need to do any fun input shenanigans.
websocket_outgoing_message msg;
const auto str = winrt::to_string(data);
msg.set_utf8_message(str);
_cloudShellSocket.send(msg).get();
}
default:
return;
}
std::lock_guard<std::mutex> lock{ _inputMutex };
if (data.size() > 0 && (gsl::at(data, 0) == UNICODE_BACKSPACE || gsl::at(data, 0) == UNICODE_DEL)) // BS or DEL
{
if (_userInput.size() > 0)
{
_userInput.pop_back();
_TerminalOutputHandlers(L"\x08 \x08"); // overstrike the character with a space
}
}
else
{
_TerminalOutputHandlers(data); // echo back
switch (_currentInputMode)
{
case InputMode::Line:
if (data.size() > 0 && gsl::at(data, 0) == UNICODE_CARRIAGERETURN)
{
_TerminalOutputHandlers(L"\r\n"); // we probably got a \r, so we need to advance to the next line.
_currentInputMode = InputMode::None; // toggling the mode indicates completion
_inputEvent.notify_one();
break;
}
[[fallthrough]];
default:
std::copy(data.cbegin(), data.cend(), std::back_inserter(_userInput));
break;
}
}
}
// Method description:
@@ -229,8 +231,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
if (_transitionToState(ConnectionState::Closing))
{
_inputEvent.notify_all();
_canProceed.notify_all();
if (_state == AzureState::TermConnected)
{
// Close the websocket connection
@@ -403,8 +404,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_state = AzureState::DeviceFlow;
return S_FALSE;
}
int numTenants{ 0 };
_maxStored = 0;
for (const auto& entry : credList)
{
auto nameJson = json::value::parse(entry.UserName().c_str());
@@ -422,11 +422,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
continue;
}
_WriteStringWithNewline(_formatTenantLine(numTenants, nameJson.at(L"displayName").as_string(), nameJson.at(L"tenantID").as_string()));
numTenants++;
winrt::hstring tenantLine{ wil::str_printf<std::wstring>(RS_(L"AzureIthTenant").c_str(), _maxStored, nameJson.at(L"displayName").as_string().c_str(), nameJson.at(L"tenantID").as_string().c_str()) };
_WriteStringWithNewline(tenantLine);
_maxStored++;
}
if (!numTenants)
if (!_maxStored)
{
if (oldVersionEncountered)
{
@@ -438,53 +439,33 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
_WriteStringWithNewline(RS_(L"AzureEnterTenant"));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureNewLogin"), USES_RESOURCE(L"AzureUserEntry_NewLogin")));
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureRemoveStored"), USES_RESOURCE(L"AzureUserEntry_RemoveStored")));
_WriteStringWithNewline(RS_(L"AzureNewLogin"));
_WriteStringWithNewline(RS_(L"AzureRemoveStored"));
int selectedTenant{ -1 };
do
std::unique_lock<std::mutex> storedLock{ _commonMutex };
_canProceed.wait(storedLock, [=]() {
return (_storedNumber >= 0 && _storedNumber < _maxStored) || _removeOrNew.has_value() || _isStateAtOrBeyond(ConnectionState::Closing);
});
// User might have closed the tab while we waited for input
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
auto maybeTenantSelection = _ReadUserInput(InputMode::Line);
FAILOUT_IF_OPTIONAL_EMPTY(maybeTenantSelection);
const auto& tenantSelection = maybeTenantSelection.value();
if (tenantSelection == RS_(L"AzureUserEntry_RemoveStored"))
{
// User wants to remove the stored settings
_RemoveCredentials();
_state = AzureState::DeviceFlow;
return S_OK;
}
else if (tenantSelection == RS_(L"AzureUserEntry_NewLogin"))
{
// User wants to login with a different account
_state = AzureState::DeviceFlow;
return S_OK;
}
else
{
try
{
selectedTenant = std::stoi(tenantSelection);
if (selectedTenant < 0 || selectedTenant >= numTenants)
{
_WriteStringWithNewline(RS_(L"AzureNumOutOfBoundsError"));
continue; // go 'round again
}
break;
}
catch (...)
{
// suppress exceptions in conversion
}
}
// if we got here, we didn't break out of the loop early and need to go 'round again
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureInvalidAccessInput"), USES_RESOURCE(L"AzureUserEntry_NewLogin"), USES_RESOURCE(L"AzureUserEntry_RemoveStored")));
} while (true);
return E_FAIL;
}
else if (_removeOrNew.has_value() && _removeOrNew.value())
{
// User wants to remove the stored settings
_RemoveCredentials();
_state = AzureState::DeviceFlow;
return S_OK;
}
else if (_removeOrNew.has_value() && !_removeOrNew.value())
{
// User wants to login with a different account
_state = AzureState::DeviceFlow;
return S_OK;
}
// User wants to login with one of the saved connection settings
auto desiredCredential = credList.GetAt(selectedTenant);
auto desiredCredential = credList.GetAt(_storedNumber);
desiredCredential.RetrievePassword();
auto nameJson = json::value::parse(desiredCredential.UserName().c_str());
auto passWordJson = json::value::parse(desiredCredential.Password().c_str());
@@ -586,43 +567,27 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
try
{
const auto tenantListAsArray = _tenantList.as_array();
auto numTenants = gsl::narrow<int>(tenantListAsArray.size());
for (int i = 0; i < numTenants; i++)
_maxSize = gsl::narrow<int>(tenantListAsArray.size());
for (int i = 0; i < _maxSize; i++)
{
const auto& tenant = tenantListAsArray.at(i);
const auto [tenantId, tenantDisplayName] = _crackTenant(tenant);
_WriteStringWithNewline(_formatTenantLine(i, tenantDisplayName, tenantId));
winrt::hstring tenantLine{ wil::str_printf<std::wstring>(RS_(L"AzureIthTenant").c_str(), i, tenantDisplayName.c_str(), tenantId.c_str()) };
_WriteStringWithNewline(tenantLine);
}
_WriteStringWithNewline(RS_(L"AzureEnterTenant"));
int selectedTenant{ -1 };
do
// Use a lock to wait for the user to input a valid number
std::unique_lock<std::mutex> tenantNumberLock{ _commonMutex };
_canProceed.wait(tenantNumberLock, [=]() {
return (_tenantNumber >= 0 && _tenantNumber < _maxSize) || _isStateAtOrBeyond(ConnectionState::Closing);
});
// User might have closed the tab while we waited for input
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
auto maybeTenantSelection = _ReadUserInput(InputMode::Line);
FAILOUT_IF_OPTIONAL_EMPTY(maybeTenantSelection);
return E_FAIL;
}
const auto& tenantSelection = maybeTenantSelection.value();
try
{
selectedTenant = std::stoi(tenantSelection);
if (selectedTenant < 0 || selectedTenant >= numTenants)
{
_WriteStringWithNewline(RS_(L"AzureNumOutOfBoundsError"));
continue;
}
break;
}
catch (...)
{
// suppress exceptions in conversion
}
// if we got here, we didn't break out of the loop early and need to go 'round again
_WriteStringWithNewline(RS_(L"AzureNonNumberError"));
} while (true);
const auto& chosenTenant = tenantListAsArray.at(selectedTenant);
const auto& chosenTenant = tenantListAsArray.at(_tenantNumber);
std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant);
// We have to refresh now that we have the tenantID
@@ -644,28 +609,24 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - S_OK otherwise
HRESULT AzureConnection::_StoreHelper()
{
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureStorePrompt"), USES_RESOURCE(L"AzureUserEntry_Yes"), USES_RESOURCE(L"AzureUserEntry_No")));
_WriteStringWithNewline(RS_(L"AzureStorePrompt"));
// Wait for user input
do
std::unique_lock<std::mutex> storeLock{ _commonMutex };
_canProceed.wait(storeLock, [=]() {
return _store.has_value() || _isStateAtOrBeyond(ConnectionState::Closing);
});
// User might have closed the tab while we waited for input
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
auto maybeStoreCredentials = _ReadUserInput(InputMode::Line);
FAILOUT_IF_OPTIONAL_EMPTY(maybeStoreCredentials);
return E_FAIL;
}
const auto& storeCredentials = maybeStoreCredentials.value();
if (storeCredentials == RS_(L"AzureUserEntry_Yes"))
{
_StoreCredential();
_WriteStringWithNewline(RS_(L"AzureTokensStored"));
break;
}
else if (storeCredentials == RS_(L"AzureUserEntry_No"))
{
break; // we're done, but the user wants nothing.
}
// if we got here, we didn't break out of the loop early and need to go 'round again
_WriteStringWithNewline(_formatResWithColoredUserInputOptions(USES_RESOURCE(L"AzureInvalidStoreInput"), USES_RESOURCE(L"AzureUserEntry_Yes"), USES_RESOURCE(L"AzureUserEntry_No")));
} while (true);
if (_store.value())
{
// User has opted to store the connection settings
_StoreCredential();
_WriteStringWithNewline(RS_(L"AzureTokensStored"));
}
_state = AzureState::TermConnecting;
return S_OK;
@@ -693,7 +654,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_WriteStringWithNewline(RS_(L"AzureSuccess"));
// Request for a terminal for said cloud shell
const auto shellType = settingsResponse.at(L"properties").at(L"preferredShellType").as_string();
// We only support bash for now, so don't bother with the user's preferred shell
// fyi: we can't call powershell yet because it sends VT sequences we don't support yet
// TODO: GitHub #1883
//const auto shellType = settingsResponse.at(L"properties").at(L"preferredShellType").as_string();
const auto shellType = L"bash";
_WriteStringWithNewline(RS_(L"AzureRequestingTerminal"));
const auto socketUri = _GetTerminal(shellType);
_TerminalOutputHandlers(L"\r\n");
@@ -703,14 +668,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
connReqTask.wait();
_state = AzureState::TermConnected;
std::wstring queuedUserInput{};
std::swap(_userInput, queuedUserInput);
if (queuedUserInput.size() > 0)
{
WriteInput(static_cast<winrt::hstring>(queuedUserInput)); // send the user's queued up input back through
}
return S_OK;
}
@@ -876,8 +833,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
http_request shellRequest(L"PUT");
shellRequest.set_request_uri(L"providers/Microsoft.Portal/consoles/default?api-version=2018-10-01");
_HeaderHelper(shellRequest);
// { "properties": { "osType": "linux" } }
auto body = json::value::object({ { U("properties"), json::value::object({ { U("osType"), json::value::string(U("linux")) } }) } });
const auto innerBody = json::value::parse(U("{ \"osType\" : \"linux\" }"));
json::value body;
body[U("properties")] = innerBody;
shellRequest.set_body(body);
// Send the request and get the response as a json value
@@ -956,15 +914,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_WriteStringWithNewline(RS_(L"AzureNoTokens"));
return;
}
for (const auto& cred : credList)
while (credList.Size() > 0)
{
try
{
vault.Remove(cred);
vault.Remove(credList.GetAt(0));
}
catch (...)
{
_WriteStringWithNewline(RS_(L"AzureTokensRemoved"));
return;
}
CATCH_LOG();
}
_WriteStringWithNewline(RS_(L"AzureTokensRemoved"));
}
}

View File

@@ -31,6 +31,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
private:
uint32_t _initialRows{};
uint32_t _initialCols{};
int _storedNumber{ -1 };
int _maxStored;
int _tenantNumber{ -1 };
int _maxSize;
std::condition_variable _canProceed;
std::mutex _commonMutex;
enum class AzureState
{
@@ -45,6 +51,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
AzureState _state{ AzureState::AccessStored };
std::optional<bool> _store;
std::optional<bool> _removeOrNew;
wil::unique_handle _hOutputThread;
static DWORD WINAPI StaticOutputThreadProc(LPVOID lpParameter);
@@ -58,17 +67,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const utility::string_t _loginUri{ U("https://login.microsoftonline.com/") };
const utility::string_t _resourceUri{ U("https://management.azure.com/") };
const utility::string_t _wantedResource{ U("https://management.core.windows.net/") };
const int _expireLimit{ 2700 };
int _expireLimit{ 2700 };
web::json::value _tenantList;
utility::string_t _displayName;
utility::string_t _tenantID;
utility::string_t _accessToken;
utility::string_t _refreshToken;
int _expiry{ 0 };
int _expiry;
utility::string_t _cloudShellUri;
utility::string_t _terminalID;
void _WriteStringWithNewline(const std::wstring_view str);
void _WriteStringWithNewline(const winrt::hstring& str);
web::json::value _RequestHelper(web::http::client::http_client theClient, web::http::http_request theRequest);
web::json::value _GetDeviceCode();
web::json::value _WaitForUser(utility::string_t deviceCode, int pollInterval, int expiresIn);
@@ -81,18 +90,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void _StoreCredential();
void _RemoveCredentials();
enum class InputMode
{
None = 0,
Line
};
InputMode _currentInputMode{ InputMode::None };
std::wstring _userInput;
std::condition_variable _inputEvent;
std::mutex _inputMutex;
std::optional<std::wstring> _ReadUserInput(InputMode mode);
web::websockets::client::websocket_client _cloudShellSocket;
};
}

View File

@@ -123,14 +123,14 @@
<data name="AzureEnterTenant" xml:space="preserve">
<value>Please enter the desired tenant number.</value>
</data>
<data name="AzureNewLogin" xml:space="default">
<value>Enter %s to login with a different account</value>
<data name="AzureNewLogin" xml:space="preserve">
<value>Enter "n" to login with a different account.</value>
</data>
<data name="AzureRemoveStored" xml:space="default">
<value>Enter %s to remove the above saved connection settings.</value>
<data name="AzureRemoveStored" xml:space="preserve">
<value>Enter "r" to remove the above saved connection settings.</value>
</data>
<data name="AzureInvalidAccessInput" xml:space="default">
<value>Please enter a valid number to access the stored connection settings, %s to make a new one, or %s to remove the stored ones.</value>
<data name="AzureInvalidAccessInput" xml:space="preserve">
<value>Please enter a valid number to access the stored connection settings, n to make a new one, or r to remove the stored ones.</value>
</data>
<data name="AzureNonNumberError" xml:space="preserve">
<value>Please enter a number.</value>
@@ -144,11 +144,11 @@
<data name="AzureNoCloudAccount" xml:space="preserve">
<value>You have not set up your cloud shell account yet. Please go to https://shell.azure.com to set it up.</value>
</data>
<data name="AzureStorePrompt" xml:space="default">
<value>Do you want to save these connection settings for future logins? [%s/%s]</value>
<data name="AzureStorePrompt" xml:space="preserve">
<value>Do you want to save these connection settings for future logins? [y/n]</value>
</data>
<data name="AzureInvalidStoreInput" xml:space="default">
<value>Please enter %s or %s</value>
<data name="AzureInvalidStoreInput" xml:space="preserve">
<value>Please enter y or n</value>
</data>
<data name="AzureRequestingCloud" xml:space="preserve">
<value>Requesting a cloud shell instance...</value>
@@ -183,8 +183,8 @@
<data name="AzureUnknownTenantName" xml:space="preserve">
<value>&lt;unknown tenant name&gt;</value>
</data>
<data name="AzureIthTenant" xml:space="default">
<value>Tenant %s: %s (%s)</value>
<data name="AzureIthTenant" xml:space="preserve">
<value>Tenant %d: %s (%s)</value>
</data>
<data name="AzureSuccessfullyAuthenticated" xml:space="preserve">
<value>Authenticated.</value>
@@ -217,4 +217,4 @@ If this resource spans multiple lines, it will not be displayed properly. Yeah.<
<data name="TelnetInternetOrServerIssue" xml:space="preserve">
<value>Could not connect to telnet server.</value>
</data>
</root>
</root>

View File

@@ -59,7 +59,7 @@
<Midl Include="TelnetConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/Resources.language-en.resw" />
<PRIResource Include="Resources/en-US/Resources.resw" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
@@ -95,4 +95,4 @@
</Link>
</ItemDefinitionGroup>
<Import Project="..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets" Condition="Exists('..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets')" />
</Project>
</Project>

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -117,51 +117,27 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SearchBox_CaseSensitivity.ToolTipService.ToolTip" xml:space="preserve">
<data name="CaseSensitivityButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Match Case</value>
<comment>The tooltip text for the case sensitivity button on the search box control.</comment>
<comment>The tooltip text for CaseSensitivityButton</comment>
</data>
<data name="SearchBox_Close.ToolTipService.ToolTip" xml:space="preserve">
<data name="CloseButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Close</value>
<comment>The tooltip text for the close button on the search box control.</comment>
<comment>The tooltip text for CloseButton</comment>
</data>
<data name="SearchBox_SearchBackwards.ToolTipService.ToolTip" xml:space="preserve">
<data name="GoBackwardButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Find Up</value>
<comment>The tooltip text for the search backward button.</comment>
<comment>The tooltip text for GoBackward Button</comment>
</data>
<data name="SearchBox_SearchForwards.ToolTipService.ToolTip" xml:space="preserve">
<data name="GoForwardButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Find Down</value>
<comment>The tooltip text for the search forward button.</comment>
<comment>The tooltip text for GoForward Button</comment>
</data>
<data name="SearchBox_TextBox.PlaceholderText" xml:space="preserve">
<data name="TextBoxLocalizedText.PlaceholderText" xml:space="preserve">
<value>Find...</value>
<comment>The placeholder text in the search box control.</comment>
<comment>The placeholder text in the search dialog TextBox</comment>
</data>
<data name="DragFileCaption" xml:space="preserve">
<value>Copy path to file</value>
<comment>The displayed caption for dragging a file onto a terminal.</comment>
</data>
<data name="SearchBox_CaseSensitivity.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Case Sensitivity</value>
<comment>The name of the case sensitivity button on the search box control for accessibility.</comment>
</data>
<data name="SearchBox_SearchForwards.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Forward</value>
<comment>The name of the search forward button for accessibility.</comment>
</data>
<data name="SearchBox_SearchBackwards.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Backward</value>
<comment>The name of the search backward button for accessibility.</comment>
</data>
<data name="SearchBox_TextBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Text</value>
<comment>The name of the text box on the search box control for accessibility.</comment>
</data>
<data name="TerminalControl_ControlType" xml:space="preserve">
<value>terminal</value>
<comment>The type of control that the terminal ahderes to. Used to identify how a user can interact with this kind of control.</comment>
</data>
<data name="SearchBox_Close.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Close Search Box</value>
</data>
</root>
</root>

View File

@@ -153,44 +153,29 @@
</UserControl.Resources>
<StackPanel Orientation="Horizontal" Style="{ThemeResource SearchBoxBackground}" Padding="5" CornerRadius="0,0,2,2">
<TextBox x:Name="TextBox"
x:Uid="SearchBox_TextBox"
CornerRadius="2"
Width="160"
PlaceholderForeground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
FontSize="15"
KeyDown="TextBoxKeyDown"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBox x:Name="TextBox" x:Uid="TextBoxLocalizedText" AutomationProperties.Name="Search Text" CornerRadius="2" Width="160"
PlaceholderForeground="{ThemeResource TextBoxPlaceholderTextThemeBrush}" FontSize="15" KeyDown = "TextBoxKeyDown"
Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center">
</TextBox>
<ToggleButton x:Name="GoBackwardButton"
x:Uid="SearchBox_SearchBackwards"
HorizontalAlignment="Right"
Style="{StaticResource ToggleButtonStyle}"
Click="GoBackwardClicked"
IsChecked="True">
<ToggleButton x:Name="GoBackwardButton" x:Uid="GoBackwardButtonLocalizedText" AutomationProperties.Name="Set Search Backward"
HorizontalAlignment="Right" Style="{StaticResource ToggleButtonStyle}"
Click="GoBackwardClicked" IsChecked="True">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE74A;" Style="{ThemeResource FontIconStyle}"/>
</ToggleButton>
<ToggleButton x:Name="GoForwardButton"
x:Uid="SearchBox_SearchForwards"
Style="{StaticResource ToggleButtonStyle}"
<ToggleButton x:Name="GoForwardButton" x:Uid="GoForwardButtonLocalizedText"
AutomationProperties.Name="Set Search Forward" Style="{StaticResource ToggleButtonStyle}"
Click="GoForwardClicked">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE74B;" Style="{ThemeResource FontIconStyle}"/>
</ToggleButton>
<ToggleButton x:Name="CaseSensitivityButton"
x:Uid="SearchBox_CaseSensitivity"
Style="{StaticResource ToggleButtonStyle}">
<ToggleButton x:Name="CaseSensitivityButton" x:Uid="CaseSensitivityButtonLocalizedText"
AutomationProperties.Name="CaseSensitivity" Style="{StaticResource ToggleButtonStyle}">
<PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z"/>
</ToggleButton>
<Button x:Name="CloseButton"
x:Uid="SearchBox_Close"
Padding="0"
Click="CloseClick"
Style="{ThemeResource ButtonStyle}">
<Button x:Name="CloseButton" x:Uid="CloseButtonLocalizedText" AutomationProperties.Name="Close"
Padding="0" Click="CloseClick" Style="{ ThemeResource ButtonStyle}">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE711;" FontSize="12"/>
</Button>
</StackPanel>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
@@ -17,11 +17,35 @@ using namespace winrt::Windows::UI::Xaml;
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
TSFInputControl::TSFInputControl() :
_editContext{ nullptr },
_inComposition{ false },
_activeTextStart{ 0 }
_editContext{ nullptr }
{
InitializeComponent();
_Create();
}
// Method Description:
// - Creates XAML controls for displaying user input and hooks up CoreTextEditContext handlers
// for handling text input from the Text Services Framework.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TSFInputControl::_Create()
{
// TextBlock for user input form TSF
_textBlock = Controls::TextBlock();
_textBlock.Visibility(Visibility::Collapsed);
_textBlock.IsTextSelectionEnabled(false);
_textBlock.TextDecorations(TextDecorations::Underline);
// Canvas for controlling exact position of the TextBlock
_canvas = Windows::UI::Xaml::Controls::Canvas();
_canvas.Visibility(Visibility::Collapsed);
// add the Textblock to the Canvas
_canvas.Children().Append(_textBlock);
// set the content of this control to be the Canvas
this->Content(_canvas);
// Create a CoreTextEditingContext for since we are acting like a custom edit control
auto manager = Core::CoreTextServicesManager::GetForCurrentView();
@@ -93,28 +117,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
// Method Description:
// - Clears the input buffer and tells the text server to clear their buffer as well.
// Also clears the TextBlock and sets the active text starting point to 0.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TSFInputControl::ClearBuffer()
{
if (!_inputBuffer.empty())
{
TextBlock().Text(L"");
const auto bufLen = ::base::ClampedNumeric<int32_t>(_inputBuffer.length());
_inputBuffer.clear();
_editContext.NotifyFocusLeave();
_editContext.NotifyTextChanged({ 0, bufLen }, 0, { 0, 0 });
_editContext.NotifyFocusEnter();
_activeTextStart = 0;
_inComposition = false;
}
}
// Method Description:
// - Handler for LayoutRequested event by CoreEditContext responsible
// for returning the current position the IME should be placed
@@ -136,31 +138,32 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Get the cursor position in text buffer position
auto cursorArgs = winrt::make_self<CursorPositionEventArgs>();
_CurrentCursorPositionHandlers(*this, *cursorArgs);
const COORD cursorPos = { ::base::ClampedNumeric<short>(cursorArgs->CurrentPosition().X), ::base::ClampedNumeric<short>(cursorArgs->CurrentPosition().Y) };
const COORD cursorPos = { gsl::narrow_cast<SHORT>(cursorArgs->CurrentPosition().X), gsl::narrow_cast<SHORT>(cursorArgs->CurrentPosition().Y) };
// Get Font Info as we use this is the pixel size for characters in the display
auto fontArgs = winrt::make_self<FontInfoEventArgs>();
_CurrentFontInfoHandlers(*this, *fontArgs);
const auto fontWidth = fontArgs->FontSize().Width;
const auto fontHeight = fontArgs->FontSize().Height;
const float fontWidth = fontArgs->FontSize().Width;
const float fontHeight = fontArgs->FontSize().Height;
// Convert text buffer cursor position to client coordinate position within the window
COORD clientCursorPos;
clientCursorPos.X = ::base::ClampMul(cursorPos.X, ::base::ClampedNumeric<short>(fontWidth));
clientCursorPos.Y = ::base::ClampMul(cursorPos.Y, ::base::ClampedNumeric<short>(fontHeight));
COORD screenCursorPos;
THROW_IF_FAILED(ShortMult(cursorPos.X, gsl::narrow<SHORT>(fontWidth), &clientCursorPos.X));
THROW_IF_FAILED(ShortMult(cursorPos.Y, gsl::narrow<SHORT>(fontHeight), &clientCursorPos.Y));
// Convert from client coordinate to screen coordinate by adding window position
COORD screenCursorPos;
screenCursorPos.X = ::base::ClampAdd(clientCursorPos.X, ::base::ClampedNumeric<short>(windowBounds.X));
screenCursorPos.Y = ::base::ClampAdd(clientCursorPos.Y, ::base::ClampedNumeric<short>(windowBounds.Y));
THROW_IF_FAILED(ShortAdd(clientCursorPos.X, gsl::narrow_cast<SHORT>(windowBounds.X), &screenCursorPos.X));
THROW_IF_FAILED(ShortAdd(clientCursorPos.Y, gsl::narrow_cast<SHORT>(windowBounds.Y), &screenCursorPos.Y));
// get any offset (margin + tabs, etc..) of the control within the window
const auto offsetPoint = this->TransformToVisual(nullptr).TransformPoint(winrt::Windows::Foundation::Point(0, 0));
// add the margin offsets if any
screenCursorPos.X = ::base::ClampAdd(screenCursorPos.X, ::base::ClampedNumeric<short>(offsetPoint.X));
screenCursorPos.Y = ::base::ClampAdd(screenCursorPos.Y, ::base::ClampedNumeric<short>(offsetPoint.Y));
const auto currentMargin = this->Margin();
THROW_IF_FAILED(ShortAdd(screenCursorPos.X, gsl::narrow_cast<SHORT>(offsetPoint.X), &screenCursorPos.X));
THROW_IF_FAILED(ShortAdd(screenCursorPos.Y, gsl::narrow_cast<SHORT>(offsetPoint.Y), &screenCursorPos.Y));
// Get scale factor for view
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
@@ -174,15 +177,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
request.LayoutBounds().ControlBounds(ScaleRect(controlRect, scaleFactor));
// position textblock to cursor position
Canvas().SetLeft(TextBlock(), clientCursorPos.X);
Canvas().SetTop(TextBlock(), ::base::ClampedNumeric<double>(clientCursorPos.Y));
_canvas.SetLeft(_textBlock, clientCursorPos.X);
_canvas.SetTop(_textBlock, static_cast<double>(clientCursorPos.Y));
// width is cursor to end of canvas
_textBlock.Width(200); // TODO GitHub #3640: Determine proper Width
_textBlock.Height(fontHeight);
TextBlock().Height(fontHeight);
// calculate FontSize in pixels from DIPs
const double fontSizePx = (fontHeight * 72) / USER_DEFAULT_SCREEN_DPI;
TextBlock().FontSize(fontSizePx);
_textBlock.FontSize(fontSizePx);
TextBlock().FontFamily(Media::FontFamily(fontArgs->FontFace()));
_textBlock.FontFamily(Media::FontFamily(fontArgs->FontFace()));
}
// Method Description:
@@ -195,7 +201,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TSFInputControl::_compositionStartedHandler(CoreTextEditContext sender, CoreTextCompositionStartedEventArgs const& /*args*/)
{
_inComposition = true;
_canvas.Visibility(Visibility::Visible);
_textBlock.Visibility(Visibility::Visible);
}
// Method Description:
@@ -208,19 +215,30 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TSFInputControl::_compositionCompletedHandler(CoreTextEditContext sender, CoreTextCompositionCompletedEventArgs const& /*args*/)
{
_inComposition = false;
// only need to do work if the current buffer has text
if (!_inputBuffer.empty())
{
_SendAndClearText();
// call event handler with data handled by parent
_compositionCompletedHandlers(_inputBuffer);
// clear the buffer for next round
const auto bufferLength = gsl::narrow_cast<int32_t>(_inputBuffer.length());
_inputBuffer.clear();
_textBlock.Text(L"");
// indicate text is now 0
_editContext.NotifyTextChanged({ 0, bufferLength }, 0, { 0, 0 });
// hide the controls until composition starts again
_canvas.Visibility(Visibility::Collapsed);
_textBlock.Visibility(Visibility::Collapsed);
}
}
// Method Description:
// - Handler for FocusRemoved event by CoreEditContext responsible
// for removing focus for the TSFInputControl control accordingly
// when focus was forcibly removed from text input control.
// when focus was forcibly removed from text input control. (TODO GitHub #3644)
// NOTE: Documentation says application should handle this event
// Arguments:
// - sender: CoreTextEditContext sending the request. Not used in method.
@@ -247,9 +265,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
try
{
const auto textEnd = ::base::ClampMin<size_t>(range.EndCaretPosition, _inputBuffer.length());
const auto length = ::base::ClampSub<size_t>(textEnd, range.StartCaretPosition);
const auto textRequested = _inputBuffer.substr(range.StartCaretPosition, length);
const auto textRequested = _inputBuffer.substr(range.StartCaretPosition, static_cast<size_t>(range.EndCaretPosition) - static_cast<size_t>(range.StartCaretPosition));
args.Request().Text(textRequested);
}
@@ -294,28 +310,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TSFInputControl::_textUpdatingHandler(CoreTextEditContext sender, CoreTextTextUpdatingEventArgs const& args)
{
const auto incomingText = args.Text();
const auto text = args.Text();
const auto range = args.Range();
try
{
_inputBuffer = _inputBuffer.replace(
range.StartCaretPosition,
::base::ClampSub<size_t>(range.EndCaretPosition, range.StartCaretPosition),
incomingText);
static_cast<size_t>(range.EndCaretPosition) - static_cast<size_t>(range.StartCaretPosition),
text);
// Emojis/Kaomojis/Symbols chosen through the IME without starting composition
// will be sent straight through to the terminal.
if (!_inComposition)
{
_SendAndClearText();
}
else
{
Canvas().Visibility(Visibility::Visible);
const auto text = _inputBuffer.substr(_activeTextStart);
TextBlock().Text(text);
}
_textBlock.Text(_inputBuffer);
// Notify the TSF that the update succeeded
args.Result(CoreTextTextUpdatingResult::Succeeded);
@@ -329,27 +334,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
// Method Description:
// - Send the portion of the textBuffer starting at _activeTextStart to the end of the buffer.
// Then clear the TextBlock and hide it until the next time text is received.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TSFInputControl::_SendAndClearText()
{
const auto text = _inputBuffer.substr(_activeTextStart);
_compositionCompletedHandlers(text);
_activeTextStart = _inputBuffer.length();
TextBlock().Text(L"");
// hide the controls until text input starts again
Canvas().Visibility(Visibility::Collapsed);
}
// Method Description:
// - Handler for FormatUpdating event by CoreEditContext responsible
// for handling different format updates for a particular range of text.

View File

@@ -36,10 +36,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void NotifyFocusEnter();
void NotifyFocusLeave();
void ClearBuffer();
void Close();
static void OnCompositionChanged(Windows::UI::Xaml::DependencyObject const&, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const&);
// -------------------------------- WinRT Events ---------------------------------
TYPED_EVENT(CurrentCursorPosition, TerminalControl::TSFInputControl, TerminalControl::CursorPositionEventArgs);
TYPED_EVENT(CurrentFontInfo, TerminalControl::TSFInputControl, TerminalControl::FontInfoEventArgs);
@@ -66,13 +67,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionStarted_revoker _compositionStartedRevoker;
winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionCompleted_revoker _compositionCompletedRevoker;
Windows::UI::Xaml::Controls::Canvas _canvas;
Windows::UI::Xaml::Controls::TextBlock _textBlock;
Windows::UI::Text::Core::CoreTextEditContext _editContext;
std::wstring _inputBuffer;
bool _inComposition;
size_t _activeTextStart;
void _SendAndClearText();
void _Create();
};
}
namespace winrt::Microsoft::Terminal::TerminalControl::factory_implementation

View File

@@ -27,7 +27,6 @@ namespace Microsoft.Terminal.TerminalControl
void NotifyFocusEnter();
void NotifyFocusLeave();
void ClearBuffer();
void Close();
}

View File

@@ -1,17 +0,0 @@
<UserControl
x:Class="Microsoft.Terminal.TerminalControl.TSFInputControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="768"
d:DesignWidth="1024">
<Canvas x:Name="Canvas"
Visibility="Collapsed">
<TextBlock x:Name="TextBlock"
IsTextSelectionEnabled="false"
TextDecorations="Underline" />
</Canvas>
</UserControl>

View File

@@ -18,7 +18,6 @@
using namespace ::Microsoft::Console::Types;
using namespace ::Microsoft::Terminal::Core;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Input;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
@@ -54,6 +53,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
TermControl::TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
_connection{ connection },
_initializedTerminal{ false },
_root{ nullptr },
_swapChainPanel{ nullptr },
_settings{ settings },
_closing{ false },
_isTerminalInitiatedScroll{ false },
@@ -68,15 +69,54 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_lastMouseClick{},
_lastMouseClickPos{},
_searchBox{ nullptr },
_unfocusedClickPos{ std::nullopt },
_isClickDragSelection{ false }
_tsfInputControl{ nullptr }
{
_EnsureStaticInitialization();
InitializeComponent();
_Create();
}
void TermControl::_Create()
{
Controls::Grid container;
Controls::ColumnDefinition contentColumn{};
Controls::ColumnDefinition scrollbarColumn{};
contentColumn.Width(GridLength{ 1.0, GridUnitType::Star });
scrollbarColumn.Width(GridLength{ 1.0, GridUnitType::Auto });
container.ColumnDefinitions().Append(contentColumn);
container.ColumnDefinitions().Append(scrollbarColumn);
_scrollBar = Controls::Primitives::ScrollBar{};
_scrollBar.Orientation(Controls::Orientation::Vertical);
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
_scrollBar.HorizontalAlignment(HorizontalAlignment::Right);
_scrollBar.VerticalAlignment(VerticalAlignment::Stretch);
// Initialize the scrollbar with some placeholder values.
// The scrollbar will be updated with real values on _Initialize
_scrollBar.Maximum(1);
_scrollBar.ViewportSize(10);
_scrollBar.IsTabStop(false);
_scrollBar.SmallChange(1);
_scrollBar.LargeChange(4);
_scrollBar.Visibility(Visibility::Visible);
_tsfInputControl = TSFInputControl();
_tsfInputControl.CompositionCompleted({ this, &TermControl::_CompositionCompleted });
_tsfInputControl.CurrentCursorPosition({ this, &TermControl::_CurrentCursorPositionHandler });
_tsfInputControl.CurrentFontInfo({ this, &TermControl::_FontInfoHandler });
container.Children().Append(_tsfInputControl);
// Create the SwapChainPanel that will display our content
Controls::SwapChainPanel swapChainPanel;
_sizeChangedRevoker = swapChainPanel.SizeChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainSizeChanged });
_compositionScaleChangedRevoker = swapChainPanel.CompositionScaleChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainScaleChanged });
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
_layoutUpdatedRevoker = swapChainPanel.LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
// This event fires every time the layout changes, but it is always the last one to fire
// in any layout change chain. That gives us great flexibility in finding the right point
// at which to initialize our renderer (and our terminal).
@@ -89,29 +129,74 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
});
container.Children().Append(swapChainPanel);
container.Children().Append(_scrollBar);
Controls::Grid::SetColumn(swapChainPanel, 0);
Controls::Grid::SetColumn(_scrollBar, 1);
Controls::Grid root{};
Controls::Image bgImageLayer{};
root.Children().Append(bgImageLayer);
root.Children().Append(container);
_root = root;
_bgImageLayer = bgImageLayer;
_swapChainPanel = swapChainPanel;
this->Content(_root);
_ApplyUISettings();
// These are important:
// 1. When we get tapped, focus us
_tappedRevoker = this->Tapped(winrt::auto_revoke, [this](auto&, auto& e) {
Focus(FocusState::Pointer);
e.Handled(true);
});
// 2. Make sure we can be focused (why this isn't `Focusable` I'll never know)
this->IsTabStop(true);
// 3. Actually not sure about this one. Maybe it isn't necessary either.
this->AllowFocusOnInteraction(true);
// DON'T CALL _InitializeTerminal here - wait until the swap chain is loaded to do that.
// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
_ConnectionStateChangedHandlers(*this, nullptr);
});
_ApplyUISettings();
_root.AllowDrop(true);
_root.Drop({ get_weak(), &TermControl::_DragDropHandler });
_root.DragOver({ get_weak(), &TermControl::_DragOverHandler });
}
// Method Description:
// - Loads the search box from the xaml UI and focuses it.
// - Create the SearchBoxControl object, and attach it
// to the Terminal Control root
// Arguments:
// - <none>
// Return Value:
// - <none>
void TermControl::CreateSearchBoxControl()
{
// Lazy load the search box control.
if (auto loadedSearchBox{ FindName(L"SearchBox") })
if (!_searchBox)
{
if (auto searchBox{ loadedSearchBox.try_as<::winrt::Microsoft::Terminal::TerminalControl::SearchBoxControl>() })
{
// get at its private implementation
_searchBox.copy_from(winrt::get_self<implementation::SearchBoxControl>(searchBox));
_searchBox->Visibility(Visibility::Visible);
_searchBox->SetFocusOnTextbox();
}
_searchBox = winrt::make_self<SearchBoxControl>();
_searchBox->HorizontalAlignment(HorizontalAlignment::Right);
_searchBox->VerticalAlignment(VerticalAlignment::Top);
// We need to make sure the searchbox does not overlap
// with the scroll bar
Thickness searchBoxPadding = { 0, 0, _scrollBar.ActualWidth(), 0 };
_searchBox->Margin(searchBoxPadding);
_root.Children().Append(*_searchBox);
// Event handlers
_searchBox->Search({ get_weak(), &TermControl::_Search });
_searchBox->Closed({ get_weak(), &TermControl::_CloseSearchBoxControl });
}
_searchBox->SetFocusOnTextbox();
}
// Method Description:
@@ -142,7 +227,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto lock = _terminal->LockForWriting();
if (search.FindNext())
{
_terminal->SetBlockSelection(false);
_terminal->SetBoxSelection(false);
search.Select();
_renderer->TriggerSelection();
}
@@ -151,6 +236,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Method Description:
// - The handler for the close button or pressing "Esc" when focusing on the
// search dialog.
// This removes the SearchBoxControl object from the XAML tree,
// reset smart pointer and set focus back to Terminal
// Arguments:
// - IInspectable: not used
// - RoutedEventArgs: not used
@@ -158,7 +245,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
{
_searchBox->Visibility(Visibility::Collapsed);
unsigned int idx;
_root.Children().IndexOf(*_searchBox, idx);
_root.Children().RemoveAt(idx);
_searchBox = nullptr;
// Set focus back to terminal control
this->Focus(FocusState::Programmatic);
@@ -177,7 +268,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Dispatch a call to the UI thread to apply the new settings to the
// terminal.
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_root.Dispatcher());
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
@@ -194,8 +285,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Refresh our font with the renderer
_UpdateFont();
const auto width = SwapChainPanel().ActualWidth();
const auto height = SwapChainPanel().ActualHeight();
const auto width = _swapChainPanel.ActualWidth();
const auto height = _swapChainPanel.ActualHeight();
if (width != 0 && height != 0)
{
// If the font size changed, or the _swapchainPanel's size changed
@@ -204,6 +295,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto lock = _terminal->LockForWriting();
_DoResize(width, height);
}
// set TSF Foreground
Media::SolidColorBrush foregroundBrush{};
foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
_tsfInputControl.Foreground(foregroundBrush);
}
}
@@ -228,11 +324,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Apply padding as swapChainPanel's margin
auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
SwapChainPanel().Margin(newMargin);
auto existingMargin = _swapChainPanel.Margin();
_swapChainPanel.Margin(newMargin);
// Initialize our font information.
const auto fontFace = _settings.FontFace();
const short fontHeight = gsl::narrow_cast<short>(_settings.FontSize());
const short fontHeight = gsl::narrow<short>(_settings.FontSize());
// 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.
@@ -245,24 +342,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// set TSF Foreground
Media::SolidColorBrush foregroundBrush{};
foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
TSFInputControl().Foreground(foregroundBrush);
TSFInputControl().Margin(newMargin);
// Apply settings for scrollbar
if (_settings.ScrollState() == ScrollbarState::Hidden)
{
// In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
// achieve the intended effect.
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
ScrollBar().Visibility(Visibility::Collapsed);
}
else // (default or Visible)
{
// Default behavior
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
ScrollBar().Visibility(Visibility::Visible);
}
_tsfInputControl.Foreground(foregroundBrush);
_tsfInputControl.Margin(newMargin);
// set number of rows to scroll at a time
_rowsToScroll = _settings.RowsToScroll();
@@ -288,7 +369,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// See if we've already got an acrylic background brush
// to avoid the flicker when setting up a new one
auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>();
auto acrylic = _root.Background().try_as<Media::AcrylicBrush>();
// Instantiate a brush if there's not already one there
if (acrylic == nullptr)
@@ -313,15 +394,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
acrylic.TintOpacity(_settings.TintOpacity());
// Apply brush to control if it's not already there
if (RootGrid().Background() != acrylic)
if (_root.Background() != acrylic)
{
RootGrid().Background(acrylic);
_root.Background(acrylic);
}
}
else
{
Media::SolidColorBrush solidColor{};
RootGrid().Background(solidColor);
_root.Background(solidColor);
}
if (!_settings.BackgroundImage().empty())
@@ -331,7 +412,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Check if the image brush is already pointing to the image
// in the modified settings; if it isn't (or isn't there),
// set a new image source for the brush
auto imageSource = BackgroundImage().Source().try_as<Media::Imaging::BitmapImage>();
auto imageSource = _bgImageLayer.Source().try_as<Media::Imaging::BitmapImage>();
if (imageSource == nullptr ||
imageSource.UriSource() == nullptr ||
@@ -342,18 +423,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// may well be both large and somewhere out on the
// internet.
Media::Imaging::BitmapImage image(imageUri);
BackgroundImage().Source(image);
_bgImageLayer.Source(image);
}
// Apply stretch, opacity and alignment settings
BackgroundImage().Stretch(_settings.BackgroundImageStretchMode());
BackgroundImage().Opacity(_settings.BackgroundImageOpacity());
BackgroundImage().HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
BackgroundImage().VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
_bgImageLayer.Stretch(_settings.BackgroundImageStretchMode());
_bgImageLayer.Opacity(_settings.BackgroundImageOpacity());
_bgImageLayer.HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
_bgImageLayer.VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
}
else
{
BackgroundImage().Source(nullptr);
_bgImageLayer.Source(nullptr);
}
}
@@ -367,7 +448,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_root.Dispatcher());
if (auto control{ weakThis.get() })
{
@@ -381,12 +462,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
bgColor.B = B;
bgColor.A = 255;
if (auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>())
if (auto acrylic = _root.Background().try_as<Media::AcrylicBrush>())
{
acrylic.FallbackColor(bgColor);
acrylic.TintColor(bgColor);
}
else if (auto solidColor = RootGrid().Background().try_as<Media::SolidColorBrush>())
else if (auto solidColor = _root.Background().try_as<Media::SolidColorBrush>())
{
solidColor.Color(bgColor);
}
@@ -439,9 +520,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return _actualFont;
}
const Windows::UI::Xaml::Thickness TermControl::GetPadding()
const Windows::UI::Xaml::Thickness TermControl::GetPadding() const
{
return SwapChainPanel().Margin();
return _swapChainPanel.Margin();
}
TerminalConnection::ConnectionState TermControl::ConnectionState() const
@@ -459,17 +540,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto chain = _renderEngine->GetSwapChain();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
{
if (_terminal)
{
auto lock = _terminal->LockForWriting();
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
}
auto lock = _terminal->LockForWriting();
auto nativePanel = _swapChainPanel.as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
}
}
@@ -478,16 +556,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto chain = _renderEngine->GetSwapChain();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
if (auto control{ weakThis.get() })
{
if (_terminal)
{
auto lock = _terminal->LockForWriting();
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
}
_terminal->LockConsole();
auto nativePanel = _swapChainPanel.as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
_terminal->UnlockConsole();
}
}
@@ -498,8 +574,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return false;
}
const auto windowWidth = SwapChainPanel().ActualWidth(); // Width() and Height() are NaN?
const auto windowHeight = SwapChainPanel().ActualHeight();
const auto windowWidth = _swapChainPanel.ActualWidth(); // Width() and Height() are NaN?
const auto windowHeight = _swapChainPanel.ActualHeight();
if (windowWidth == 0 || windowHeight == 0)
{
@@ -560,22 +636,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// here, the setting will only be used when the Terminal is initialized.
dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
// TODO:GH#3927 - hot-reload this one too
// Update DxEngine's AntialiasingMode
switch (_settings.AntialiasingMode())
{
case TextAntialiasingMode::Cleartype:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
break;
case TextAntialiasingMode::Aliased:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break;
case TextAntialiasingMode::Grayscale:
default:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
break;
}
THROW_IF_FAILED(dxEngine->Enable());
_renderEngine = std::move(dxEngine);
@@ -594,13 +654,62 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto bottom = _terminal->GetViewport().BottomExclusive();
auto bufferHeight = bottom;
ScrollBar().Maximum(bufferHeight - bufferHeight);
ScrollBar().Minimum(0);
ScrollBar().Value(0);
ScrollBar().ViewportSize(bufferHeight);
const auto originalMaximum = _scrollBar.Maximum();
const auto originalMinimum = _scrollBar.Minimum();
const auto originalValue = _scrollBar.Value();
const auto originalViewportSize = _scrollBar.ViewportSize();
_scrollBar.Maximum(bufferHeight - bufferHeight);
_scrollBar.Minimum(0);
_scrollBar.Value(0);
_scrollBar.ViewportSize(bufferHeight);
_scrollBar.ValueChanged({ this, &TermControl::_ScrollbarChangeHandler });
_scrollBar.PointerPressed({ this, &TermControl::_CapturePointer });
_scrollBar.PointerReleased({ this, &TermControl::_ReleasePointerCapture });
// Apply settings for scrollbar
if (_settings.ScrollState() == ScrollbarState::Visible)
{
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
}
else if (_settings.ScrollState() == ScrollbarState::Hidden)
{
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
// In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
// achieve the intended effect.
_scrollBar.Visibility(Visibility::Collapsed);
}
else
{
// Default behavior
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
}
_root.PointerWheelChanged({ this, &TermControl::_MouseWheelHandler });
// These need to be hooked up to the SwapChainPanel because we don't want the scrollbar to respond to pointer events (GitHub #950)
_swapChainPanel.PointerPressed({ this, &TermControl::_PointerPressedHandler });
_swapChainPanel.PointerMoved({ this, &TermControl::_PointerMovedHandler });
_swapChainPanel.PointerReleased({ this, &TermControl::_PointerReleasedHandler });
localPointerToThread->EnablePainting();
// No matter what order these guys are in, The KeyDown's will fire
// before the CharacterReceived, so we can't easily get characters
// first, then fallback to getting keys from vkeys.
// TODO: This apparently handles keys and characters correctly, though
// I'd keep an eye on it, and test more.
// I presume that the characters that aren't translated by terminalInput
// just end up getting ignored, and the rest of the input comes
// through CharacterReceived.
// I don't believe there's a difference between KeyDown and
// PreviewKeyDown for our purposes
// These two handlers _must_ be on this, not _root.
this->PreviewKeyDown({ this, &TermControl::_KeyDownHandler });
this->CharacterReceived({ this, &TermControl::_CharacterHandler });
auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
_terminal->SetTitleChangedCallback(pfnTitleChanged);
@@ -633,7 +742,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// import value from WinUser (convert from milli-seconds to micro-seconds)
_multiClickTimer = GetDoubleClickTime() * 1000;
// Focus the control here. If we do it during control initialization, then
_gotFocusRevoker = this->GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler });
_lostFocusRevoker = this->LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler });
// Focus the control here. If we do it up above (in _Create_), then the
// focus won't actually get passed to us. I believe this is because
// we're not technically a part of the UI tree yet, so focusing us
// becomes a no-op.
@@ -659,38 +771,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
e.Handled(handled);
}
// Method Description:
// - Manually generate an F7 event into the key bindings or terminal.
// This is required as part of GH#638.
// Return value:
// - Whether F7 was handled.
bool TermControl::OnF7Pressed()
{
bool handled{ false };
auto bindings{ _settings.KeyBindings() };
const auto modifiers{ _GetPressedModifierKeys() };
if (bindings)
{
handled = bindings.TryKeyChord({
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
VK_F7,
});
}
if (!handled)
{
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
(void)_TrySendKeyEvent(VK_F7, 0, modifiers);
handled = true;
}
return handled;
}
void TermControl::_KeyDownHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
Input::KeyRoutedEventArgs const& e)
{
@@ -770,7 +850,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (_terminal->IsSelectionActive())
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
if (vkey == VK_ESCAPE)
{
@@ -778,12 +857,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
if (vkey == VK_ESCAPE ||
vkey == VK_RETURN)
{
TSFInputControl().ClearBuffer();
}
// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event.
@@ -800,17 +873,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return handled;
}
// Method Description:
// - handle a tap event by taking focus
// Arguments:
// - sender: the XAML element responding to the tap event
// - args: event data
void TermControl::_TappedHandler(const IInspectable& /*sender*/, const TappedRoutedEventArgs& e)
{
Focus(FocusState::Pointer);
e.Handled(true);
}
// Method Description:
// - handle a mouse click event. Begin selection process.
// Arguments:
@@ -822,7 +884,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_CapturePointer(sender, args);
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto point = args.GetCurrentPoint(_root);
if (!_focused)
{
@@ -833,7 +895,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// only want to start the selection when the user moves the pointer with
// the left mouse button held down, the PointerMovedHandler will use
// this saved position to set the SelectionAnchor.
_unfocusedClickPos = point.Position();
_clickDragStartPos = point.Position();
}
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
@@ -846,11 +908,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (point.Properties().IsLeftButtonPressed())
{
// _unfocusedClickPos having a value signifies to us that
// _clickDragStartPos having a value signifies to us that
// the user clicked on an unfocused terminal. We don't want
// a single left click from out of focus to start a selection,
// so we return fast here.
if (_unfocusedClickPos)
if (_clickDragStartPos)
{
args.Handled(true);
return;
@@ -860,7 +922,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto terminalPosition = _GetTerminalPosition(cursorPosition);
// handle ALT key
_terminal->SetBlockSelection(altEnabled);
_terminal->SetBoxSelection(altEnabled);
auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp());
@@ -871,17 +933,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (multiClickMapper == 3)
{
_terminal->MultiClickSelection(terminalPosition, ::Terminal::SelectionExpansionMode::Line);
_terminal->TripleClickSelection(terminalPosition);
}
else if (multiClickMapper == 2)
{
_terminal->MultiClickSelection(terminalPosition, ::Terminal::SelectionExpansionMode::Word);
_terminal->DoubleClickSelection(terminalPosition);
}
else
{
if (shiftEnabled && _terminal->IsSelectionActive())
{
_terminal->SetSelectionEnd(terminalPosition, ::Terminal::SelectionExpansionMode::Cell);
_terminal->SetEndSelectionPosition(terminalPosition);
}
else
{
@@ -903,7 +965,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
else
{
CopySelectionToClipboard(shiftEnabled);
CopySelectionToClipboard(!shiftEnabled);
}
}
}
@@ -926,28 +988,26 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Input::PointerRoutedEventArgs const& args)
{
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto point = args.GetCurrentPoint(_root);
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
if (point.Properties().IsLeftButtonPressed())
{
_isClickDragSelection = true;
// If this does not have a value, it means that PointerPressedHandler already
// set the SelectionAnchor. If it does have a value, that means the user is
// performing a click-drag selection on an unfocused terminal, so
// a SelectionAnchor isn't set yet. We'll have to set it here.
if (_unfocusedClickPos)
if (_clickDragStartPos)
{
_terminal->SetSelectionAnchor(_GetTerminalPosition(*_unfocusedClickPos));
_terminal->SetSelectionAnchor(_GetTerminalPosition(*_clickDragStartPos));
}
const auto cursorPosition = point.Position();
_SetEndSelectionPointAtCursor(cursorPosition);
const double cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
const double cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;
const double cursorBelowBottomDist = cursorPosition.Y - _swapChainPanel.Margin().Top - _swapChainPanel.ActualHeight();
const double cursorAboveTopDist = -1 * cursorPosition.Y + _swapChainPanel.Margin().Top;
constexpr double MinAutoScrollDist = 2.0; // Arbitrary value
double newAutoScrollVelocity = 0.0;
@@ -981,8 +1041,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const float dy = newTouchPoint.Y - anchor.Y;
// Start viewport scroll after we've moved more than a half row of text
if (std::abs(dy) > (fontHeight / 2.0f))
// If we've moved more than one row of text, we'll want to scroll the viewport
if (std::abs(dy) > fontHeight)
{
// Multiply by -1, because moving the touch point down will
// create a positive delta, but we want the viewport to move up,
@@ -990,10 +1050,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// panning down)
const float numRows = -1.0f * (dy / fontHeight);
const auto currentOffset = ::base::ClampedNumeric<double>(ScrollBar().Value());
const auto newValue = numRows + currentOffset;
const auto currentOffset = this->GetScrollOffset();
const double newValue = (numRows) + (currentOffset);
ScrollBar().Value(newValue);
_scrollBar.Value(static_cast<int>(newValue));
// Use this point as our new scroll anchor.
_touchAnchor = newTouchPoint;
@@ -1015,6 +1075,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto ptr = args.Pointer();
_clickDragStartPos = std::nullopt;
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
@@ -1024,12 +1086,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (_terminal->IsCopyOnSelectActive())
{
// If the terminal was in focus, copy to clipboard.
// If the terminal was unfocused AND a click-drag selection happened, copy to clipboard.
if (!_unfocusedClickPos || (_unfocusedClickPos && _isClickDragSelection))
{
CopySelectionToClipboard(shiftEnabled);
}
CopySelectionToClipboard(!shiftEnabled);
}
}
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
@@ -1037,9 +1094,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_touchAnchor = std::nullopt;
}
_unfocusedClickPos = std::nullopt;
_isClickDragSelection = false;
_TryStopAutoScroll(ptr.PointerId());
args.Handled(true);
@@ -1054,7 +1108,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
Input::PointerRoutedEventArgs const& args)
{
const auto point = args.GetCurrentPoint(*this);
const auto point = args.GetCurrentPoint(_root);
const auto delta = point.Properties().MouseWheelDelta();
// Get the state of the Ctrl & Shift keys
@@ -1092,7 +1146,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
try
{
auto acrylicBrush = RootGrid().Background().as<Media::AcrylicBrush>();
auto acrylicBrush = _root.Background().as<Media::AcrylicBrush>();
acrylicBrush.TintOpacity(acrylicBrush.TintOpacity() + effectiveDelta);
}
CATCH_LOG();
@@ -1135,11 +1189,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - mouseDelta: the mouse wheel delta that triggered this event.
void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint)
{
const auto currentOffset = ScrollBar().Value();
const auto currentOffset = this->GetScrollOffset();
// negative = down, positive = up
// However, for us, the signs are flipped.
const auto rowDelta = mouseDelta / (-1.0 * WHEEL_DELTA);
const auto rowDelta = mouseDelta < 0 ? 1.0 : -1.0;
// With one of the precision mouses, one click is always a multiple of 120,
// but the "smooth scrolling" mode results in non-int values
@@ -1148,7 +1202,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// The scroll bar's ValueChanged handler will actually move the viewport
// for us.
ScrollBar().Value(newValue);
_scrollBar.Value(static_cast<int>(newValue));
if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed())
{
@@ -1279,7 +1333,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
static constexpr double microSecPerSec = 1000000.0;
const double deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - _lastAutoScrollUpdateTime.value()).count() / microSecPerSec;
ScrollBar().Value(ScrollBar().Value() + _autoScrollVelocity * deltaTime);
_scrollBar.Value(_scrollBar.Value() + _autoScrollVelocity * deltaTime);
if (_autoScrollingPointerPoint.has_value())
{
@@ -1303,25 +1357,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
return;
}
_focused = true;
// If the searchbox is focused, we don't want TSFInputControl to think
// it has focus so it doesn't intercept IME input. We also don't want the
// terminal's cursor to start blinking. So, we'll just return quickly here.
if (_searchBox && _searchBox->ContainsFocus())
{
return;
}
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Enable());
}
if (TSFInputControl() != nullptr)
if (_tsfInputControl != nullptr)
{
TSFInputControl().NotifyFocusEnter();
_tsfInputControl.NotifyFocusEnter();
}
if (_cursorTimer.has_value())
@@ -1351,9 +1396,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
THROW_IF_FAILED(_uiaEngine->Disable());
}
if (TSFInputControl() != nullptr)
if (_tsfInputControl != nullptr)
{
TSFInputControl().NotifyFocusLeave();
_tsfInputControl.NotifyFocusLeave();
}
if (_cursorTimer.has_value())
@@ -1417,7 +1462,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
auto lock = _terminal->LockForWriting();
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * SwapChainPanel().CompositionScaleX());
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * _swapChainPanel.CompositionScaleX());
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
// actually fail. We need a way to gracefully fallback.
@@ -1436,7 +1481,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
try
{
// Make sure we have a non-zero font size
const auto newSize = std::max<short>(gsl::narrow_cast<short>(fontSize), 1);
const auto newSize = std::max(gsl::narrow<short>(fontSize), static_cast<short>(1));
const auto fontFace = _settings.FontFace();
_actualFont = { fontFace, 0, 10, { 0, newSize }, CP_UTF8, false };
_desiredFont = { _actualFont };
@@ -1448,7 +1493,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// problems (like what happens when you change the font size while the
// window is maximized?)
auto lock = _terminal->LockForWriting();
_DoResize(SwapChainPanel().ActualWidth(), SwapChainPanel().ActualHeight());
_DoResize(_swapChainPanel.ActualWidth(), _swapChainPanel.ActualHeight());
}
CATCH_LOG();
}
@@ -1517,7 +1562,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
terminalPosition.X = std::clamp<short>(terminalPosition.X, 0, lastVisibleCol);
// save location (for rendering) + render
_terminal->SetSelectionEnd(terminalPosition);
_terminal->SetEndSelectionPosition(terminalPosition);
_renderer->TriggerSelection();
}
@@ -1542,8 +1587,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return;
}
_terminal->ClearSelection();
// Tell the dx engine that our window is now the new size.
THROW_IF_FAILED(_renderEngine->SetWindowSize(size));
@@ -1627,7 +1670,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_scrollBar.Dispatcher());
// Even if we weren't closed/closing few lines above, we might be
// while waiting for this block of code to be dispatched.
@@ -1637,7 +1680,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (!_closing.load())
{
// Update our scrollbar
_ScrollbarUpdater(ScrollBar(), viewTop, viewHeight, bufferSize);
_ScrollbarUpdater(_scrollBar, viewTop, viewHeight, bufferSize);
}
}
}
@@ -1651,18 +1694,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return hstr;
}
hstring TermControl::GetProfileName() const
{
return _settings.ProfileName();
}
// Method Description:
// - Given a copy-able selection, get the selected text from the buffer and send it to the
// Windows Clipboard (CascadiaWin32:main.cpp).
// - CopyOnSelect does NOT clear the selection
// Arguments:
// - collapseText: collapse all of the text to one line
bool TermControl::CopySelectionToClipboard(bool collapseText)
// - trimTrailingWhitespace: enable removing any whitespace from copied selection
// and get text to appear on separate lines.
bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace)
{
// no selection --> nothing to copy
if (_terminal == nullptr || !_terminal->IsSelectionActive())
@@ -1670,7 +1709,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return false;
}
// extract text from buffer
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(collapseText);
const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace);
// convert text: vector<string> --> string
std::wstring textData;
@@ -1695,7 +1734,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (!_terminal->IsCopyOnSelectActive())
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
}
// send data up for clipboard
@@ -1727,7 +1765,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_connection.TerminalOutput(_connectionOutputEventToken);
_connectionStateChangedRevoker.revoke();
TSFInputControl().Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
_tsfInputControl.Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
if (auto localConnection{ std::exchange(_connection, nullptr) })
{
@@ -1759,7 +1797,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - viewTop: the viewTop to scroll to
void TermControl::ScrollViewport(int viewTop)
{
ScrollBar().Value(viewTop);
_scrollBar.Value(viewTop);
}
int TermControl::GetScrollOffset()
@@ -1794,7 +1832,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// Initialize our font information.
const auto fontFace = settings.FontFace();
const short fontHeight = gsl::narrow_cast<short>(settings.FontSize());
const short fontHeight = gsl::narrow<short>(settings.FontSize());
// 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.
@@ -1828,8 +1866,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// the Terminal). At runtime, this is fine, as we'll transform
// everything by our scaling, so it'll work out. However, right now we
// need to get the exact pixel count.
const float fFontWidth = gsl::narrow_cast<float>(fontSize.X * scale);
const float fFontHeight = gsl::narrow_cast<float>(fontSize.Y * scale);
const float fFontWidth = gsl::narrow<float>(fontSize.X * scale);
const float fFontHeight = gsl::narrow<float>(fontSize.Y * scale);
// UWP XAML scrollbars aren't guaranteed to be the same size as the
// ComCtl scrollbars, but it's certainly close enough.
@@ -1874,7 +1912,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Return Value:
// - The minimum size that this terminal control can be resized to and still
// have a visible character.
winrt::Windows::Foundation::Size TermControl::MinimumSize()
winrt::Windows::Foundation::Size TermControl::MinimumSize() const
{
const auto fontSize = _actualFont.GetSize();
double width = fontSize.X;
@@ -1882,11 +1920,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Reserve additional space if scrollbar is intended to be visible
if (_settings.ScrollState() == ScrollbarState::Visible)
{
width += ScrollBar().ActualWidth();
width += _scrollBar.ActualWidth();
}
// Account for the size of any padding
const auto padding = SwapChainPanel().Margin();
const auto padding = _swapChainPanel.Margin();
width += padding.Left + padding.Right;
height += padding.Top + padding.Bottom;
@@ -1901,19 +1939,19 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - dimension: a dimension (width or height) to be snapped
// Return Value:
// - A dimension that would be aligned to the character grid.
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension)
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const
{
const auto fontSize = _actualFont.GetSize();
const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y;
const auto padding = SwapChainPanel().Margin();
const auto padding = _swapChainPanel.Margin();
auto nonTerminalArea = gsl::narrow_cast<float>(widthOrHeight ?
padding.Left + padding.Right :
padding.Top + padding.Bottom);
if (widthOrHeight && _settings.ScrollState() == ScrollbarState::Visible)
{
nonTerminalArea += gsl::narrow_cast<float>(ScrollBar().ActualWidth());
nonTerminalArea += gsl::narrow_cast<float>(_scrollBar.ActualWidth());
}
const auto gridSize = dimension - nonTerminalArea;
@@ -2038,8 +2076,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// Exclude padding from cursor position calculation
COORD terminalPosition = {
static_cast<SHORT>(cursorPosition.X - SwapChainPanel().Margin().Left),
static_cast<SHORT>(cursorPosition.Y - SwapChainPanel().Margin().Top)
static_cast<SHORT>(cursorPosition.X - _swapChainPanel.Margin().Left),
static_cast<SHORT>(cursorPosition.Y - _swapChainPanel.Margin().Top)
};
const auto fontSize = _actualFont.GetSize();
@@ -2074,13 +2112,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, const CursorPositionEventArgs& eventArgs)
{
// If we haven't initialized yet, just quick return.
if (!_terminal)
{
return;
}
const COORD cursorPos = _terminal->GetCursorPosition();
Windows::Foundation::Point p = { gsl::narrow_cast<float>(cursorPos.X), gsl::narrow_cast<float>(cursorPos.Y) };
Windows::Foundation::Point p = { gsl::narrow<float>(cursorPos.X), gsl::narrow<float>(cursorPos.Y) };
eventArgs.CurrentPosition(p);
}

View File

@@ -61,14 +61,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
winrt::fire_and_forget UpdateSettings(Settings::IControlSettings newSettings);
hstring Title();
hstring GetProfileName() const;
bool CopySelectionToClipboard(bool collapseText);
bool CopySelectionToClipboard(bool trimTrailingWhitespace);
void PasteTextFromClipboard();
void Close();
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize();
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
Windows::Foundation::Size MinimumSize() const;
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const;
void ScrollViewport(int viewTop);
int GetScrollOffset();
@@ -81,14 +80,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void CreateSearchBoxControl();
bool OnF7Pressed();
~TermControl();
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
::Microsoft::Console::Types::IUiaData* GetUiaData() const;
const FontInfo GetActualFont() const;
const Windows::UI::Xaml::Thickness GetPadding();
const Windows::UI::Xaml::Thickness GetPadding() const;
TerminalConnection::ConnectionState ConnectionState() const;
@@ -108,13 +105,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// clang-format on
private:
friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
TerminalConnection::ITerminalConnection _connection;
bool _initializedTerminal;
Windows::UI::Xaml::Controls::Grid _root;
Windows::UI::Xaml::Controls::Image _bgImageLayer;
Windows::UI::Xaml::Controls::SwapChainPanel _swapChainPanel;
Windows::UI::Xaml::Controls::Primitives::ScrollBar _scrollBar;
winrt::com_ptr<SearchBoxControl> _searchBox;
TSFInputControl _tsfInputControl;
event_token _connectionOutputEventToken;
TermControl::Tapped_revoker _tappedRevoker;
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
@@ -158,18 +162,23 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Timestamp _lastMouseClick;
unsigned int _multiClickCounter;
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPos;
std::optional<winrt::Windows::Foundation::Point> _unfocusedClickPos;
bool _isClickDragSelection;
std::optional<winrt::Windows::Foundation::Point> _clickDragStartPos{ std::nullopt };
// Event revokers -- we need to deregister ourselves before we die,
// lest we get callbacks afterwards.
winrt::Windows::UI::Xaml::Controls::Control::SizeChanged_revoker _sizeChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::CompositionScaleChanged_revoker _compositionScaleChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
void _Create();
void _ApplyUISettings();
void _InitializeBackgroundBrush();
winrt::fire_and_forget _BackgroundColorChanged(const uint32_t color);
bool _InitializeTerminal();
void _UpdateFont(const bool initialUpdate = false);
void _SetFontSize(int fontSize);
void _TappedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs const& e);
void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
void _CharacterHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs const& e);
void _PointerPressedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);

View File

@@ -7,17 +7,6 @@ namespace Microsoft.Terminal.TerminalControl
delegate void FontSizeChangedEventArgs(Int32 width, Int32 height, Boolean isInitialChange);
delegate void ScrollPositionChangedEventArgs(Int32 viewTop, Int32 viewHeight, Int32 bufferLength);
// C++/winrt makes it difficult to share this idl between two projects,
// Instead, we just pin the uuid and include it in both TermControl and App
// If you update this one, please update TerminalApp\IF7Listener.idl.
// If you change this interface, please update the guid.
// If you press F7 and get a runtime error, go make sure both copies are the same.
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")]
interface IF7Listener
{
Boolean OnF7Pressed();
}
runtimeclass CopyToClipboardEventArgs
{
String Text { get; };
@@ -30,7 +19,7 @@ namespace Microsoft.Terminal.TerminalControl
void HandleClipboardData(String data);
}
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IF7Listener
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl
{
TermControl();
TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
@@ -52,7 +41,7 @@ namespace Microsoft.Terminal.TerminalControl
String Title { get; };
Boolean CopySelectionToClipboard(Boolean collapseText);
Boolean CopySelectionToClipboard(Boolean trimTrailingWhitespace);
void PasteTextFromClipboard();
void Close();
Windows.Foundation.Size CharacterDimensions { get; };

View File

@@ -1,82 +0,0 @@
<UserControl
x:Class="Microsoft.Terminal.TerminalControl.TermControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.TerminalControl"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="768"
d:DesignWidth="1024"
TabNavigation="Cycle"
IsTabStop="True"
AllowFocusOnInteraction="True"
AllowDrop="True"
Drop="_DragDropHandler"
DragOver="_DragOverHandler"
Tapped="_TappedHandler"
PointerWheelChanged="_MouseWheelHandler"
PreviewKeyDown="_KeyDownHandler"
CharacterReceived="_CharacterHandler"
GotFocus="_GotFocusHandler"
LostFocus="_LostFocusHandler">
<!--
TODO GH#4031: We've investigated whether we should be using KeyDown or PreviewKeyDown
but not necessarily come to a consensus. It's possible that moving input to the SwapChainPanel
will solve a bunch of the search input issues we found last time we switched.
-->
<Grid x:Name="RootGrid">
<Image x:Name="BackgroundImage"
AutomationProperties.AccessibilityView="Raw" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<SwapChainPanel x:Name="SwapChainPanel"
SizeChanged="_SwapChainSizeChanged"
CompositionScaleChanged="_SwapChainScaleChanged"
PointerPressed="_PointerPressedHandler"
PointerMoved="_PointerMovedHandler"
PointerReleased="_PointerReleasedHandler" />
<!-- Putting this in a grid w/ the SwapChainPanel
ensures that it's always aligned w/ the scrollbar -->
<local:SearchBoxControl x:Name="SearchBox"
x:Load="False"
Visibility="Collapsed"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Search="_Search"
Closed="_CloseSearchBoxControl" />
</Grid>
<ScrollBar Grid.Column="1"
x:Name="ScrollBar"
Orientation="Vertical"
IndicatorMode="MouseIndicator"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Maximum="1"
ViewportSize="10"
IsTabStop="False"
SmallChange="1"
LargeChange="4"
ValueChanged="_ScrollbarChangeHandler"
PointerPressed="_CapturePointer"
PointerReleased="_ReleasePointerCapture" />
</Grid>
<local:TSFInputControl x:Name="TSFInputControl"
CompositionCompleted="_CompositionCompleted"
CurrentCursorPosition="_CurrentCursorPositionHandler"
CurrentFontInfo="_FontInfoHandler" />
</Grid>
</UserControl>

View File

@@ -3,7 +3,6 @@
#include "pch.h"
#include <UIAutomationCore.h>
#include <LibraryResources.h>
#include "TermControlAutomationPeer.h"
#include "TermControl.h"
#include "TermControlAutomationPeer.g.cpp"
@@ -12,7 +11,6 @@
using namespace Microsoft::Console::Types;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::Graphics::Display;
namespace UIA
{
@@ -30,10 +28,9 @@ namespace XamlAutomation
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner) :
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer
_termControl{ owner }
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner) // pass owner to FrameworkElementAutomationPeer
{
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, _termControl->GetUiaData(), this));
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, owner, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this)));
};
// Method Description:
@@ -78,7 +75,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
});
}
hstring TermControlAutomationPeer::GetClassNameCore() const
winrt::hstring TermControlAutomationPeer::GetClassNameCore() const
{
return L"TermControl";
}
@@ -88,12 +85,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return AutomationControlType::Text;
}
hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
winrt::hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
{
return RS_(L"TerminalControl_ControlType");
// TODO GitHub #2142: Localize string
return L"TerminalControl";
}
Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
winrt::Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
{
switch (patternInterface)
{
@@ -105,41 +103,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
AutomationOrientation TermControlAutomationPeer::GetOrientationCore() const
{
return AutomationOrientation::Vertical;
}
hstring TermControlAutomationPeer::GetNameCore() const
{
// fallback to title if profile name is empty
auto profileName = _termControl->GetProfileName();
if (profileName.empty())
{
return _termControl->Title();
}
return profileName;
}
hstring TermControlAutomationPeer::GetHelpTextCore() const
{
return _termControl->Title();
}
AutomationLiveSetting TermControlAutomationPeer::GetLiveSettingCore() const
{
return AutomationLiveSetting::Polite;
}
#pragma region ITextProvider
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal));
return WrapArrayOfTextRangeProviders(pReturnVal);
}
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetVisibleRanges(&pReturnVal));
@@ -178,7 +150,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
Windows::UI::Xaml::Automation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
{
UIA::SupportedTextSelection returnVal;
THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal));
@@ -187,63 +159,27 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
#pragma endregion
#pragma region IControlAccessibilityInfo
COORD TermControlAutomationPeer::GetFontSize() const
{
return _termControl->GetActualFont().GetSize();
}
RECT TermControlAutomationPeer::GetBounds() const
RECT TermControlAutomationPeer::GetBoundingRectWrapped()
{
auto rect = GetBoundingRectangle();
return {
gsl::narrow_cast<LONG>(rect.X),
gsl::narrow_cast<LONG>(rect.Y),
gsl::narrow_cast<LONG>(rect.X + rect.Width),
gsl::narrow_cast<LONG>(rect.Y + rect.Height)
gsl::narrow<LONG>(rect.X),
gsl::narrow<LONG>(rect.Y),
gsl::narrow<LONG>(rect.X + rect.Width),
gsl::narrow<LONG>(rect.Y + rect.Height)
};
}
HRESULT TermControlAutomationPeer::GetHostUiaProvider(IRawElementProviderSimple** provider)
{
RETURN_HR_IF(E_INVALIDARG, provider == nullptr);
*provider = nullptr;
return S_OK;
}
RECT TermControlAutomationPeer::GetPadding() const
{
auto padding = _termControl->GetPadding();
return {
gsl::narrow_cast<LONG>(padding.Left),
gsl::narrow_cast<LONG>(padding.Top),
gsl::narrow_cast<LONG>(padding.Right),
gsl::narrow_cast<LONG>(padding.Bottom)
};
}
double TermControlAutomationPeer::GetScaleFactor() const
{
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
}
void TermControlAutomationPeer::ChangeViewport(const SMALL_RECT NewWindow)
{
_termControl->ScrollViewport(NewWindow.Top);
}
#pragma endregion
// Method Description:
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
// Arguments:
// - SAFEARRAY of UIA::UiaTextRange (ITextRangeProviders)
// Return Value:
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{
// transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges);
int count = gsl::narrow<int>(providers.size());
std::vector<XamlAutomation::ITextRangeProvider> vec;
@@ -251,11 +187,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto parentProvider = this->ProviderFromPeer(*this);
for (int i = 0; i < count; i++)
{
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
auto xutr = winrt::make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
}
com_array<XamlAutomation::ITextRangeProvider> result{ vec };
winrt::com_array<XamlAutomation::ITextRangeProvider> result{ vec };
return result;
}

View File

@@ -27,30 +27,22 @@ Author(s):
#include "TermControl.h"
#include "TermControlAutomationPeer.g.h"
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "../types/TermControlUiaProvider.hpp"
#include "TermControlUiaProvider.hpp"
#include "../types/IUiaEventDispatcher.h"
#include "../types/IControlAccessibilityInfo.h"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct TermControlAutomationPeer :
public TermControlAutomationPeerT<TermControlAutomationPeer>,
::Microsoft::Console::Types::IUiaEventDispatcher,
::Microsoft::Console::Types::IControlAccessibilityInfo
::Microsoft::Console::Types::IUiaEventDispatcher
{
public:
TermControlAutomationPeer(Microsoft::Terminal::TerminalControl::implementation::TermControl* owner);
TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner);
#pragma region FrameworkElementAutomationPeer
hstring GetClassNameCore() const;
Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
hstring GetLocalizedControlTypeCore() const;
Windows::Foundation::IInspectable GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) const;
Windows::UI::Xaml::Automation::Peers::AutomationOrientation GetOrientationCore() const;
hstring GetNameCore() const;
hstring GetHelpTextCore() const;
Windows::UI::Xaml::Automation::Peers::AutomationLiveSetting GetLiveSettingCore() const;
#pragma endregion
winrt::hstring GetClassNameCore() const;
winrt::Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
winrt::hstring GetLocalizedControlTypeCore() const;
winrt::Windows::Foundation::IInspectable GetPatternCore(winrt::Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) const;
#pragma region IUiaEventDispatcher
void SignalSelectionChanged() override;
@@ -61,27 +53,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
#pragma region ITextProvider Pattern
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromPoint(Windows::Foundation::Point screenLocation);
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromChild(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple childElement);
com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetVisibleRanges();
com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetSelection();
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetVisibleRanges();
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetSelection();
Windows::UI::Xaml::Automation::SupportedTextSelection SupportedTextSelection();
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider DocumentRange();
#pragma endregion
#pragma region IControlAccessibilityInfo Pattern
// Inherited via IControlAccessibilityInfo
virtual COORD GetFontSize() const override;
virtual RECT GetBounds() const override;
virtual RECT GetPadding() const override;
virtual double GetScaleFactor() const override;
virtual void ChangeViewport(SMALL_RECT NewWindow) override;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override;
#pragma endregion
RECT GetBoundingRectWrapped();
private:
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* _termControl;
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
};
}

View File

@@ -1,21 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "pch.h"
#include "TermControlUiaProvider.hpp"
#include "TermControl.h"
using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL;
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept
HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* termControl,
_In_ std::function<RECT(void)> GetBoundingRect)
{
RETURN_HR_IF_NULL(E_INVALIDARG, uiaData);
RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(uiaData));
RETURN_HR_IF_NULL(E_INVALIDARG, termControl);
RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(termControl->GetUiaData()));
_controlInfo = controlInfo;
_getBoundingRect = GetBoundingRect;
_termControl = termControl;
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr);
@@ -23,10 +24,8 @@ HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ ::Microsoft::Console
}
IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
/*ApiMsgNavigate apiMsg;
apiMsg.Direction = direction;
@@ -57,29 +56,18 @@ IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRec
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr);
const RECT rc = _controlInfo->GetBounds();
RECT rc = _getBoundingRect();
pRect->left = rc.left;
pRect->top = rc.top;
pRect->width = static_cast<double>(rc.right) - static_cast<double>(rc.left);
pRect->height = static_cast<double>(rc.bottom) - static_cast<double>(rc.top);
pRect->width = rc.right - rc.left;
pRect->height = rc.bottom - rc.top;
return S_OK;
}
IFACEMETHODIMP TermControlUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider)
{
try
{
return _controlInfo->GetHostUiaProvider(ppProvider);
}
CATCH_RETURN();
}
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr);
try
@@ -99,48 +87,47 @@ IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybe
const COORD TermControlUiaProvider::GetFontSize() const
{
return _controlInfo->GetFontSize();
return _termControl->GetActualFont().GetSize();
}
const RECT TermControlUiaProvider::GetPadding() const
const winrt::Windows::UI::Xaml::Thickness TermControlUiaProvider::GetPadding() const
{
return _controlInfo->GetPadding();
}
const double TermControlUiaProvider::GetScaleFactor() const
{
return _controlInfo->GetScaleFactor();
return _termControl->GetPadding();
}
void TermControlUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
{
_controlInfo->ChangeViewport(NewWindow);
_termControl->ScrollViewport(NewWindow.Top);
}
HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
try
{
result.clear();
typename std::remove_reference<decltype(result)>::type temporaryResult;
const auto start = _pData->GetSelectionAnchor();
std::deque<ComPtr<UiaTextRange>> ranges;
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, wordDelimiters, ranges));
// we need to make end exclusive
auto end = _pData->GetSelectionEnd();
_pData->GetTextBuffer().GetSize().IncrementInBounds(end, true);
while (!ranges.empty())
{
temporaryResult.emplace_back(std::move(ranges.back()));
ranges.pop_back();
}
// TODO GH #4509: Box Selection is misrepresented here as a line selection.
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result;
return S_OK;
std::swap(result, temporaryResult);
return S_OK;
}
CATCH_RETURN();
}
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, wordDelimiters));
*ppUtr = result;
return S_OK;
}
@@ -152,8 +139,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
*ppUtr = result;
return S_OK;
}
@@ -166,8 +153,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result;
return S_OK;
}
@@ -179,8 +166,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
*ppUtr = result;
return S_OK;
}

View File

@@ -19,33 +19,36 @@ Author(s):
#pragma once
#include "ScreenInfoUiaProviderBase.h"
#include "UiaTextRangeBase.hpp"
#include "IControlAccessibilityInfo.h"
#include "TermControlUiaTextRange.hpp"
#include "..\types\ScreenInfoUiaProviderBase.h"
#include "..\types\UiaTextRangeBase.hpp"
#include "UiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct TermControl;
}
namespace Microsoft::Terminal
{
class TermControlUiaProvider : public Microsoft::Console::Types::ScreenInfoUiaProviderBase
{
public:
TermControlUiaProvider() = default;
HRESULT RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept;
HRESULT RuntimeClassInitialize(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* termControl,
_In_ std::function<RECT()> GetBoundingRect);
// IRawElementProviderFragment methods
IFACEMETHODIMP Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept override;
IFACEMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple** ppProvider) noexcept override;
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override;
IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override;
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept override;
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override;
const COORD GetFontSize() const;
const RECT GetPadding() const;
const double GetScaleFactor() const;
const winrt::Windows::UI::Xaml::Thickness GetPadding() const;
void ChangeViewport(const SMALL_RECT NewWindow) override;
protected:
HRESULT GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
// degenerate range
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
@@ -70,6 +73,7 @@ namespace Microsoft::Terminal
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
private:
::Microsoft::Console::Types::IControlAccessibilityInfo* _controlInfo{ nullptr };
std::function<RECT(void)> _getBoundingRect;
winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* _termControl;
};
}

View File

@@ -36,15 +36,17 @@
<ClInclude Include="SearchBoxControl.h">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="TermControlUiaProvider.hpp" />
<ClInclude Include="TermControl.h">
<DependentUpon>TermControl.xaml</DependentUpon>
<DependentUpon>TermControl.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TermControlAutomationPeer.h">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TSFInputControl.h">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
<DependentUpon>TSFInputControl.idl</DependentUpon>
</ClInclude>
<ClInclude Include="UiaTextRange.hpp" />
<ClInclude Include="XamlUiaTextRange.h" />
</ItemGroup>
<ItemGroup>
@@ -55,36 +57,34 @@
<ClCompile Include="SearchBoxControl.cpp">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TermControlUiaProvider.cpp" />
<ClCompile Include="TermControl.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
<DependentUpon>TermControl.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TSFInputControl.cpp">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
<DependentUpon>TSFInputControl.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="TermControlAutomationPeer.cpp">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="UiaTextRange.cpp" />
<ClCompile Include="XamlUiaTextRange.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="SearchBoxControl.idl">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</Midl>
<Midl Include="TermControl.idl">
<DependentUpon>TermControl.xaml</DependentUpon>
</Midl>
<Midl Include="TermControl.idl" />
<Midl Include="TermControlAutomationPeer.idl" />
<Midl Include="TSFInputControl.idl">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</Midl>
<Midl Include="TSFInputControl.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="TerminalControl.def" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/Resources.language-en.resw" />
<PRIResource Include="Resources/en-US/Resources.resw" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
@@ -113,12 +113,6 @@
<Page Include="SearchBoxControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="TermControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="TSFInputControl.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />

View File

@@ -18,7 +18,6 @@
<ClCompile Include="TermControlUiaProvider.cpp" />
<ClCompile Include="UiaTextRange.cpp" />
<ClCompile Include="SearchBoxControl.cpp" />
<ClCompile Include="init.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -27,12 +26,12 @@
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="TermControlUiaProvider.hpp" />
<ClInclude Include="UiaTextRange.hpp" />
<ClCompile Include="SearchBoxControl.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="TermControl.idl" />
<Midl Include="TermControlAutomationPeer.idl" />
<Midl Include="SearchBoxControl.idl" />
<Midl Include="TSFInputControl.idl" />
</ItemGroup>
<ItemGroup>
<None Include="TerminalControl.def" />
@@ -44,9 +43,4 @@
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/Resources.language-en.resw">
<Filter>Resources</Filter>
</PRIResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,188 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "UiaTextRange.hpp"
#include "TermControlUiaProvider.hpp"
using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL;
using namespace winrt::Windows::Graphics::Display;
HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
_In_ const std::wstring_view wordDelimiters,
_Out_ std::deque<ComPtr<UiaTextRange>>& ranges)
{
try
{
typename std::remove_reference<decltype(ranges)>::type temporaryResult;
// get the selection rects
const auto rectangles = pData->GetSelectionRects();
// create a range for each row
for (const auto& rect : rectangles)
{
const auto start = rect.Origin();
const auto end = rect.EndExclusive();
ComPtr<UiaTextRange> range;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, wordDelimiters));
temporaryResult.emplace_back(std::move(range));
}
std::swap(temporaryResult, ranges);
return S_OK;
}
CATCH_RETURN();
}
// degenerate range constructor.
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
}
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
}
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const COORD start,
const COORD end,
const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters);
}
// returns a degenerate text range of the start of the row closest to the y value of point
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const UiaPoint point,
const std::wstring_view wordDelimiters)
{
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters));
Initialize(point);
return S_OK;
}
HRESULT UiaTextRange::RuntimeClassInitialize(const UiaTextRange& a)
{
return UiaTextRangeBase::RuntimeClassInitialize(a);
}
IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr);
*ppRetVal = nullptr;
auto hr = MakeAndInitialize<UiaTextRange>(ppRetVal, *this);
if (hr != S_OK)
{
*ppRetVal = nullptr;
return hr;
}
#if defined(_DEBUG) && defined(UiaTextRangeBase_DEBUG_MSGS)
OutputDebugString(L"Clone\n");
std::wstringstream ss;
ss << _id << L" cloned to " << (static_cast<UiaTextRangeBase*>(*ppRetVal))->_id;
std::wstring str = ss.str();
OutputDebugString(str.c_str());
OutputDebugString(L"\n");
#endif
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
// tracing
/*ApiMsgClone apiMsg;
apiMsg.CloneId = static_cast<UiaTextRangeBase*>(*ppRetVal)->GetId();
Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/
return S_OK;
}
void UiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow)
{
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
provider->ChangeViewport(NewWindow);
}
// Method Description:
// - Transform coordinates relative to the client to relative to the screen
// Arguments:
// - clientPoint: coordinates relative to the client where
// (0,0) is the top-left of the app window
// Return Value:
// - <none>
void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
{
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
auto includeOffsets = [](long clientPos, double termControlPos, double padding, double scaleFactor) {
auto result = base::ClampedNumeric<double>(clientPos);
result += padding;
result *= scaleFactor;
result += termControlPos;
return result;
};
// update based on TermControl location (important for Panes)
UiaRect boundingRect;
THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
// update based on TermControl padding
const auto padding = provider->GetPadding();
// Get scale factor for display
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
clientPoint->x = includeOffsets(clientPoint->x, boundingRect.left, padding.Left, scaleFactor);
clientPoint->y = includeOffsets(clientPoint->y, boundingRect.top, padding.Top, scaleFactor);
}
// Method Description:
// - Transform coordinates relative to the screen to relative to the client
// Arguments:
// - screenPoint: coordinates relative to the screen where
// (0,0) is the top-left of the screen
// Return Value:
// - <none>
void UiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const
{
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
auto includeOffsets = [](long screenPos, double termControlPos, double padding, double scaleFactor) {
auto result = base::ClampedNumeric<double>(screenPos);
result -= termControlPos;
result /= scaleFactor;
result -= padding;
return result;
};
// update based on TermControl location (important for Panes)
UiaRect boundingRect;
THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
// update based on TermControl padding
const auto padding = provider->GetPadding();
// Get scale factor for display
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
screenPoint->x = includeOffsets(screenPoint->x, boundingRect.left, padding.Left, scaleFactor);
screenPoint->y = includeOffsets(screenPoint->y, boundingRect.top, padding.Top, scaleFactor);
}
const COORD UiaTextRange::_getScreenFontSize() const
{
// Do NOT get the font info from IRenderData. It is a dummy font info.
// Instead, the font info is saved in the TermControl. So we have to
// ask our parent to get it for us.
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
return provider->GetFontSize();
}

View File

@@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- TermControlUiaTextRange.hpp
- UiaTextRange.hpp
Abstract:
- This module provides UI Automation access to the text of the console
@@ -20,28 +20,33 @@ Author(s):
namespace Microsoft::Terminal
{
class TermControlUiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase
class UiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase
{
public:
TermControlUiaTextRange() = default;
static HRESULT GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
_In_ const std::wstring_view wordDelimiters,
_Out_ std::deque<WRL::ComPtr<UiaTextRange>>& ranges);
UiaTextRange() = default;
// degenerate range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// degenerate range at cursor position
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// specific endpoint range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const COORD start,
const COORD end,
const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// range from a UiaPoint
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
@@ -49,7 +54,7 @@ namespace Microsoft::Terminal
const UiaPoint point,
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
HRESULT RuntimeClassInitialize(const TermControlUiaTextRange& a) noexcept;
HRESULT RuntimeClassInitialize(const UiaTextRange& a);
IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override;

View File

@@ -3,7 +3,7 @@
#include "pch.h"
#include "XamlUiaTextRange.h"
#include "../types/TermControlUiaTextRange.hpp"
#include "UiaTextRange.hpp"
#include <UIAutomationClient.h>
// the same as COR_E_NOTSUPPORTED

View File

@@ -22,7 +22,7 @@ Author(s):
#include "TermControlAutomationPeer.h"
#include <UIAutomationCore.h>
#include "../types/TermControlUiaTextRange.hpp"
#include "UiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{

View File

@@ -44,10 +44,12 @@ Terminal::Terminal() :
_pfnWriteInput{ nullptr },
_scrollOffset{ 0 },
_snapOnInput{ true },
_blockSelection{ false },
_selection{ std::nullopt },
_boxSelection{ false },
_selectionActive{ false },
_allowSingleCharSelection{ true },
_copyOnSelect{ false }
_copyOnSelect{ false },
_selectionAnchor{ 0, 0 },
_endSelectionPosition{ 0, 0 }
{
auto dispatch = std::make_unique<TerminalDispatch>(*this);
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
@@ -452,22 +454,6 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
// With well behaving shells during normal operation this safeguard should normally not be encountered.
proposedCursorPosition.X = 0;
proposedCursorPosition.Y++;
// Try the character again.
i--;
// Mark the line we're currently on as wrapped
// TODO: GH#780 - This should really be a _deferred_ newline. If
// the next character to come in is a newline or a cursor
// movement or anything, then we should _not_ wrap this line
// here.
//
// This is more WriteCharsLegacy2ElectricBoogaloo work. I'm
// leaving it like this for now - it'll break for lines that
// _exactly_ wrap, but we can't re-wrap lines now anyways, so it
// doesn't matter.
_buffer->GetRowByOffset(cursorPosBefore.Y).GetCharRow().SetWrapForced(true);
}
_AdjustCursorPosition(proposedCursorPosition);

View File

@@ -141,8 +141,7 @@ public:
const bool IsSelectionActive() const noexcept override;
void ClearSelection() override;
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
const COORD GetSelectionAnchor() const noexcept override;
const COORD GetSelectionEnd() const noexcept override;
const COORD GetSelectionAnchor() const override;
const std::wstring GetConsoleTitle() const noexcept override;
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override;
#pragma endregion
@@ -157,17 +156,12 @@ public:
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
enum class SelectionExpansionMode
{
Cell,
Word,
Line
};
const bool IsCopyOnSelectActive() const noexcept;
void MultiClickSelection(const COORD viewportPos, SelectionExpansionMode expansionMode);
void DoubleClickSelection(const COORD position);
void TripleClickSelection(const COORD position);
void SetSelectionAnchor(const COORD position);
void SetSelectionEnd(const COORD position, std::optional<SelectionExpansionMode> newExpansionMode = std::nullopt);
void SetBlockSelection(const bool isEnabled) noexcept;
void SetEndSelectionPosition(const COORD position);
void SetBoxSelection(const bool isEnabled) noexcept;
const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const;
#pragma endregion
@@ -192,20 +186,19 @@ private:
bool _suppressApplicationTitle;
#pragma region Text Selection
// a selection is represented as a range between two COORDs (start and end)
// the pivot is the COORD that remains selected when you extend a selection in any direction
// this is particularly useful when a word selection is extended over its starting point
// see TerminalSelection.cpp for more information
struct SelectionAnchors
enum class SelectionExpansionMode
{
COORD start;
COORD end;
COORD pivot;
Cell,
Word,
Line
};
std::optional<SelectionAnchors> _selection;
bool _blockSelection;
COORD _selectionAnchor;
COORD _endSelectionPosition;
bool _boxSelection;
bool _selectionActive;
bool _allowSingleCharSelection;
bool _copyOnSelect;
SHORT _selectionVerticalOffset;
std::wstring _wordDelimiters;
SelectionExpansionMode _multiClickSelectionMode;
#pragma endregion
@@ -254,10 +247,15 @@ private:
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
std::vector<SMALL_RECT> _GetSelectionRects() const noexcept;
std::pair<COORD, COORD> _PivotSelection(const COORD targetPos) const;
std::pair<COORD, COORD> _ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const;
SHORT _ExpandWideGlyphSelectionLeft(const SHORT xPos, const SHORT yPos) const;
SHORT _ExpandWideGlyphSelectionRight(const SHORT xPos, const SHORT yPos) const;
COORD _ExpandDoubleClickSelectionLeft(const COORD position) const;
COORD _ExpandDoubleClickSelectionRight(const COORD position) const;
COORD _ConvertToBufferCell(const COORD viewportPos) const;
const bool _IsSingleCellSelection() const noexcept;
std::tuple<COORD, COORD> _PreprocessSelectionCoords() const;
SMALL_RECT _GetSelectionRow(const SHORT row, const COORD higherCoord, const COORD lowerCoord) const;
void _ExpandSelectionRow(SMALL_RECT& selectionRow) const;
#pragma endregion
#ifdef UNIT_TESTING

View File

@@ -7,38 +7,6 @@
using namespace Microsoft::Terminal::Core;
/* Selection Pivot Description:
* The pivot helps properly update the selection when a user moves a selection over itself
* See SelectionTest::DoubleClickDrag_Left for an example of the functionality mentioned here
* As an example, consider the following scenario...
* 1. Perform a word selection (double-click) on a word
*
* |-position where we double-clicked
* _|_
* |word|
* |--|
* start & pivot-| |-end
*
* 2. Drag your mouse down a line
*
*
* start & pivot-|__________
* __|word_______|
* |______|
* |
* |-end & mouse position
*
* 3. Drag your mouse up two lines
*
* |-start & mouse position
* |________
* ____| ______|
* |___w|ord
* |-end & pivot
*
* The pivot never moves until a new selection is created. It ensures that that cell will always be selected.
*/
// Method Description:
// - Helper to determine the selected region of the buffer. Used for rendering.
// Return Value:
@@ -54,32 +22,183 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const noexcept
try
{
return _buffer->GetTextRects(_selection->start, _selection->end, _blockSelection);
// NOTE: (0,0) is the top-left of the screen
// the physically "higher" coordinate is closer to the top-left
// the physically "lower" coordinate is closer to the bottom-right
const auto [higherCoord, lowerCoord] = _PreprocessSelectionCoords();
SHORT selectionRectSize;
THROW_IF_FAILED(ShortSub(lowerCoord.Y, higherCoord.Y, &selectionRectSize));
THROW_IF_FAILED(ShortAdd(selectionRectSize, 1, &selectionRectSize));
std::vector<SMALL_RECT> selectionArea;
selectionArea.reserve(selectionRectSize);
for (auto row = higherCoord.Y; row <= lowerCoord.Y; row++)
{
SMALL_RECT selectionRow = _GetSelectionRow(row, higherCoord, lowerCoord);
_ExpandSelectionRow(selectionRow);
selectionArea.emplace_back(selectionRow);
}
result.swap(selectionArea);
}
CATCH_LOG();
return result;
}
// Method Description:
// - convert selection anchors to proper coordinates for rendering
// NOTE: (0,0) is top-left so vertical comparison is inverted
// Arguments:
// - None
// Return Value:
// - tuple.first: the physically "higher" coordinate (closer to the top-left)
// - tuple.second: the physically "lower" coordinate (closer to the bottom-right)
std::tuple<COORD, COORD> Terminal::_PreprocessSelectionCoords() const
{
// create these new anchors for comparison and rendering
COORD selectionAnchorWithOffset{ _selectionAnchor };
COORD endSelectionPositionWithOffset{ _endSelectionPosition };
// Add anchor offset here to update properly on new buffer output
THROW_IF_FAILED(ShortAdd(selectionAnchorWithOffset.Y, _selectionVerticalOffset, &selectionAnchorWithOffset.Y));
THROW_IF_FAILED(ShortAdd(endSelectionPositionWithOffset.Y, _selectionVerticalOffset, &endSelectionPositionWithOffset.Y));
// clamp anchors to be within buffer bounds
const auto bufferSize = _buffer->GetSize();
bufferSize.Clamp(selectionAnchorWithOffset);
bufferSize.Clamp(endSelectionPositionWithOffset);
// NOTE: (0,0) is top-left so vertical comparison is inverted
// CompareInBounds returns whether A is to the left of (rv<0), equal to (rv==0), or to the right of (rv>0) B.
// Here, we want the "left"most coordinate to be the one "higher" on the screen. The other gets the dubious honor of
// being the "lower."
return bufferSize.CompareInBounds(selectionAnchorWithOffset, endSelectionPositionWithOffset) <= 0 ?
std::make_tuple(selectionAnchorWithOffset, endSelectionPositionWithOffset) :
std::make_tuple(endSelectionPositionWithOffset, selectionAnchorWithOffset);
}
// Method Description:
// - constructs the selection row at the given row
// NOTE: (0,0) is top-left so vertical comparison is inverted
// Arguments:
// - row: the buffer y-value under observation
// - higherCoord: the physically "higher" coordinate (closer to the top-left)
// - lowerCoord: the physically "lower" coordinate (closer to the bottom-right)
// Return Value:
// - the selection row needed for rendering
SMALL_RECT Terminal::_GetSelectionRow(const SHORT row, const COORD higherCoord, const COORD lowerCoord) const
{
SMALL_RECT selectionRow;
selectionRow.Top = row;
selectionRow.Bottom = row;
if (_boxSelection || higherCoord.Y == lowerCoord.Y)
{
selectionRow.Left = std::min(higherCoord.X, lowerCoord.X);
selectionRow.Right = std::max(higherCoord.X, lowerCoord.X);
}
else
{
selectionRow.Left = (row == higherCoord.Y) ? higherCoord.X : _buffer->GetSize().Left();
selectionRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : _buffer->GetSize().RightInclusive();
}
return selectionRow;
}
// Method Description:
// - Get the current anchor position relative to the whole text buffer
// Arguments:
// - None
// Return Value:
// - None
const COORD Terminal::GetSelectionAnchor() const noexcept
const COORD Terminal::GetSelectionAnchor() const
{
return _selection->start;
COORD selectionAnchorPos{ _selectionAnchor };
THROW_IF_FAILED(ShortAdd(selectionAnchorPos.Y, _selectionVerticalOffset, &selectionAnchorPos.Y));
return selectionAnchorPos;
}
// Method Description:
// - Get the current end anchor position relative to the whole text buffer
// - Expand the selection row according to selection mode and wide glyphs
// - this is particularly useful for box selections (ALT + selection)
// Arguments:
// - None
// - selectionRow: the selection row to be expanded
// Return Value:
// - None
const COORD Terminal::GetSelectionEnd() const noexcept
// - modifies selectionRow's Left and Right values to expand properly
void Terminal::_ExpandSelectionRow(SMALL_RECT& selectionRow) const
{
return _selection->end;
const auto row = selectionRow.Top;
// expand selection for Double/Triple Click
if (_multiClickSelectionMode == SelectionExpansionMode::Word)
{
selectionRow.Left = _ExpandDoubleClickSelectionLeft({ selectionRow.Left, row }).X;
selectionRow.Right = _ExpandDoubleClickSelectionRight({ selectionRow.Right, row }).X;
}
else if (_multiClickSelectionMode == SelectionExpansionMode::Line)
{
selectionRow.Left = _buffer->GetSize().Left();
selectionRow.Right = _buffer->GetSize().RightInclusive();
}
// expand selection for Wide Glyphs
selectionRow.Left = _ExpandWideGlyphSelectionLeft(selectionRow.Left, row);
selectionRow.Right = _ExpandWideGlyphSelectionRight(selectionRow.Right, row);
}
// Method Description:
// - Expands the selection left-wards to cover a wide glyph, if necessary
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
// Return Value:
// - updated x position to encapsulate the wide glyph
SHORT Terminal::_ExpandWideGlyphSelectionLeft(const SHORT xPos, const SHORT yPos) const
{
// don't change the value if at/outside the boundary
const auto bufferSize = _buffer->GetSize();
if (xPos <= bufferSize.Left() || xPos > bufferSize.RightInclusive())
{
return xPos;
}
COORD position{ xPos, yPos };
const auto attr = _buffer->GetCellDataAt(position)->DbcsAttr();
if (attr.IsTrailing())
{
// move off by highlighting the lead half too.
// alters position.X
bufferSize.DecrementInBounds(position);
}
return position.X;
}
// Method Description:
// - Expands the selection right-wards to cover a wide glyph, if necessary
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
// Return Value:
// - updated x position to encapsulate the wide glyph
SHORT Terminal::_ExpandWideGlyphSelectionRight(const SHORT xPos, const SHORT yPos) const
{
// don't change the value if at/outside the boundary
const auto bufferSize = _buffer->GetSize();
if (xPos < bufferSize.Left() || xPos >= bufferSize.RightInclusive())
{
return xPos;
}
COORD position{ xPos, yPos };
const auto attr = _buffer->GetCellDataAt(position)->DbcsAttr();
if (attr.IsLeading())
{
// move off by highlighting the trailing half too.
// alters position.X
bufferSize.IncrementInBounds(position);
}
return position.X;
}
// Method Description:
@@ -88,7 +207,7 @@ const COORD Terminal::GetSelectionEnd() const noexcept
// - bool representing if selection is only a single cell. Used for copyOnSelect
const bool Terminal::_IsSingleCellSelection() const noexcept
{
return (_selection->start == _selection->end);
return (_selectionAnchor == _endSelectionPosition);
}
// Method Description:
@@ -103,7 +222,7 @@ const bool Terminal::IsSelectionActive() const noexcept
{
return false;
}
return _selection.has_value();
return _selectionActive;
}
// Method Description:
@@ -116,60 +235,79 @@ const bool Terminal::IsCopyOnSelectActive() const noexcept
}
// Method Description:
// - Perform a multi-click selection at viewportPos expanding according to the expansionMode
// - Select the sequence between delimiters defined in Settings
// Arguments:
// - viewportPos: the (x,y) coordinate on the visible viewport
// - expansionMode: the SelectionExpansionMode to dictate the boundaries of the selection anchors
void Terminal::MultiClickSelection(const COORD viewportPos, SelectionExpansionMode expansionMode)
// - position: the (x,y) coordinate on the visible viewport
void Terminal::DoubleClickSelection(const COORD position)
{
// set the selection pivot to expand the selection using SetSelectionEnd()
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
#pragma warning(suppress : 26496) // cpp core checks wants this const but .Clamp() can write it.
COORD positionWithOffsets = _ConvertToBufferCell(position);
_multiClickSelectionMode = expansionMode;
SetSelectionEnd(viewportPos);
// scan leftwards until delimiter is found and
// set selection anchor to one right of that spot
_selectionAnchor = _ExpandDoubleClickSelectionLeft(positionWithOffsets);
THROW_IF_FAILED(ShortSub(_selectionAnchor.Y, gsl::narrow<SHORT>(ViewStartIndex()), &_selectionAnchor.Y));
_selectionVerticalOffset = gsl::narrow<SHORT>(ViewStartIndex());
// we need to set the _selectionPivot again
// for future shift+clicks
_selection->pivot = _selection->start;
// scan rightwards until delimiter is found and
// set endSelectionPosition to one left of that spot
_endSelectionPosition = _ExpandDoubleClickSelectionRight(positionWithOffsets);
THROW_IF_FAILED(ShortSub(_endSelectionPosition.Y, gsl::narrow<SHORT>(ViewStartIndex()), &_endSelectionPosition.Y));
_selectionActive = true;
_multiClickSelectionMode = SelectionExpansionMode::Word;
}
// Method Description:
// - Select the entire row of the position clicked
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
void Terminal::TripleClickSelection(const COORD position)
{
SetSelectionAnchor({ 0, position.Y });
SetEndSelectionPosition({ _buffer->GetSize().RightInclusive(), position.Y });
_multiClickSelectionMode = SelectionExpansionMode::Line;
}
// Method Description:
// - Record the position of the beginning of a selection
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
void Terminal::SetSelectionAnchor(const COORD viewportPos)
void Terminal::SetSelectionAnchor(const COORD position)
{
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
_selectionAnchor = position;
// include _scrollOffset here to ensure this maps to the right spot of the original viewport
THROW_IF_FAILED(ShortSub(_selectionAnchor.Y, gsl::narrow<SHORT>(_scrollOffset), &_selectionAnchor.Y));
// copy value of ViewStartIndex to support scrolling
// and update on new buffer output (used in _GetSelectionRects())
_selectionVerticalOffset = gsl::narrow<SHORT>(ViewStartIndex());
_selectionActive = true;
_allowSingleCharSelection = (_copyOnSelect) ? false : true;
_multiClickSelectionMode = SelectionExpansionMode::Cell;
SetSelectionEnd(viewportPos);
SetEndSelectionPosition(position);
_selection->start = _selection->pivot;
_multiClickSelectionMode = SelectionExpansionMode::Cell;
}
// Method Description:
// - Update selection anchors when dragging to a position
// - based on the selection expansion mode
// - Record the position of the end of a selection
// Arguments:
// - viewportPos: the (x,y) coordinate on the visible viewport
// - newExpansionMode: overwrites the _multiClickSelectionMode for this function call. Used for ShiftClick
void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionExpansionMode> newExpansionMode)
// - position: the (x,y) coordinate on the visible viewport
void Terminal::SetEndSelectionPosition(const COORD position)
{
const auto textBufferPos = _ConvertToBufferCell(viewportPos);
_endSelectionPosition = position;
// if this is a shiftClick action, we need to overwrite the _multiClickSelectionMode value (even if it's the same)
// Otherwise, we may accidentally expand during other selection-based actions
_multiClickSelectionMode = newExpansionMode.has_value() ? *newExpansionMode : _multiClickSelectionMode;
// include _scrollOffset here to ensure this maps to the right spot of the original viewport
THROW_IF_FAILED(ShortSub(_endSelectionPosition.Y, gsl::narrow<SHORT>(_scrollOffset), &_endSelectionPosition.Y));
const auto anchors = _PivotSelection(textBufferPos);
std::tie(_selection->start, _selection->end) = _ExpandSelectionAnchors(anchors);
// copy value of ViewStartIndex to support scrolling
// and update on new buffer output (used in _GetSelectionRects())
_selectionVerticalOffset = gsl::narrow<SHORT>(ViewStartIndex());
// moving the endpoint of what used to be a single cell selection
// allows the user to drag back and select just one cell
if (_copyOnSelect && !_IsSingleCellSelection())
{
_allowSingleCharSelection = true;
@@ -177,94 +315,78 @@ void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionE
}
// Method Description:
// - returns a new pair of selection anchors for selecting around the pivot
// - This ensures start < end when compared
// - enable/disable box selection (ALT + selection)
// Arguments:
// - targetPos: the (x,y) coordinate we are moving to on the text buffer
// Return Value:
// - the new start/end for a selection
std::pair<COORD, COORD> Terminal::_PivotSelection(const COORD targetPos) const
// - isEnabled: new value for _boxSelection
void Terminal::SetBoxSelection(const bool isEnabled) noexcept
{
if (_buffer->GetSize().CompareInBounds(targetPos, _selection->pivot) <= 0)
{
// target is before pivot
// treat target as start
return std::make_pair(targetPos, _selection->pivot);
}
else
{
// target is after pivot
// treat pivot as start
return std::make_pair(_selection->pivot, targetPos);
}
}
// Method Description:
// - Update the selection anchors to expand according to the expansion mode
// Arguments:
// - anchors: a pair of selection anchors representing a desired selection
// Return Value:
// - the new start/end for a selection
std::pair<COORD, COORD> Terminal::_ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const
{
COORD start = anchors.first;
COORD end = anchors.second;
const auto bufferSize = _buffer->GetSize();
switch (_multiClickSelectionMode)
{
case SelectionExpansionMode::Line:
start = { bufferSize.Left(), start.Y };
end = { bufferSize.RightInclusive(), end.Y };
break;
case SelectionExpansionMode::Word:
start = _buffer->GetWordStart(start, _wordDelimiters);
end = _buffer->GetWordEnd(end, _wordDelimiters);
break;
case SelectionExpansionMode::Cell:
default:
// no expansion is necessary
break;
}
return std::make_pair(start, end);
}
// Method Description:
// - enable/disable block selection (ALT + selection)
// Arguments:
// - isEnabled: new value for _blockSelection
void Terminal::SetBlockSelection(const bool isEnabled) noexcept
{
_blockSelection = isEnabled;
_boxSelection = isEnabled;
}
// Method Description:
// - clear selection data and disable rendering it
#pragma warning(disable : 26440) // changing this to noexcept would require a change to ConHost's selection model
void Terminal::ClearSelection()
{
_selectionActive = false;
_allowSingleCharSelection = false;
_selection = std::nullopt;
_selectionAnchor = { 0, 0 };
_endSelectionPosition = { 0, 0 };
_selectionVerticalOffset = 0;
_buffer->GetRenderTarget().TriggerSelection();
}
// Method Description:
// - get wstring text from highlighted portion of text buffer
// Arguments:
// - collapseText: collapse all of the text to one line
// - trimTrailingWhitespace: enable removing any whitespace from copied selection
// and get text to appear on separate lines.
// Return Value:
// - wstring text from buffer. If extended to multiple lines, each line is separated by \r\n
const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool collapseText) const
const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const
{
const auto selectionRects = _GetSelectionRects();
std::function<COLORREF(TextAttribute&)> GetForegroundColor = std::bind(&Terminal::GetForegroundColor, this, std::placeholders::_1);
std::function<COLORREF(TextAttribute&)> GetBackgroundColor = std::bind(&Terminal::GetBackgroundColor, this, std::placeholders::_1);
return _buffer->GetText(!collapseText,
!collapseText,
selectionRects,
GetForegroundColor,
GetBackgroundColor);
return _buffer->GetTextForClipboard(!_boxSelection,
trimTrailingWhitespace,
_GetSelectionRects(),
GetForegroundColor,
GetBackgroundColor);
}
// Method Description:
// - expand the double click selection to the left
// - stopped by delimiter if started on delimiter
// Arguments:
// - position: buffer coordinate for selection
// Return Value:
// - updated copy of "position" to new expanded location (with vertical offset)
COORD Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) const
{
// force position to be within bounds
#pragma warning(suppress : 26496) // cpp core checks wants this const but .Clamp() can write it.
COORD positionWithOffsets = position;
_buffer->GetSize().Clamp(positionWithOffsets);
return _buffer->GetWordStart(positionWithOffsets, _wordDelimiters);
}
// Method Description:
// - expand the double click selection to the right
// - stopped by delimiter if started on delimiter
// Arguments:
// - position: buffer coordinate for selection
// Return Value:
// - updated copy of "position" to new expanded location (with vertical offset)
COORD Terminal::_ExpandDoubleClickSelectionRight(const COORD position) const
{
// force position to be within bounds
#pragma warning(suppress : 26496) // cpp core checks wants this const but .Clamp() can write it.
COORD positionWithOffsets = position;
_buffer->GetSize().Clamp(positionWithOffsets);
return _buffer->GetWordEnd(positionWithOffsets, _wordDelimiters);
}
// Method Description:
@@ -275,10 +397,13 @@ const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool col
// - the corresponding location on the buffer
COORD Terminal::_ConvertToBufferCell(const COORD viewportPos) const
{
const auto yPos = base::ClampedNumeric<short>(_VisibleStartIndex()) + viewportPos.Y;
COORD bufferPos = { viewportPos.X, yPos };
_buffer->GetSize().Clamp(bufferPos);
return bufferPos;
// Force position to be valid
COORD positionWithOffsets = viewportPos;
_buffer->GetSize().Clamp(positionWithOffsets);
THROW_IF_FAILED(ShortSub(viewportPos.Y, gsl::narrow<SHORT>(_scrollOffset), &positionWithOffsets.Y));
THROW_IF_FAILED(ShortAdd(positionWithOffsets.Y, gsl::narrow<SHORT>(ViewStartIndex()), &positionWithOffsets.Y));
return positionWithOffsets;
}
// Method Description:

View File

@@ -173,7 +173,8 @@ void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
realCoordEnd.Y -= gsl::narrow<short>(_VisibleStartIndex());
SetSelectionAnchor(realCoordStart);
SetSelectionEnd(realCoordEnd, SelectionExpansionMode::Cell);
SetEndSelectionPosition(realCoordEnd);
_buffer->GetRenderTarget().TriggerSelection();
}
const std::wstring Terminal::GetConsoleTitle() const noexcept

View File

@@ -12,13 +12,6 @@ namespace Microsoft.Terminal.Settings
Hidden
};
enum TextAntialiasingMode
{
Grayscale = 0,
Cleartype,
Aliased
};
// Class Description:
// TerminalSettings encapsulates all settings that control the
// TermControl's behavior. In these settings there is both the entirety
@@ -26,8 +19,6 @@ namespace Microsoft.Terminal.Settings
// for specifically the control.
interface IControlSettings requires Microsoft.Terminal.Settings.ICoreSettings
{
String ProfileName;
Boolean UseAcrylic;
Double TintOpacity;
ScrollbarState ScrollState;
@@ -50,7 +41,5 @@ namespace Microsoft.Terminal.Settings
UInt32 SelectionBackground;
Boolean RetroTerminalEffect;
TextAntialiasingMode AntialiasingMode;
};
}

View File

@@ -28,7 +28,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_cursorHeight{ DEFAULT_CURSOR_HEIGHT },
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
_copyOnSelect{ false },
_profileName{},
_useAcrylic{ false },
_tintOpacity{ 0.5 },
_padding{ DEFAULT_PADDING },
@@ -40,9 +39,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_backgroundImageHorizontalAlignment{ winrt::Windows::UI::Xaml::HorizontalAlignment::Center },
_backgroundImageVerticalAlignment{ winrt::Windows::UI::Xaml::VerticalAlignment::Center },
_keyBindings{ nullptr },
_scrollbarState{ ScrollbarState::Visible },
_antialiasingMode{ TextAntialiasingMode::Grayscale }
_scrollbarState{ ScrollbarState::Visible }
{
}
@@ -197,16 +194,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_copyOnSelect = value;
}
void TerminalSettings::ProfileName(hstring const& value)
{
_profileName = value;
}
hstring TerminalSettings::ProfileName()
{
return _profileName;
}
bool TerminalSettings::UseAcrylic() noexcept
{
return _useAcrylic;
@@ -387,14 +374,4 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_retroTerminalEffect = value;
}
Settings::TextAntialiasingMode TerminalSettings::AntialiasingMode() const noexcept
{
return _antialiasingMode;
}
void TerminalSettings::AntialiasingMode(const Settings::TextAntialiasingMode& value) noexcept
{
_antialiasingMode = value;
}
}

View File

@@ -55,8 +55,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
void CopyOnSelect(bool value) noexcept;
// ------------------------ End of Core Settings -----------------------
hstring ProfileName();
void ProfileName(hstring const& value);
bool UseAcrylic() noexcept;
void UseAcrylic(bool value) noexcept;
double TintOpacity() noexcept;
@@ -104,9 +102,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
bool RetroTerminalEffect() noexcept;
void RetroTerminalEffect(bool value) noexcept;
TextAntialiasingMode AntialiasingMode() const noexcept;
void AntialiasingMode(winrt::Microsoft::Terminal::Settings::TextAntialiasingMode const& value) noexcept;
private:
uint32_t _defaultForeground;
uint32_t _defaultBackground;
@@ -122,7 +117,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
uint32_t _cursorHeight;
hstring _wordDelimiters;
hstring _profileName;
bool _useAcrylic;
double _tintOpacity;
hstring _fontFace;
@@ -143,8 +137,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
Settings::ScrollbarState _scrollbarState;
bool _retroTerminalEffect;
Settings::TextAntialiasingMode _antialiasingMode;
};
}

View File

@@ -44,7 +44,7 @@ using namespace Microsoft::Terminal::Core;
namespace TerminalCoreUnitTests
{
class ConptyRoundtripTests;
class TerminalBufferTests;
};
using namespace TerminalCoreUnitTests;
@@ -152,14 +152,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
TEST_METHOD(SimpleWriteOutputTest);
TEST_METHOD(WriteTwoLinesUsesNewline);
TEST_METHOD(WriteAFewSimpleLines);
TEST_METHOD(PassthroughClearScrollback);
TEST_METHOD(TestWrappingALongString);
TEST_METHOD(TestAdvancedWrapping);
TEST_METHOD(TestExactWrappingWithoutSpaces);
TEST_METHOD(TestExactWrappingWithSpaces);
TEST_METHOD(MoveCursorAtEOL);
private:
@@ -354,262 +348,6 @@ void ConptyRoundtripTests::WriteAFewSimpleLines()
verifyData(termTb);
}
void ConptyRoundtripTests::TestWrappingALongString()
{
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
_flushFirstFrame();
_checkConptyOutput = false;
const auto initialTermView = term->GetViewport();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(100, charsToWrite);
VERIFY_ARE_EQUAL(0, initialTermView.Top());
VERIFY_ARE_EQUAL(32, initialTermView.BottomExclusive());
hostSm.ProcessString(TestUtils::Test100CharsString);
const auto secondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());
auto verifyBuffer = [&](const TextBuffer& tb) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(charsToWrite % initialTermView.Width(), cursor.GetPosition().X);
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, TestUtils::Test100CharsString, { 0, 0 });
};
verifyBuffer(hostTb);
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb);
}
void ConptyRoundtripTests::TestAdvancedWrapping()
{
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
_flushFirstFrame();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(100, charsToWrite);
hostSm.ProcessString(TestUtils::Test100CharsString);
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L" ");
hostSm.ProcessString(L"1234567890");
auto verifyBuffer = [&](const TextBuffer& tb) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(2, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(20, cursor.GetPosition().X);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, TestUtils::Test100CharsString, { 0, 0 });
TestUtils::VerifyExpectedString(tb, L" 1234567890", { 0, 2 });
};
verifyBuffer(hostTb);
// First write the first 80 characters from the string
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
// Without line breaking, write the remaining 20 chars
expectedOutput.push_back(R"(qrstuvwxyz{|}~!"#$%&)");
// Clear the rest of row 1
expectedOutput.push_back("\x1b[K");
// This is the hard line break
expectedOutput.push_back("\r\n");
// Now write row 2 of the buffer
expectedOutput.push_back(" 1234567890");
// and clear everything after the text, because the buffer is empty.
expectedOutput.push_back("\x1b[K");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb);
}
void ConptyRoundtripTests::TestExactWrappingWithoutSpaces()
{
// This test (and TestExactWrappingWitSpaces) reveals a bug in the old
// implementation.
//
// If a line _exactly_ wraps to the next line, we can't tell if the line
// should really wrap, or manually break. The client app is writing a line
// that's exactly the width of the buffer that manually linebreaked at the
// end of the line, followed by another line.
//
// With the old PaintBufferLine interface, there's no way to know if this
// case is because the line wrapped or not. Hence, the addition of the
// `lineWrapped` parameter
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
_flushFirstFrame();
const auto charsToWrite = initialTermView.Width();
VERIFY_ARE_EQUAL(80, charsToWrite);
for (auto i = 0; i < charsToWrite; i++)
{
// This is a handy way of just printing the printable characters that
// _aren't_ the space character.
const wchar_t wch = static_cast<wchar_t>(33 + (i % 94));
hostSm.ProcessCharacter(wch);
}
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L"1234567890");
auto verifyBuffer = [&](const TextBuffer& tb, const bool isTerminal) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(10, cursor.GetPosition().X);
// TODO: GH#780 - In the Terminal, neither line should be wrapped.
// Unfortunately, until WriteCharsLegacy2ElectricBoogaloo is complete,
// the Terminal will still treat the first line as wrapped. When #780 is
// implemented, these tests will fail, and should again expect the first
// line to not be wrapped.
// Verify that we marked the 0th row as _not wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_ARE_EQUAL(isTerminal, row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, LR"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)", { 0, 0 });
TestUtils::VerifyExpectedString(tb, L"1234567890", { 0, 1 });
};
verifyBuffer(hostTb, false);
// First write the first 80 characters from the string
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
// This is the hard line break
expectedOutput.push_back("\r\n");
// Now write row 2 of the buffer
expectedOutput.push_back("1234567890");
// and clear everything after the text, because the buffer is empty.
expectedOutput.push_back("\x1b[K");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb, true);
}
void ConptyRoundtripTests::TestExactWrappingWithSpaces()
{
// This test is also explained by the comment at the top of TestExactWrappingWithoutSpaces
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
_flushFirstFrame();
const auto charsToWrite = initialTermView.Width();
VERIFY_ARE_EQUAL(80, charsToWrite);
for (auto i = 0; i < charsToWrite; i++)
{
// This is a handy way of just printing the printable characters that
// _aren't_ the space character.
const wchar_t wch = static_cast<wchar_t>(33 + (i % 94));
hostSm.ProcessCharacter(wch);
}
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L" ");
hostSm.ProcessString(L"1234567890");
auto verifyBuffer = [&](const TextBuffer& tb, const bool isTerminal) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(20, cursor.GetPosition().X);
// TODO: GH#780 - In the Terminal, neither line should be wrapped.
// Unfortunately, until WriteCharsLegacy2ElectricBoogaloo is complete,
// the Terminal will still treat the first line as wrapped. When #780 is
// implemented, these tests will fail, and should again expect the first
// line to not be wrapped.
// Verify that we marked the 0th row as _not wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_ARE_EQUAL(isTerminal, row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, LR"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)", { 0, 0 });
TestUtils::VerifyExpectedString(tb, L" 1234567890", { 0, 1 });
};
verifyBuffer(hostTb, false);
// First write the first 80 characters from the string
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
// This is the hard line break
expectedOutput.push_back("\r\n");
// Now write row 2 of the buffer
expectedOutput.push_back(" 1234567890");
// and clear everything after the text, because the buffer is empty.
expectedOutput.push_back("\x1b[K");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb, true);
}
void ConptyRoundtripTests::MoveCursorAtEOL()
{
// This is a test for GH#1245
@@ -622,6 +360,7 @@ void ConptyRoundtripTests::MoveCursorAtEOL()
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
_flushFirstFrame();
Log::Comment(NoThrowString().Format(

View File

@@ -72,7 +72,7 @@ namespace TerminalCoreUnitTests
term.SetSelectionAnchor({ 5, rowValue });
// Simulate move to (x,y) = (15,20)
term.SetSelectionEnd({ 15, 20 });
term.SetEndSelectionPosition({ 15, 20 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -110,19 +110,19 @@ namespace TerminalCoreUnitTests
{
const COORD maxCoord = { SHRT_MAX, SHRT_MAX };
// Test SetSelectionAnchor(COORD) and SetSelectionEnd(COORD)
// Test SetSelectionAnchor(COORD) and SetEndSelectionPosition(COORD)
// Behavior: clamp coord to viewport.
auto ValidateSingleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) {
Terminal term;
DummyRenderTarget emptyRT;
term.Create({ 10, 10 }, scrollback, emptyRT);
// NOTE: SetSelectionEnd(COORD) is called within SetSelectionAnchor(COORD)
// NOTE: SetEndSelectionPosition(COORD) is called within SetSelectionAnchor(COORD)
term.SetSelectionAnchor(maxCoord);
ValidateSingleRowSelection(term, expected);
};
// Test a Double Click Selection
// Test DoubleClickSelection(COORD)
// Behavior: clamp coord to viewport.
// Then, do double click selection.
auto ValidateDoubleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) {
@@ -130,11 +130,11 @@ namespace TerminalCoreUnitTests
DummyRenderTarget emptyRT;
term.Create({ 10, 10 }, scrollback, emptyRT);
term.MultiClickSelection(maxCoord, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(maxCoord);
ValidateSingleRowSelection(term, expected);
};
// Test a Triple Click Selection
// Test TripleClickSelection(COORD)
// Behavior: clamp coord to viewport.
// Then, do triple click selection.
auto ValidateTripleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) {
@@ -142,7 +142,7 @@ namespace TerminalCoreUnitTests
DummyRenderTarget emptyRT;
term.Create({ 10, 10 }, scrollback, emptyRT);
term.MultiClickSelection(maxCoord, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(maxCoord);
ValidateSingleRowSelection(term, expected);
};
@@ -226,17 +226,17 @@ namespace TerminalCoreUnitTests
// Case 1: Move out of right boundary
Log::Comment(L"Out of bounds: X-value too large");
term.SetSelectionEnd({ 20, 5 });
term.SetEndSelectionPosition({ 20, 5 });
ValidateSingleRowSelection(term, SMALL_RECT({ 5, 5, rightBoundary, 5 }));
// Case 2: Move out of left boundary
Log::Comment(L"Out of bounds: X-value negative");
term.SetSelectionEnd({ -20, 5 });
term.SetEndSelectionPosition({ -20, 5 });
ValidateSingleRowSelection(term, { leftBoundary, 5, 5, 5 });
// Case 3: Move out of top boundary
Log::Comment(L"Out of bounds: Y-value negative");
term.SetSelectionEnd({ 5, -20 });
term.SetEndSelectionPosition({ 5, -20 });
{
auto selectionRects = term.GetSelectionRects();
@@ -267,7 +267,7 @@ namespace TerminalCoreUnitTests
// Case 4: Move out of bottom boundary
Log::Comment(L"Out of bounds: Y-value too large");
term.SetSelectionEnd({ 5, 20 });
term.SetEndSelectionPosition({ 5, 20 });
{
auto selectionRects = term.GetSelectionRects();
@@ -310,10 +310,10 @@ namespace TerminalCoreUnitTests
// Simulate ALT + click at (x,y) = (5,10)
term.SetSelectionAnchor({ 5, rowValue });
term.SetBlockSelection(true);
term.SetBoxSelection(true);
// Simulate move to (x,y) = (15,20)
term.SetSelectionEnd({ 15, 20 });
term.SetEndSelectionPosition({ 15, 20 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -349,7 +349,7 @@ namespace TerminalCoreUnitTests
term.SetSelectionAnchor({ 5, rowValue });
// Simulate move to (x,y) = (15,20)
term.SetSelectionEnd({ 15, 20 });
term.SetEndSelectionPosition({ 15, 20 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -449,10 +449,10 @@ namespace TerminalCoreUnitTests
// Simulate ALT + click at (x,y) = (5,8)
term.SetSelectionAnchor({ 5, 8 });
term.SetBlockSelection(true);
term.SetBoxSelection(true);
// Simulate move to (x,y) = (7,12)
term.SetSelectionEnd({ 7, 12 });
term.SetEndSelectionPosition({ 7, 12 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -501,7 +501,7 @@ namespace TerminalCoreUnitTests
// Simulate double click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(clickPos);
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 4, 10, (4 + gsl::narrow<SHORT>(text.size()) - 1), 10 }));
@@ -519,7 +519,7 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(clickPos);
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -546,7 +546,7 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (15,10)
// this is over the '>' char
auto clickPos = COORD{ 15, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(clickPos);
// ---Validate selection area---
// "Terminal" is in class 2
@@ -572,14 +572,14 @@ namespace TerminalCoreUnitTests
term.Write(text);
// Simulate double click at (x,y) = (5,10)
term.MultiClickSelection({ 5, 10 }, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection({ 5, 10 });
// Simulate move to (x,y) = (21,10)
//
// buffer: doubleClickMe dragThroughHere
// ^ ^
// start finish
term.SetSelectionEnd({ 21, 10 });
term.SetEndSelectionPosition({ 21, 10 });
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 4, 10, 32, 10 }));
@@ -601,14 +601,14 @@ namespace TerminalCoreUnitTests
term.Write(text);
// Simulate double click at (x,y) = (21,10)
term.MultiClickSelection({ 21, 10 }, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection({ 21, 10 });
// Simulate move to (x,y) = (5,10)
//
// buffer: doubleClickMe dragThroughHere
// ^ ^
// finish start
term.SetSelectionEnd({ 5, 10 });
term.SetEndSelectionPosition({ 5, 10 });
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 4, 10, 32, 10 }));
@@ -622,7 +622,7 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(clickPos);
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 0, 10, 99, 10 }));
@@ -636,10 +636,10 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(clickPos);
// Simulate move to (x,y) = (7,10)
term.SetSelectionEnd({ 7, 10 });
term.SetEndSelectionPosition({ 7, 10 });
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 0, 10, 99, 10 }));
@@ -653,10 +653,10 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(clickPos);
// Simulate move to (x,y) = (5,11)
term.SetSelectionEnd({ 5, 11 });
term.SetEndSelectionPosition({ 5, 11 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -689,7 +689,7 @@ namespace TerminalCoreUnitTests
// Simulate move to (x,y) = (5,10)
// (So, no movement)
term.SetSelectionEnd({ 5, 10 });
term.SetEndSelectionPosition({ 5, 10 });
// Case 1: single cell selection not allowed
{
@@ -705,12 +705,12 @@ namespace TerminalCoreUnitTests
}
// Case 2: move off of single cell
term.SetSelectionEnd({ 6, 10 });
term.SetEndSelectionPosition({ 6, 10 });
ValidateSingleRowSelection(term, { 5, 10, 6, 10 });
VERIFY_IS_TRUE(term.IsSelectionActive());
// Case 3: move back onto single cell (now allowed)
term.SetSelectionEnd({ 5, 10 });
term.SetEndSelectionPosition({ 5, 10 });
ValidateSingleRowSelection(term, { 5, 10, 5, 10 });
// single cell selection should now be allowed

View File

@@ -29,9 +29,6 @@ class TerminalCoreUnitTests::TerminalBufferTests final
TEST_METHOD(TestSimpleBufferWriting);
TEST_METHOD(TestWrappingCharByChar);
TEST_METHOD(TestWrappingALongString);
TEST_METHOD_SETUP(MethodSetup)
{
// STEP 1: Set up the Terminal
@@ -69,76 +66,3 @@ void TerminalBufferTests::TestSimpleBufferWriting()
TestUtils::VerifyExpectedString(termTb, L"Hello World", { 0, 0 });
}
void TerminalBufferTests::TestWrappingCharByChar()
{
auto& termTb = *term->_buffer;
auto& termSm = *term->_stateMachine;
const auto initialView = term->GetViewport();
auto& cursor = termTb.GetCursor();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(0, initialView.Top());
VERIFY_ARE_EQUAL(32, initialView.BottomExclusive());
for (auto i = 0; i < charsToWrite; i++)
{
// This is a handy way of just printing the printable characters that
// _aren't_ the space character.
const wchar_t wch = static_cast<wchar_t>(33 + (i % 94));
termSm.ProcessCharacter(wch);
}
const auto secondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(charsToWrite % initialView.Width(), cursor.GetPosition().X);
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = termTb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = termTb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(termTb, TestUtils::Test100CharsString, { 0, 0 });
}
void TerminalBufferTests::TestWrappingALongString()
{
auto& termTb = *term->_buffer;
auto& termSm = *term->_stateMachine;
const auto initialView = term->GetViewport();
auto& cursor = termTb.GetCursor();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(100, charsToWrite);
VERIFY_ARE_EQUAL(0, initialView.Top());
VERIFY_ARE_EQUAL(32, initialView.BottomExclusive());
termSm.ProcessString(TestUtils::Test100CharsString);
const auto secondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(charsToWrite % initialView.Width(), cursor.GetPosition().X);
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = termTb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = termTb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(termTb, TestUtils::Test100CharsString, { 0, 0 });
}

View File

@@ -21,10 +21,6 @@ namespace TerminalCoreUnitTests
class TerminalCoreUnitTests::TestUtils
{
public:
static constexpr std::wstring_view Test100CharsString{
LR"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&)"
};
// Function Description:
// - Helper function to validate that a number of characters in a row are all
// the same. Validates that the next end-start characters are all equal to the

View File

@@ -28,10 +28,10 @@ inline winrt::Windows::UI::Color ColorRefToColor(const COLORREF& colorref)
// - Rect scaled by scale
inline winrt::Windows::Foundation::Rect ScaleRect(winrt::Windows::Foundation::Rect rect, double scale)
{
const auto scaleLocal = base::ClampedNumeric<float>(scale);
rect.X = base::ClampMul(rect.X, scaleLocal);
rect.Y = base::ClampMul(rect.Y, scaleLocal);
rect.Width = base::ClampMul(rect.Width, scaleLocal);
rect.Height = base::ClampMul(rect.Height, scaleLocal);
const float scaleLocal = gsl::narrow_cast<float>(scale);
rect.X *= scaleLocal;
rect.Y *= scaleLocal;
rect.Width *= scaleLocal;
rect.Height *= scaleLocal;
return rect;
}

View File

@@ -63,15 +63,6 @@ AppHost::~AppHost()
_app = nullptr;
}
bool AppHost::OnF7Pressed()
{
if (_logic)
{
return _logic.OnF7Pressed();
}
return false;
}
// Method Description:
// - Retrieve any commandline args passed on the commandline, and pass them to
// the app logic for processing.

View File

@@ -17,7 +17,6 @@ public:
void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
void Initialize();
bool OnF7Pressed();
private:
bool _useNonClientArea;

View File

@@ -7,7 +7,6 @@
#include "NonClientIslandWindow.h"
#include "../types/inc/ThemeUtils.h"
#include "../types/inc/utils.hpp"
#include "TerminalThemeHelpers.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -110,16 +109,6 @@ void NonClientIslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement conte
void NonClientIslandWindow::SetTitlebarContent(winrt::Windows::UI::Xaml::UIElement content)
{
_titlebar.Content(content);
// GH#4288 - add a SizeChanged handler to this content. It's possible that
// this element's size will change after the dragbar's. When that happens,
// the drag bar won't send another SizeChanged event, because the dragbar's
// _size_ didn't change, only it's position.
const auto fwe = content.try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
if (fwe)
{
fwe.SizeChanged({ this, &NonClientIslandWindow::_OnDragBarSizeChanged });
}
}
// Method Description:
@@ -490,20 +479,12 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
{
switch (message)
{
case WM_DISPLAYCHANGE:
// GH#4166: When the DPI of the monitor changes out from underneath us,
// resize our drag bar, to reflect its newly scaled size.
_UpdateIslandRegion();
return 0;
case WM_NCCALCSIZE:
return _OnNcCalcSize(wParam, lParam);
case WM_NCHITTEST:
return _OnNcHitTest({ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) });
case WM_PAINT:
return _OnPaint();
case WM_ACTIVATE:
// If we do this every time we're activated, it should be close enough to correct.
TerminalTrySetDarkTheme(_window.get());
}
return IslandWindow::MessageHandler(message, wParam, lParam);

View File

@@ -112,7 +112,6 @@
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets'))" />
</Target>
<!-- Override GetPackagingOutputs to roll up all our dependencies.
@@ -143,5 +142,4 @@
</Target>
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.1.200305005\build\native\Terminal.ThemeHelpers.targets')" />
</Project>
</Project>

View File

@@ -73,11 +73,6 @@ static void EnsureNativeArchitecture()
}
}
static bool _messageIsF7Keypress(const MSG& message)
{
return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7;
}
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
TraceLoggingRegister(g_hWindowsTerminalProvider);
@@ -120,23 +115,6 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
while (GetMessage(&message, nullptr, 0, 0))
{
// GH#638 (Pressing F7 brings up both the history AND a caret browsing message)
// The Xaml input stack doesn't allow an application to suppress the "caret browsing"
// dialog experience triggered when you press F7. Official recommendation from the Xaml
// team is to catch F7 before we hand it off.
// AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes
// implementing a custom IF7Listener interface.
// If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact,
// been handled we can discard the message before we even translate it.
if (_messageIsF7Keypress(message))
{
if (host.OnF7Pressed())
{
// The application consumed the F7. Don't let Xaml get it.
continue;
}
}
TranslateMessage(&message);
DispatchMessage(&message);
}

View File

@@ -4,5 +4,4 @@
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.0.0" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.3.191217003-prerelease" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.1-rc" targetFramework="native" />
<package id="Terminal.ThemeHelpers" version="0.1.200305005" targetFramework="native" />
</packages>
</packages>

View File

@@ -7,7 +7,6 @@ namespace Microsoft.Terminal.Wpf
{
using System;
using System.Runtime.InteropServices;
using System.Windows.Automation.Provider;
#pragma warning disable SA1600 // Elements should be documented
internal static class NativeMethods
@@ -37,8 +36,6 @@ namespace Microsoft.Terminal.Wpf
/// </summary>
WM_MOUSEACTIVATE = 0x0021,
WM_GETOBJECT = 0x003D,
/// <summary>
/// The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function.
/// </summary>

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