mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 06:09:50 +00:00
Compare commits
87 Commits
v0.4.2382.
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37af3f00fe | ||
|
|
00bf9f6757 | ||
|
|
3a89c6e5fa | ||
|
|
537258a60f | ||
|
|
b5fe4ffd54 | ||
|
|
12d2e170dd | ||
|
|
2da3b49c9e | ||
|
|
2063197605 | ||
|
|
2ac24979da | ||
|
|
429af0e6fa | ||
|
|
18bacfe973 | ||
|
|
bac69f7cab | ||
|
|
ce34c7320c | ||
|
|
badbbc43a4 | ||
|
|
fecddafad5 | ||
|
|
d8ff47a0d3 | ||
|
|
125e1771ae | ||
|
|
c58033cda2 | ||
|
|
fc81adf32f | ||
|
|
689c21e802 | ||
|
|
96cc7727bc | ||
|
|
886d018bb4 | ||
|
|
d0c207bc9c | ||
|
|
ce3028e12f | ||
|
|
b7c1e05060 | ||
|
|
3bff2a3eb0 | ||
|
|
7c66e66ca1 | ||
|
|
e0762f6bb3 | ||
|
|
51f53535d1 | ||
|
|
21067a7629 | ||
|
|
7d9534bfa8 | ||
|
|
6735311fc9 | ||
|
|
4204733c34 | ||
|
|
23b4a466f5 | ||
|
|
01bd77003c | ||
|
|
ae25a32913 | ||
|
|
93aa9455e2 | ||
|
|
41f209f6d3 | ||
|
|
244fb72fee | ||
|
|
3a0da64276 | ||
|
|
b2c093fa2f | ||
|
|
87f5852a72 | ||
|
|
e14a59a1b6 | ||
|
|
cd144e98c6 | ||
|
|
c7f0a3439d | ||
|
|
5d60d69e86 | ||
|
|
072bbfd09d | ||
|
|
b87f8f9070 | ||
|
|
b78d9176ae | ||
|
|
3bbd8f4c97 | ||
|
|
2d3f285894 | ||
|
|
49ff36bfc3 | ||
|
|
d8bc94f13c | ||
|
|
dd49c3ed51 | ||
|
|
9678dd894c | ||
|
|
45e599368f | ||
|
|
594dca993b | ||
|
|
c956913a28 | ||
|
|
b180406b07 | ||
|
|
bbdfdf91eb | ||
|
|
d5d7cf420d | ||
|
|
81ab5803aa | ||
|
|
7d4096bbbf | ||
|
|
230e7f43e0 | ||
|
|
cdfbf8f106 | ||
|
|
30e8e7f3a3 | ||
|
|
feb5b18296 | ||
|
|
7ec6bfc01c | ||
|
|
4f1157c044 | ||
|
|
8c3a629b52 | ||
|
|
8579d8905a | ||
|
|
50e2d0c433 | ||
|
|
8ea7401dc9 | ||
|
|
a381f6a042 | ||
|
|
c63289b114 | ||
|
|
b33a59816e | ||
|
|
bd2d5ddb4b | ||
|
|
23897b1bd4 | ||
|
|
65dec36cb1 | ||
|
|
1989eb9d00 | ||
|
|
cb02ca7534 | ||
|
|
5de63096ac | ||
|
|
f93adb9540 | ||
|
|
0d12a25b2d | ||
|
|
5e38bcd754 | ||
|
|
f4294b17d7 | ||
|
|
974e95ebf7 |
@@ -248,6 +248,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "s
|
||||
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty", "src\winconpty\winconpty.vcxproj", "{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|ARM64 = AuditMode|ARM64
|
||||
@@ -1029,6 +1031,21 @@ Global
|
||||
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.Build.0 = Release|x64
|
||||
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.Build.0 = Release|Win32
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|ARM64.ActiveCfg = Release|ARM64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.Build.0 = Debug|x64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.Build.0 = Debug|Win32
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.ActiveCfg = Release|x64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.Build.0 = Release|x64
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.ActiveCfg = Release|Win32
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -1090,6 +1107,7 @@ Global
|
||||
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
||||
41
SECURITY.md
Normal file
41
SECURITY.md
Normal file
@@ -0,0 +1,41 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.2 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
@@ -14,6 +14,7 @@
|
||||
"/.vs/",
|
||||
"/build/",
|
||||
"/src/cascadia/",
|
||||
"/src/winconpty/",
|
||||
"/.nuget/",
|
||||
"/.github/",
|
||||
"/samples/"
|
||||
|
||||
21
doc/bot.md
21
doc/bot.md
@@ -33,6 +33,17 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
|
||||
## Rules
|
||||
|
||||
### Triage Shorthand
|
||||
- All rules in this category apply to triaging issues. They're shorthand comments that the triage team can use in order to complete the triage process faster.
|
||||
- Only individuals with `Write` or `Admin` privileges on the repository can use these responses.
|
||||
|
||||
#### Duplicate Issues
|
||||
- When a comment on the thread says `/dup #<issue ID>`...
|
||||
1. Reply with a comment explaining that the issue is a duplicate and recommend that the opener and interested parties follow the issue on the listed ID number.
|
||||
1. Close the issue
|
||||
1. Remove all `Needs-*` tags
|
||||
1. Add `Resolution-Duplicate`
|
||||
|
||||
### Issue Management
|
||||
|
||||
#### Mark as Triage Needed
|
||||
@@ -65,10 +76,12 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
- Then close the issue automatically informing the opener that they can resolve the problem and reopen the issue. (See Bug/Feature templates for example situations.)
|
||||
|
||||
#### Help ask for Feedback Hub
|
||||
- If an issue is tagged `Needs-Feedback-Hub`
|
||||
- Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link.
|
||||
- And remove the `Needs-Feedback-Hub` tag
|
||||
- And add the `Needs-Author-Feedback` tag
|
||||
- When a comment on the thread says `/feedback`...
|
||||
1. Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link.
|
||||
1. And add the `Needs-Author-Feedback` tag
|
||||
|
||||
#### Remove Help Wanted from In PR issues
|
||||
- If an issue gets the `In-PR` tag when a new PR is created, we will remove the `Help-Wanted` tag to avoid someone trying to work on an issue where another person has already submitted a proposed fix.
|
||||
|
||||
### PR Management
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
# How to build Openconsole
|
||||
|
||||
Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and powershell in /tools.
|
||||
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.
|
||||
|
||||
@@ -33,4 +33,4 @@ Openconsole has three configuration types:
|
||||
- Release
|
||||
- AuditMode
|
||||
|
||||
AuditMode is an experimental mode that enables some additional static analyis from CppCoreCheck.
|
||||
AuditMode is an experimental mode that enables some additional static analysis from CppCoreCheck.
|
||||
|
||||
@@ -27,11 +27,11 @@ Properties listed below are specific to each unique profile.
|
||||
| `cursorColor` | _Required_ | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorShape` | _Required_ | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) |
|
||||
| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
|
||||
| `fontSize` | _Required_ | Integer | `10` | Sets the font size. |
|
||||
| `fontSize` | _Required_ | Integer | `12` | Sets the font size. |
|
||||
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
|
||||
| `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
|
||||
| `name` | _Required_ | String | `PowerShell Core` | 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 overriden by using `tabTitle`. |
|
||||
| `padding` | _Required_ | String | `0, 0, 0, 0` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. |
|
||||
| `padding` | _Required_ | String | `8, 8, 8, 8` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. |
|
||||
| `snapOnInput` | _Required_ | Boolean | `true` | When set to `true`, the window will scroll to the command input line when typing. When set to `false`, the window will not scroll when you start typing. |
|
||||
| `startingDirectory` | _Required_ | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
|
||||
| `useAcrylic` | _Required_ | Boolean | `false` | When set to `true`, the window will have an acrylic background. When set to `false`, the window will have a plain, untextured background. |
|
||||
@@ -43,7 +43,7 @@ Properties listed below are specific to each unique profile.
|
||||
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Colors use hex color format: `"#rrggbb"`. Ordering is as follows: `[black, red, green, yellow, blue, magenta, cyan, white, bright black, bright red, bright green, bright yellow, bright blue, bright magenta, bright cyan, bright white]` |
|
||||
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
|
||||
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. |
|
||||
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. See [Background Images and Icons](./SettingsSchema.md#background-images-and-icons) below for help on specifying your own icons |
|
||||
| `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `tabTitle` | Optional | String | | If set, will replace the `name` 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. |
|
||||
|
||||
@@ -133,3 +133,48 @@ Bindings listed below are per the implementation in `src/cascadia/TerminalApp/Ap
|
||||
- moveFocusUp
|
||||
- moveFocusDown
|
||||
|
||||
## Background Images and Icons
|
||||
Some Terminal settings allow you to specify custom background images and icons. It is recommended that custom images and icons are stored in system-provided folders and are referred to using the correct [URI Schemes](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes). URI Schemes provide a way to reference files independent of their physical paths (which may change in the future).
|
||||
|
||||
The most useful URI schemes to remember when customizing background images and icons are:
|
||||
|
||||
| URI Scheme | Corresponding Physical Path | Use / description |
|
||||
| --- | --- | ---|
|
||||
| `ms-appdata:///Local/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\` | Per-machine files |
|
||||
| `ms-appdata:///Roaming/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\RoamingState\` | Common files |
|
||||
|
||||
> ⚠ Note: Do not rely on file references using the `ms-appx` URI Scheme (i.e. icons). These files are considered an internal implementation detail and may change name/location or may be omitted in the future.
|
||||
|
||||
### Icons
|
||||
Terminal displays icons for each of your profiles which Terminal generates for any built-in shells - PowerShell Core, PowerShell, and any installed Linux/WSL distros. Each profile refers to a stock icon via the `ms-appx` URI Scheme.
|
||||
|
||||
> ⚠ Note: Do not rely on the files referenced by the `ms-appx` URI Scheme - they are considered an internal implementation detail and may change name/location or may be omitted in the future.
|
||||
|
||||
You can refer to you own icons if you wish, e.g.:
|
||||
|
||||
```json
|
||||
"icon" : "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\icon-ubuntu-32.png",
|
||||
```
|
||||
|
||||
> 👉 Tip: Icons should be sized to 32x32px in an appropriate raster image format (e.g. .PNG, .GIF, or .ICO) to avoid having to scale your icons during runtime (causing a noticeable delay and loss of quality.)
|
||||
|
||||
### Custom Background Images
|
||||
You can apply a background image to each of your profiles, allowing you to configure/brand/style each of your profiles independently from one another if you wish.
|
||||
|
||||
To do so, specify your preferred `backgroundImage`, position it using `backgroundImageAlignment`, set its opacity with `backgroundImageOpacity`, and/or specify how your image fill the available space using `backgroundImageStretchMode`.
|
||||
|
||||
For example:
|
||||
```json
|
||||
"backgroundImage": "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\bg-ubuntu-256.png",
|
||||
"backgroundImageAlignment": "bottomRight",
|
||||
"backgroundImageOpacity": 0.1,
|
||||
"backgroundImageStretchMode": "none"
|
||||
```
|
||||
|
||||
> 👉 Tip: You can easily roam your collection of images and icons across all your machines by storing your icons and images in OneDrive (as shown above).
|
||||
|
||||
With these settings, your Terminal's Ubuntu profile would look similar to this:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
BIN
doc/images/custom-icon-and-background-image.jpg
Normal file
BIN
doc/images/custom-icon-and-background-image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 135 KiB |
@@ -1,6 +1,8 @@
|
||||
---
|
||||
author: "Mike Griese @zadjii-msft"
|
||||
created on: 2019-May-16
|
||||
created on: 2019-05-16
|
||||
last updated: 2019-07-07
|
||||
issue id: 523
|
||||
---
|
||||
|
||||
# Panes in the Windows Terminal
|
||||
@@ -171,5 +171,5 @@ You can even set multiple keybindings for a single action if you'd like. For exa
|
||||
will bind both <kbd>ctrl+shift+v</kbd> and
|
||||
<kbd>shift+Insert</kbd> to `paste`.
|
||||
|
||||
Note: If you set your copy keybinding to `"ctrl+c"`, you won't be able to send an interrupt to the commandline application using <kbd>Ctrl+C</kbd>. This is a bug, and being tracked by [#2258](https://github.com/microsoft/terminal/issues/2285).
|
||||
Note: If you set your copy keybinding to `"ctrl+c"`, you'll only be able to send an interrupt to the commandline application using <kbd>Ctrl+C</kbd> when there's 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"`
|
||||
|
||||
@@ -36,12 +36,14 @@ default shell is displayed (default shortcut `Ctrl+Shift+1`).
|
||||
|
||||
## Running a Different Shell
|
||||
|
||||
Note: The following text assumes you have WSL installed.
|
||||
Note: This section assumes you already have _Windows Subsystem for Linux_ (WSL) installed. For more information, see [the installation guide](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
|
||||
|
||||
To choose a different shell (e.g. `cmd.exe` or WSL `bash`) then
|
||||
Windows Terminal uses PowerShell as its default shell. You can also use Windows Terminal to launch other shells, such as `cmd.exe` or WSL's `bash`:
|
||||
|
||||
1. Select the `down` button next to the `+` in the tab bar
|
||||
2. Choose your new shell from the list (more on how to extend the list in the config section)
|
||||
1. In the tab bar, click the `⌵` button to view the available shells.
|
||||
2. Choose your shell from the dropdown list. The new shell session will open in a new tab.
|
||||
|
||||
To customize the shell list, see the _Configuring Windows Terminal_ section below.
|
||||
|
||||
## Starting a new PowerShell tab with admin privilege
|
||||
|
||||
@@ -65,16 +67,15 @@ Not currently supported "out of the box". See issue [#1060](https://github.com/m
|
||||
|
||||
## Configuring Windows Terminal
|
||||
|
||||
At the time of writing all Windows Terminal settings are managed via a json file.
|
||||
All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/RoamingState`.
|
||||
|
||||
From the `down` button in the top bar select Settings (default shortcut `Ctrl+,`).
|
||||
To open the settings file from Windows Terminal:
|
||||
|
||||
Your default json editor will open up the Terminal settings file. The file can be found
|
||||
at `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>/RoamingState`
|
||||
1. Click the `⌵` button in the top bar.
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: `Ctrl+,`.
|
||||
3. Your default `json` editor will open the settings file.
|
||||
|
||||
An introduction to the the various settings can be found [here](UsingJsonSettings.md).
|
||||
|
||||
The list of valid settings can be found in the [Profiles.json Documentation](../cascadia/SettingsSchema.md) doc.
|
||||
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:
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="Console Rules" Description="These rules enforce static analysis on console code." ToolsVersion="15.0">
|
||||
|
||||
<Include Path="cppcorecheckrules.ruleset" Action="Default" />
|
||||
<Include Path="cppcorecheckrules.ruleset" Action="Error" />
|
||||
|
||||
<Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
|
||||
<Rule Id="C6001" Action="Error" />
|
||||
<Rule Id="C6011" Action="Error" />
|
||||
<Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
|
||||
<Rule Id="C6001" Action="Error" />
|
||||
<Rule Id="C6011" Action="Error" />
|
||||
<!-- We can't do dynamic cast because RTTI is off. -->
|
||||
<!-- RTTI is off because Windows OS policies believe RTTI has too much binary size impact for the value and is less portable than RTTI-off modules. -->
|
||||
<Rule Id="C26466" Action="None" />
|
||||
</Rules>
|
||||
|
||||
|
||||
</RuleSet>
|
||||
|
||||
@@ -46,7 +46,7 @@ void ATTR_ROW::Resize(const size_t newWidth)
|
||||
{
|
||||
// Get the attribute that covers the final column of old width.
|
||||
const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr);
|
||||
auto& run = _list[runPos];
|
||||
auto& run = _list.at(runPos);
|
||||
|
||||
// Extend its length by the additional columns we're adding.
|
||||
run.SetLength(run.GetLength() + newWidth - _cchRowWidth);
|
||||
@@ -60,7 +60,7 @@ void ATTR_ROW::Resize(const size_t newWidth)
|
||||
// Get the attribute that covers the final column of the new width
|
||||
size_t CountOfAttr = 0;
|
||||
const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr);
|
||||
auto& run = _list[runPos];
|
||||
auto& run = _list.at(runPos);
|
||||
|
||||
// CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run"
|
||||
// So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3
|
||||
@@ -108,7 +108,7 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column,
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth);
|
||||
const auto runPos = FindAttrIndex(column, pApplies);
|
||||
return _list[runPos].GetAttributes();
|
||||
return _list.at(runPos).GetAttributes();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -290,10 +290,10 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
// two elements in our internal list.
|
||||
else if (_list.size() == 2 && newAttrs.at(0).GetLength() == 1)
|
||||
{
|
||||
auto left = _list.begin();
|
||||
const auto left = _list.begin();
|
||||
if (iStart == left->GetLength() && NewAttr == left->GetAttributes())
|
||||
{
|
||||
auto right = left + 1;
|
||||
const auto right = left + 1;
|
||||
left->IncrementLength();
|
||||
right->DecrementLength();
|
||||
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
#include "AttrRowIterator.hpp"
|
||||
#include "AttrRow.hpp"
|
||||
|
||||
AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow)
|
||||
AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow) noexcept
|
||||
{
|
||||
AttrRowIterator it{ attrRow };
|
||||
it._setToEnd();
|
||||
return it;
|
||||
}
|
||||
|
||||
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) :
|
||||
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) noexcept :
|
||||
_pAttrRow{ attrRow },
|
||||
_run{ attrRow->_list.cbegin() },
|
||||
_currentAttributeIndex{ 0 }
|
||||
{
|
||||
}
|
||||
|
||||
AttrRowIterator::operator bool() const noexcept
|
||||
AttrRowIterator::operator bool() const
|
||||
{
|
||||
return _run < _pAttrRow->_list.cend();
|
||||
}
|
||||
@@ -139,7 +139,7 @@ void AttrRowIterator::_decrement(size_t count)
|
||||
|
||||
// Routine Description:
|
||||
// - sets fields on the iterator to describe the end() state of the ATTR_ROW
|
||||
void AttrRowIterator::_setToEnd()
|
||||
void AttrRowIterator::_setToEnd() noexcept
|
||||
{
|
||||
_run = _pAttrRow->_list.cend();
|
||||
_currentAttributeIndex = 0;
|
||||
|
||||
@@ -29,11 +29,11 @@ public:
|
||||
using pointer = TextAttribute*;
|
||||
using reference = TextAttribute&;
|
||||
|
||||
static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow);
|
||||
static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow) noexcept;
|
||||
|
||||
AttrRowIterator(const ATTR_ROW* const attrRow);
|
||||
AttrRowIterator(const ATTR_ROW* const attrRow) noexcept;
|
||||
|
||||
operator bool() const noexcept;
|
||||
operator bool() const;
|
||||
|
||||
bool operator==(const AttrRowIterator& it) const;
|
||||
bool operator!=(const AttrRowIterator& it) const;
|
||||
@@ -57,5 +57,5 @@ private:
|
||||
|
||||
void _increment(size_t count);
|
||||
void _decrement(size_t count);
|
||||
void _setToEnd();
|
||||
void _setToEnd() noexcept;
|
||||
};
|
||||
|
||||
@@ -84,7 +84,7 @@ size_t CharRow::size() const noexcept
|
||||
// - sRowWidth - The width of the row.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CharRow::Reset()
|
||||
void CharRow::Reset() noexcept
|
||||
{
|
||||
for (auto& cell : _data)
|
||||
{
|
||||
@@ -209,7 +209,7 @@ const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const
|
||||
// Note: will throw exception if column is out of bounds
|
||||
DbcsAttribute& CharRow::DbcsAttrAt(const size_t column)
|
||||
{
|
||||
return const_cast<DbcsAttribute&>(static_cast<const CharRow* const>(this)->DbcsAttrAt(column));
|
||||
return _data.at(column).DbcsAttr();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -250,29 +250,6 @@ CharRow::reference CharRow::GlyphAt(const size_t column)
|
||||
return { *this, column };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - returns string containing text data exactly how it's stored internally, including doubling of
|
||||
// leading/trailing cells.
|
||||
// Arguments:
|
||||
// - none
|
||||
// Return Value:
|
||||
// - text stored in char row
|
||||
// - Note: will throw exception if out of memory
|
||||
std::wstring CharRow::GetTextRaw() const
|
||||
{
|
||||
std::wstring wstr;
|
||||
wstr.reserve(_data.size());
|
||||
for (size_t i = 0; i < _data.size(); ++i)
|
||||
{
|
||||
auto glyph = GlyphAt(i);
|
||||
for (auto it = glyph.begin(); it != glyph.end(); ++it)
|
||||
{
|
||||
wstr.push_back(*it);
|
||||
}
|
||||
}
|
||||
return wstr;
|
||||
}
|
||||
|
||||
std::wstring CharRow::GetText() const
|
||||
{
|
||||
std::wstring wstr;
|
||||
@@ -280,24 +257,24 @@ std::wstring CharRow::GetText() const
|
||||
|
||||
for (size_t i = 0; i < _data.size(); ++i)
|
||||
{
|
||||
auto glyph = GlyphAt(i);
|
||||
const auto glyph = GlyphAt(i);
|
||||
if (!DbcsAttrAt(i).IsTrailing())
|
||||
{
|
||||
for (auto it = glyph.begin(); it != glyph.end(); ++it)
|
||||
for (const auto wch : glyph)
|
||||
{
|
||||
wstr.push_back(*it);
|
||||
wstr.push_back(wch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return wstr;
|
||||
}
|
||||
|
||||
UnicodeStorage& CharRow::GetUnicodeStorage()
|
||||
UnicodeStorage& CharRow::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
|
||||
const UnicodeStorage& CharRow::GetUnicodeStorage() const
|
||||
const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
@@ -308,7 +285,7 @@ const UnicodeStorage& CharRow::GetUnicodeStorage() const
|
||||
// - column - the column to generate the key for
|
||||
// Return Value:
|
||||
// - the COORD key for data access from UnicodeStorage for the column
|
||||
COORD CharRow::GetStorageKey(const size_t column) const
|
||||
COORD CharRow::GetStorageKey(const size_t column) const noexcept
|
||||
{
|
||||
return { gsl::narrow<SHORT>(column), _pParent->GetId() };
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
void SetDoubleBytePadded(const bool doubleBytePadded) noexcept;
|
||||
bool WasDoubleBytePadded() const noexcept;
|
||||
size_t size() const noexcept;
|
||||
void Reset();
|
||||
void Reset() noexcept;
|
||||
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
|
||||
size_t MeasureLeft() const;
|
||||
size_t MeasureRight() const noexcept;
|
||||
@@ -64,9 +64,6 @@ public:
|
||||
void ClearGlyph(const size_t column);
|
||||
std::wstring GetText() const;
|
||||
|
||||
// other functions implemented at the template class level
|
||||
std::wstring GetTextRaw() const;
|
||||
|
||||
// working with glyphs
|
||||
const reference GlyphAt(const size_t column) const;
|
||||
reference GlyphAt(const size_t column);
|
||||
@@ -78,9 +75,9 @@ public:
|
||||
iterator end() noexcept;
|
||||
const_iterator cend() const noexcept;
|
||||
|
||||
UnicodeStorage& GetUnicodeStorage();
|
||||
const UnicodeStorage& GetUnicodeStorage() const;
|
||||
COORD GetStorageKey(const size_t column) const;
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
COORD GetStorageKey(const size_t column) const noexcept;
|
||||
|
||||
void UpdateParent(ROW* const pParent) noexcept;
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
// default glyph value, used for reseting the character data portion of a cell
|
||||
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
|
||||
|
||||
CharRowCell::CharRowCell() :
|
||||
CharRowCell::CharRowCell() noexcept :
|
||||
_wch{ DefaultValue },
|
||||
_attr{}
|
||||
{
|
||||
}
|
||||
|
||||
CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) :
|
||||
CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept :
|
||||
_wch{ wch },
|
||||
_attr{ attr }
|
||||
{
|
||||
@@ -22,7 +22,7 @@ CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) :
|
||||
|
||||
// Routine Description:
|
||||
// - "erases" the glyph. really sets it back to the default "empty" value
|
||||
void CharRowCell::EraseChars()
|
||||
void CharRowCell::EraseChars() noexcept
|
||||
{
|
||||
if (_attr.IsGlyphStored())
|
||||
{
|
||||
|
||||
@@ -27,10 +27,10 @@ Author(s):
|
||||
class CharRowCell final
|
||||
{
|
||||
public:
|
||||
CharRowCell();
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr);
|
||||
CharRowCell() noexcept;
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept;
|
||||
|
||||
void EraseChars();
|
||||
void EraseChars() noexcept;
|
||||
void Reset() noexcept;
|
||||
|
||||
bool IsSpace() const noexcept;
|
||||
|
||||
@@ -91,6 +91,9 @@ CharRowCellReference::const_iterator CharRowCellReference::begin() const
|
||||
// - get read-only iterator to the end of the glyph data
|
||||
// Return Value:
|
||||
// - end iterator of the glyph data
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26481)
|
||||
// TODO GH 2672: eliminate using pointers raw as begin/end markers in this class
|
||||
CharRowCellReference::const_iterator CharRowCellReference::end() const
|
||||
{
|
||||
if (_cellData().DbcsAttr().IsGlyphStored())
|
||||
@@ -103,6 +106,7 @@ CharRowCellReference::const_iterator CharRowCellReference::end() const
|
||||
return &_cellData().Char() + 1;
|
||||
}
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
bool operator==(const CharRowCellReference& ref, const std::vector<wchar_t>& glyph)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ class CharRowCellReference final
|
||||
public:
|
||||
using const_iterator = const wchar_t*;
|
||||
|
||||
CharRowCellReference(CharRow& parent, const size_t index) :
|
||||
CharRowCellReference(CharRow& parent, const size_t index) noexcept :
|
||||
_parent{ parent },
|
||||
_index{ index }
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
return _glyphStored;
|
||||
}
|
||||
|
||||
void SetGlyphStored(const bool stored)
|
||||
void SetGlyphStored(const bool stored) noexcept
|
||||
{
|
||||
_glyphStored = stored;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR };
|
||||
|
||||
OutputCell::OutputCell() :
|
||||
OutputCell::OutputCell() noexcept :
|
||||
_text{},
|
||||
_dbcsAttribute{},
|
||||
_textAttribute{ InvalidTextAttribute },
|
||||
@@ -111,7 +111,5 @@ void OutputCell::_setFromOutputCellView(const OutputCellView& cell)
|
||||
_dbcsAttribute = cell.DbcsAttr();
|
||||
_textAttribute = cell.TextAttr();
|
||||
_behavior = cell.TextAttrBehavior();
|
||||
|
||||
const auto& view = cell.Chars();
|
||||
_text = view;
|
||||
_text = cell.Chars();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ Author:
|
||||
|
||||
class InvalidCharInfoConversionException : public std::exception
|
||||
{
|
||||
const char* what() const noexcept
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return "Cannot convert to CHAR_INFO without explicit TextAttribute";
|
||||
}
|
||||
@@ -34,7 +34,7 @@ class InvalidCharInfoConversionException : public std::exception
|
||||
class OutputCell final
|
||||
{
|
||||
public:
|
||||
OutputCell();
|
||||
OutputCell() noexcept;
|
||||
|
||||
OutputCell(const std::wstring_view charData,
|
||||
const DbcsAttribute dbcsAttribute,
|
||||
|
||||
@@ -17,7 +17,7 @@ static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLO
|
||||
// Arguments:
|
||||
// - wch - The character to use for filling
|
||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
||||
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) :
|
||||
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) noexcept :
|
||||
_mode(Mode::Fill),
|
||||
_currentView(s_GenerateView(wch)),
|
||||
_run(),
|
||||
@@ -33,7 +33,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimi
|
||||
// Arguments:
|
||||
// - attr - The color attribute to use for filling
|
||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
||||
OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) :
|
||||
OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) noexcept :
|
||||
_mode(Mode::Fill),
|
||||
_currentView(s_GenerateView(attr)),
|
||||
_run(),
|
||||
@@ -50,7 +50,7 @@ OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t f
|
||||
// - wch - The character to use for filling
|
||||
// - attr - The color attribute to use for filling
|
||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
||||
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) :
|
||||
OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) noexcept :
|
||||
_mode(Mode::Fill),
|
||||
_currentView(s_GenerateView(wch, attr)),
|
||||
_run(),
|
||||
@@ -66,7 +66,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute&
|
||||
// Arguments:
|
||||
// - charInfo - The legacy character and color data to use for fililng (uses Unicode portion of text data)
|
||||
// - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0.
|
||||
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) :
|
||||
OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept :
|
||||
_mode(Mode::Fill),
|
||||
_currentView(s_GenerateView(charInfo)),
|
||||
_run(),
|
||||
@@ -116,7 +116,12 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const
|
||||
// razzle cannot distinguish between a std::wstring_view and a std::basic_string_view<WORD>
|
||||
// NOTE: This one internally casts to wchar_t because Razzle sees WORD and wchar_t as the same type
|
||||
// despite that Visual Studio build can tell the difference.
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacyAttrs, const bool /*unused*/) :
|
||||
#pragma warning(push)
|
||||
#pragma warning(suppress : 26490)
|
||||
// Suppresses reinterpret_cast. We're only doing this because Windows doesn't understand the type difference between wchar_t and DWORD.
|
||||
// It is not worth trying to separate that out further or risking performance over this particular warning here.
|
||||
// TODO GH 2673 - Investigate real wchar_t flag in Windows and resolve this audit issue
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacyAttrs, const bool /*unused*/) noexcept :
|
||||
_mode(Mode::LegacyAttr),
|
||||
_currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))),
|
||||
_run(std::wstring_view(reinterpret_cast<const wchar_t*>(legacyAttrs.data()), legacyAttrs.size())),
|
||||
@@ -126,12 +131,13 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view<WORD> legacy
|
||||
_fillLimit(0)
|
||||
{
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
// Routine Description:
|
||||
// - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute.
|
||||
// Arguments:
|
||||
// - charInfos - Multiple cell with unicode text and legacy color data.
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) :
|
||||
OutputCellIterator::OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept :
|
||||
_mode(Mode::CharInfo),
|
||||
_currentView(s_GenerateView(charInfos.at(0))),
|
||||
_run(charInfos),
|
||||
@@ -315,7 +321,7 @@ OutputCellIterator OutputCellIterator::operator++(int)
|
||||
// - Reference the view to fully-formed output cell data representing the underlying data source.
|
||||
// Return Value:
|
||||
// - Reference to the view
|
||||
const OutputCellView& OutputCellIterator::operator*() const
|
||||
const OutputCellView& OutputCellIterator::operator*() const noexcept
|
||||
{
|
||||
return _currentView;
|
||||
}
|
||||
@@ -324,7 +330,7 @@ const OutputCellView& OutputCellIterator::operator*() const
|
||||
// - Get pointer to the view to fully-formed output cell data representing the underlying data source.
|
||||
// Return Value:
|
||||
// - Pointer to the view
|
||||
const OutputCellView* OutputCellIterator::operator->() const
|
||||
const OutputCellView* OutputCellIterator::operator->() const noexcept
|
||||
{
|
||||
return &_currentView;
|
||||
}
|
||||
@@ -338,7 +344,7 @@ const OutputCellView* OutputCellIterator::operator->() const
|
||||
// - True if we just turned a lead half into a trailing half (and caller doesn't
|
||||
// need to further update the view).
|
||||
// - False if this wasn't applicable and the caller should update the view.
|
||||
bool OutputCellIterator::_TryMoveTrailing()
|
||||
bool OutputCellIterator::_TryMoveTrailing() noexcept
|
||||
{
|
||||
if (_currentView.DbcsAttr().IsLeading())
|
||||
{
|
||||
@@ -421,7 +427,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view,
|
||||
// - wch - View representing a single UTF-16 character (that can be represented without surrogates)
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept
|
||||
{
|
||||
const auto glyph = std::wstring_view(&wch, 1);
|
||||
|
||||
@@ -443,7 +449,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch)
|
||||
// - attr - View representing a single color
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noexcept
|
||||
{
|
||||
return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly);
|
||||
}
|
||||
@@ -458,7 +464,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr)
|
||||
// - attr - View representing a single color
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept
|
||||
{
|
||||
const auto glyph = std::wstring_view(&wch, 1);
|
||||
|
||||
@@ -480,12 +486,12 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const Text
|
||||
// - legacyAttr - View representing a single legacy color
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr)
|
||||
OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept
|
||||
{
|
||||
WORD cleanAttr = legacyAttr;
|
||||
WI_ClearAllFlags(cleanAttr, COMMON_LVB_SBCSDBCS); // don't use legacy lead/trailing byte flags for colors
|
||||
|
||||
TextAttribute attr(cleanAttr);
|
||||
const TextAttribute attr(cleanAttr);
|
||||
return s_GenerateView(attr);
|
||||
}
|
||||
|
||||
@@ -498,7 +504,7 @@ OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAt
|
||||
// - charInfo - character and attribute pair representing a single cell
|
||||
// Return Value:
|
||||
// - Object representing the view into this cell
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo)
|
||||
OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noexcept
|
||||
{
|
||||
const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1);
|
||||
|
||||
|
||||
@@ -33,14 +33,14 @@ public:
|
||||
using pointer = OutputCellView*;
|
||||
using reference = OutputCellView&;
|
||||
|
||||
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0);
|
||||
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0);
|
||||
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0);
|
||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0);
|
||||
OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept;
|
||||
OutputCellIterator(const std::wstring_view utf16Text);
|
||||
OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute);
|
||||
OutputCellIterator(const std::basic_string_view<WORD> legacyAttributes, const bool unused);
|
||||
OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos);
|
||||
OutputCellIterator(const std::basic_string_view<WORD> legacyAttributes, const bool unused) noexcept;
|
||||
OutputCellIterator(const std::basic_string_view<CHAR_INFO> charInfos) noexcept;
|
||||
OutputCellIterator(const std::basic_string_view<OutputCell> cells);
|
||||
~OutputCellIterator() = default;
|
||||
|
||||
@@ -55,8 +55,8 @@ public:
|
||||
OutputCellIterator& operator++();
|
||||
OutputCellIterator operator++(int);
|
||||
|
||||
const OutputCellView& operator*() const;
|
||||
const OutputCellView* operator->() const;
|
||||
const OutputCellView& operator*() const noexcept;
|
||||
const OutputCellView* operator->() const noexcept;
|
||||
|
||||
private:
|
||||
enum class Mode
|
||||
@@ -97,7 +97,7 @@ private:
|
||||
|
||||
TextAttribute _attr;
|
||||
|
||||
bool _TryMoveTrailing();
|
||||
bool _TryMoveTrailing() noexcept;
|
||||
|
||||
static OutputCellView s_GenerateView(const std::wstring_view view);
|
||||
|
||||
@@ -108,11 +108,11 @@ private:
|
||||
const TextAttribute attr,
|
||||
const TextAttributeBehavior behavior);
|
||||
|
||||
static OutputCellView s_GenerateView(const wchar_t& wch);
|
||||
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr);
|
||||
static OutputCellView s_GenerateView(const TextAttribute& attr);
|
||||
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr);
|
||||
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo);
|
||||
static OutputCellView s_GenerateView(const wchar_t& wch) noexcept;
|
||||
static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept;
|
||||
static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept;
|
||||
static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept;
|
||||
static OutputCellView s_GenerateView(const CHAR_INFO& charInfo) noexcept;
|
||||
|
||||
static OutputCellView s_GenerateView(const OutputCell& cell);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
// Routine Description:
|
||||
// - Constucts an empty in-memory region for holding output buffer cell data.
|
||||
OutputCellRect::OutputCellRect() :
|
||||
OutputCellRect::OutputCellRect() noexcept :
|
||||
_rows(0),
|
||||
_cols(0)
|
||||
{
|
||||
@@ -64,7 +64,7 @@ OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
|
||||
// - Pointer to the location in the rectangle that represents the start of the requested row.
|
||||
OutputCell* OutputCellRect::_FindRowOffset(const size_t row)
|
||||
{
|
||||
return (_storage.data() + (row * _cols));
|
||||
return &_storage.at(row * _cols);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -76,7 +76,7 @@ OutputCell* OutputCellRect::_FindRowOffset(const size_t row)
|
||||
// - Pointer to the location in the rectangle that represents the start of the requested row.
|
||||
const OutputCell* OutputCellRect::_FindRowOffset(const size_t row) const
|
||||
{
|
||||
return (_storage.data() + (row * _cols));
|
||||
return &_storage.at(row * _cols);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -29,7 +29,7 @@ Revision History:
|
||||
class OutputCellRect final
|
||||
{
|
||||
public:
|
||||
OutputCellRect();
|
||||
OutputCellRect() noexcept;
|
||||
OutputCellRect(const size_t rows, const size_t cols);
|
||||
|
||||
gsl::span<OutputCell> GetRow(const size_t row);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
OutputCellView::OutputCellView(const std::wstring_view view,
|
||||
const DbcsAttribute dbcsAttr,
|
||||
const TextAttribute textAttr,
|
||||
const TextAttributeBehavior behavior) :
|
||||
const TextAttributeBehavior behavior) noexcept :
|
||||
_view(view),
|
||||
_dbcsAttr(dbcsAttr),
|
||||
_textAttr(textAttr),
|
||||
@@ -27,7 +27,9 @@ OutputCellView::OutputCellView(const std::wstring_view view,
|
||||
// - Returns reference to view over text data
|
||||
// Return Value:
|
||||
// - Reference to UTF-16 character data
|
||||
const std::wstring_view& OutputCellView::Chars() const noexcept
|
||||
// C26445 - suppressed to enable the `TextBufferTextIterator::operator->` method which needs a non-temporary memory location holding the wstring_view.
|
||||
// TODO: GH 2681 - remove this suppression by reconciling the probably bad design of the iterators that leads to this being required.
|
||||
[[gsl::suppress(26445)]] const std::wstring_view& OutputCellView::Chars() const noexcept
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
OutputCellView(const std::wstring_view view,
|
||||
const DbcsAttribute dbcsAttr,
|
||||
const TextAttribute textAttr,
|
||||
const TextAttributeBehavior behavior);
|
||||
const TextAttributeBehavior behavior) noexcept;
|
||||
|
||||
const std::wstring_view& Chars() const noexcept;
|
||||
size_t Columns() const noexcept;
|
||||
|
||||
@@ -30,14 +30,14 @@ size_t ROW::size() const noexcept
|
||||
return _rowWidth;
|
||||
}
|
||||
|
||||
const CharRow& ROW::GetCharRow() const
|
||||
const CharRow& ROW::GetCharRow() const noexcept
|
||||
{
|
||||
return _charRow;
|
||||
}
|
||||
|
||||
CharRow& ROW::GetCharRow()
|
||||
CharRow& ROW::GetCharRow() noexcept
|
||||
{
|
||||
return const_cast<CharRow&>(static_cast<const ROW* const>(this)->GetCharRow());
|
||||
return _charRow;
|
||||
}
|
||||
|
||||
const ATTR_ROW& ROW::GetAttrRow() const noexcept
|
||||
@@ -47,7 +47,7 @@ const ATTR_ROW& ROW::GetAttrRow() const noexcept
|
||||
|
||||
ATTR_ROW& ROW::GetAttrRow() noexcept
|
||||
{
|
||||
return const_cast<ATTR_ROW&>(static_cast<const ROW* const>(this)->GetAttrRow());
|
||||
return _attrRow;
|
||||
}
|
||||
|
||||
SHORT ROW::GetId() const noexcept
|
||||
@@ -132,12 +132,12 @@ RowCellIterator ROW::AsCellIter(const size_t startIndex, const size_t count) con
|
||||
return RowCellIterator(*this, startIndex, count);
|
||||
}
|
||||
|
||||
UnicodeStorage& ROW::GetUnicodeStorage()
|
||||
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
|
||||
const UnicodeStorage& ROW::GetUnicodeStorage() const
|
||||
const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ public:
|
||||
|
||||
size_t size() const noexcept;
|
||||
|
||||
const CharRow& GetCharRow() const;
|
||||
CharRow& GetCharRow();
|
||||
const CharRow& GetCharRow() const noexcept;
|
||||
CharRow& GetCharRow() noexcept;
|
||||
|
||||
const ATTR_ROW& GetAttrRow() const noexcept;
|
||||
ATTR_ROW& GetAttrRow() noexcept;
|
||||
@@ -54,8 +54,8 @@ public:
|
||||
RowCellIterator AsCellIter(const size_t startIndex) const;
|
||||
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const;
|
||||
|
||||
UnicodeStorage& GetUnicodeStorage();
|
||||
const UnicodeStorage& GetUnicodeStorage() const;
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
|
||||
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const bool setWrap, std::optional<size_t> limitRight = std::nullopt);
|
||||
|
||||
|
||||
@@ -37,38 +37,38 @@ bool RowCellIterator::operator!=(const RowCellIterator& it) const noexcept
|
||||
return !(*this == it);
|
||||
}
|
||||
|
||||
RowCellIterator& RowCellIterator::operator+=(const ptrdiff_t& movement)
|
||||
RowCellIterator& RowCellIterator::operator+=(const ptrdiff_t& movement) noexcept
|
||||
{
|
||||
_pos += movement;
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
RowCellIterator& RowCellIterator::operator++()
|
||||
RowCellIterator& RowCellIterator::operator++() noexcept
|
||||
{
|
||||
return this->operator+=(1);
|
||||
}
|
||||
|
||||
RowCellIterator RowCellIterator::operator++(int)
|
||||
RowCellIterator RowCellIterator::operator++(int) noexcept
|
||||
{
|
||||
auto temp(*this);
|
||||
operator++();
|
||||
return temp;
|
||||
}
|
||||
|
||||
RowCellIterator RowCellIterator::operator+(const ptrdiff_t& movement)
|
||||
RowCellIterator RowCellIterator::operator+(const ptrdiff_t& movement) noexcept
|
||||
{
|
||||
auto temp(*this);
|
||||
temp += movement;
|
||||
return temp;
|
||||
}
|
||||
|
||||
const OutputCellView& RowCellIterator::operator*() const
|
||||
const OutputCellView& RowCellIterator::operator*() const noexcept
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
const OutputCellView* RowCellIterator::operator->() const
|
||||
const OutputCellView* RowCellIterator::operator->() const noexcept
|
||||
{
|
||||
return &_view;
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ public:
|
||||
bool operator==(const RowCellIterator& it) const noexcept;
|
||||
bool operator!=(const RowCellIterator& it) const noexcept;
|
||||
|
||||
RowCellIterator& operator+=(const ptrdiff_t& movement);
|
||||
RowCellIterator& operator++();
|
||||
RowCellIterator operator++(int);
|
||||
RowCellIterator operator+(const ptrdiff_t& movement);
|
||||
RowCellIterator& operator+=(const ptrdiff_t& movement) noexcept;
|
||||
RowCellIterator& operator++() noexcept;
|
||||
RowCellIterator operator++(int) noexcept;
|
||||
RowCellIterator operator+(const ptrdiff_t& movement) noexcept;
|
||||
|
||||
const OutputCellView& operator*() const;
|
||||
const OutputCellView* operator->() const;
|
||||
const OutputCellView& operator*() const noexcept;
|
||||
const OutputCellView* operator->() const noexcept;
|
||||
|
||||
private:
|
||||
const ROW& _row;
|
||||
|
||||
@@ -16,7 +16,7 @@ bool TextAttribute::IsLegacy() const noexcept
|
||||
// - color that should be displayed as the foreground color
|
||||
COLORREF TextAttribute::CalculateRgbForeground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultFgColor,
|
||||
COLORREF defaultBgColor) const
|
||||
COLORREF defaultBgColor) const noexcept
|
||||
{
|
||||
return _IsReverseVideo() ? _GetRgbBackground(colorTable, defaultBgColor) : _GetRgbForeground(colorTable, defaultFgColor);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ COLORREF TextAttribute::CalculateRgbForeground(std::basic_string_view<COLORREF>
|
||||
// - color that should be displayed as the background color
|
||||
COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultFgColor,
|
||||
COLORREF defaultBgColor) const
|
||||
COLORREF defaultBgColor) const noexcept
|
||||
{
|
||||
return _IsReverseVideo() ? _GetRgbForeground(colorTable, defaultFgColor) : _GetRgbBackground(colorTable, defaultBgColor);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view<COLORREF>
|
||||
// Return Value:
|
||||
// - color that is stored as the foreground color
|
||||
COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultColor) const
|
||||
COLORREF defaultColor) const noexcept
|
||||
{
|
||||
return _foreground.GetColor(colorTable, defaultColor, _isBold);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view<COLORREF> color
|
||||
// Return Value:
|
||||
// - color that is stored as the background color
|
||||
COLORREF TextAttribute::_GetRgbBackground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultColor) const
|
||||
COLORREF defaultColor) const noexcept
|
||||
{
|
||||
return _background.GetColor(colorTable, defaultColor, false);
|
||||
}
|
||||
@@ -75,22 +75,22 @@ WORD TextAttribute::GetMetaAttributes() const noexcept
|
||||
return wMeta;
|
||||
}
|
||||
|
||||
void TextAttribute::SetForeground(const COLORREF rgbForeground)
|
||||
void TextAttribute::SetForeground(const COLORREF rgbForeground) noexcept
|
||||
{
|
||||
_foreground = TextColor(rgbForeground);
|
||||
}
|
||||
|
||||
void TextAttribute::SetBackground(const COLORREF rgbBackground)
|
||||
void TextAttribute::SetBackground(const COLORREF rgbBackground) noexcept
|
||||
{
|
||||
_background = TextColor(rgbBackground);
|
||||
}
|
||||
|
||||
void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept
|
||||
{
|
||||
_wAttrLegacy = static_cast<WORD>(wLegacy & META_ATTRS);
|
||||
_wAttrLegacy = gsl::narrow_cast<WORD>(wLegacy & META_ATTRS);
|
||||
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
|
||||
BYTE fgIndex = static_cast<BYTE>(wLegacy & FG_ATTRS);
|
||||
BYTE bgIndex = static_cast<BYTE>(wLegacy & BG_ATTRS) >> 4;
|
||||
const BYTE fgIndex = gsl::narrow_cast<BYTE>(wLegacy & FG_ATTRS);
|
||||
const BYTE bgIndex = gsl::narrow_cast<BYTE>(wLegacy & BG_ATTRS) >> 4;
|
||||
_foreground = TextColor(fgIndex);
|
||||
_background = TextColor(bgIndex);
|
||||
}
|
||||
@@ -98,16 +98,16 @@ void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept
|
||||
void TextAttribute::SetLegacyAttributes(const WORD attrs,
|
||||
const bool setForeground,
|
||||
const bool setBackground,
|
||||
const bool setMeta)
|
||||
const bool setMeta) noexcept
|
||||
{
|
||||
if (setForeground)
|
||||
{
|
||||
BYTE fgIndex = (BYTE)(attrs & FG_ATTRS);
|
||||
const BYTE fgIndex = gsl::narrow_cast<BYTE>(attrs & FG_ATTRS);
|
||||
_foreground = TextColor(fgIndex);
|
||||
}
|
||||
if (setBackground)
|
||||
{
|
||||
BYTE bgIndex = (BYTE)(attrs & BG_ATTRS) >> 4;
|
||||
const BYTE bgIndex = gsl::narrow_cast<BYTE>(attrs & BG_ATTRS) >> 4;
|
||||
_background = TextColor(bgIndex);
|
||||
}
|
||||
if (setMeta)
|
||||
@@ -133,17 +133,17 @@ void TextAttribute::SetIndexedAttributes(const std::optional<const BYTE> foregro
|
||||
{
|
||||
if (foreground)
|
||||
{
|
||||
BYTE fgIndex = (*foreground) & 0xFF;
|
||||
const BYTE fgIndex = (*foreground) & 0xFF;
|
||||
_foreground = TextColor(fgIndex);
|
||||
}
|
||||
if (background)
|
||||
{
|
||||
BYTE bgIndex = (*background) & 0xFF;
|
||||
const BYTE bgIndex = (*background) & 0xFF;
|
||||
_background = TextColor(bgIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground)
|
||||
void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept
|
||||
{
|
||||
if (fIsForeground)
|
||||
{
|
||||
|
||||
@@ -39,9 +39,9 @@ public:
|
||||
}
|
||||
|
||||
constexpr TextAttribute(const WORD wLegacyAttr) noexcept :
|
||||
_wAttrLegacy{ static_cast<WORD>(wLegacyAttr & META_ATTRS) },
|
||||
_foreground{ static_cast<BYTE>(wLegacyAttr & FG_ATTRS) },
|
||||
_background{ static_cast<BYTE>((wLegacyAttr & BG_ATTRS) >> 4) },
|
||||
_wAttrLegacy{ gsl::narrow_cast<WORD>(wLegacyAttr & META_ATTRS) },
|
||||
_foreground{ gsl::narrow_cast<BYTE>(wLegacyAttr & FG_ATTRS) },
|
||||
_background{ gsl::narrow_cast<BYTE>((wLegacyAttr & BG_ATTRS) >> 4) },
|
||||
_isBold{ false }
|
||||
{
|
||||
// If we're given lead/trailing byte information with the legacy color, strip it.
|
||||
@@ -59,9 +59,9 @@ public:
|
||||
|
||||
constexpr WORD GetLegacyAttributes() const noexcept
|
||||
{
|
||||
BYTE fg = (_foreground.GetIndex() & FG_ATTRS);
|
||||
BYTE bg = (_background.GetIndex() << 4) & BG_ATTRS;
|
||||
WORD meta = (_wAttrLegacy & META_ATTRS);
|
||||
const BYTE fg = (_foreground.GetIndex() & FG_ATTRS);
|
||||
const BYTE bg = (_background.GetIndex() << 4) & BG_ATTRS;
|
||||
const WORD meta = (_wAttrLegacy & META_ATTRS);
|
||||
return (fg | bg | meta) | (_isBold ? FOREGROUND_INTENSITY : 0);
|
||||
}
|
||||
|
||||
@@ -80,20 +80,20 @@ public:
|
||||
constexpr WORD GetLegacyAttributes(const BYTE defaultFgIndex,
|
||||
const BYTE defaultBgIndex) const noexcept
|
||||
{
|
||||
BYTE fgIndex = _foreground.IsLegacy() ? _foreground.GetIndex() : defaultFgIndex;
|
||||
BYTE bgIndex = _background.IsLegacy() ? _background.GetIndex() : defaultBgIndex;
|
||||
BYTE fg = (fgIndex & FG_ATTRS);
|
||||
BYTE bg = (bgIndex << 4) & BG_ATTRS;
|
||||
WORD meta = (_wAttrLegacy & META_ATTRS);
|
||||
const BYTE fgIndex = _foreground.IsLegacy() ? _foreground.GetIndex() : defaultFgIndex;
|
||||
const BYTE bgIndex = _background.IsLegacy() ? _background.GetIndex() : defaultBgIndex;
|
||||
const BYTE fg = (fgIndex & FG_ATTRS);
|
||||
const BYTE bg = (bgIndex << 4) & BG_ATTRS;
|
||||
const WORD meta = (_wAttrLegacy & META_ATTRS);
|
||||
return (fg | bg | meta) | (_isBold ? FOREGROUND_INTENSITY : 0);
|
||||
}
|
||||
|
||||
COLORREF CalculateRgbForeground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultFgColor,
|
||||
COLORREF defaultBgColor) const;
|
||||
COLORREF defaultBgColor) const noexcept;
|
||||
COLORREF CalculateRgbBackground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultFgColor,
|
||||
COLORREF defaultBgColor) const;
|
||||
COLORREF defaultBgColor) const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
void SetLegacyAttributes(const WORD attrs,
|
||||
const bool setForeground,
|
||||
const bool setBackground,
|
||||
const bool setMeta);
|
||||
const bool setMeta) noexcept;
|
||||
|
||||
void SetIndexedAttributes(const std::optional<const BYTE> foreground,
|
||||
const std::optional<const BYTE> background) noexcept;
|
||||
@@ -133,9 +133,9 @@ public:
|
||||
bool IsLegacy() const noexcept;
|
||||
bool IsBold() const noexcept;
|
||||
|
||||
void SetForeground(const COLORREF rgbForeground);
|
||||
void SetBackground(const COLORREF rgbBackground);
|
||||
void SetColor(const COLORREF rgbColor, const bool fIsForeground);
|
||||
void SetForeground(const COLORREF rgbForeground) noexcept;
|
||||
void SetBackground(const COLORREF rgbBackground) noexcept;
|
||||
void SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept;
|
||||
|
||||
void SetDefaultForeground() noexcept;
|
||||
void SetDefaultBackground() noexcept;
|
||||
@@ -150,9 +150,9 @@ public:
|
||||
|
||||
private:
|
||||
COLORREF _GetRgbForeground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultColor) const;
|
||||
COLORREF defaultColor) const noexcept;
|
||||
COLORREF _GetRgbBackground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultColor) const;
|
||||
COLORREF defaultColor) const noexcept;
|
||||
bool _IsReverseVideo() const noexcept;
|
||||
void _SetBoldness(const bool isBold) noexcept;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// - rgbColor: the COLORREF containing the color information for this TextColor
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TextColor::SetColor(const COLORREF rgbColor)
|
||||
void TextColor::SetColor(const COLORREF rgbColor) noexcept
|
||||
{
|
||||
_meta = ColorType::IsRgb;
|
||||
_red = GetRValue(rgbColor);
|
||||
@@ -25,7 +25,7 @@ void TextColor::SetColor(const COLORREF rgbColor)
|
||||
// - index: the index of the colortable we should use for this TextColor.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TextColor::SetIndex(const BYTE index)
|
||||
void TextColor::SetIndex(const BYTE index) noexcept
|
||||
{
|
||||
_meta = ColorType::IsIndex;
|
||||
_index = index;
|
||||
@@ -38,7 +38,7 @@ void TextColor::SetIndex(const BYTE index)
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TextColor::SetDefault()
|
||||
void TextColor::SetDefault() noexcept
|
||||
{
|
||||
_meta = ColorType::IsDefault;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ void TextColor::SetDefault()
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
bool brighten) const
|
||||
bool brighten) const noexcept
|
||||
{
|
||||
if (IsDefault())
|
||||
{
|
||||
@@ -81,9 +81,9 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
// If we find a match, return instead the bright version of this color
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (colorTable[i] == defaultColor)
|
||||
if (colorTable.at(i) == defaultColor)
|
||||
{
|
||||
return colorTable[i + 8];
|
||||
return colorTable.at(i + 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,12 +102,12 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
if (brighten && _index < 8)
|
||||
{
|
||||
FAIL_FAST_IF(colorTable.size() < 16);
|
||||
FAIL_FAST_IF((size_t)(_index + 8) > (size_t)(colorTable.size()));
|
||||
return colorTable[_index + 8];
|
||||
FAIL_FAST_IF(gsl::narrow_cast<size_t>(_index) + 8 > colorTable.size());
|
||||
return colorTable.at(gsl::narrow_cast<size_t>(_index) + 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
return colorTable[_index];
|
||||
return colorTable.at(_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ COLORREF TextColor::GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a COLORREF containing our stored value
|
||||
COLORREF TextColor::_GetRGB() const
|
||||
COLORREF TextColor::_GetRGB() const noexcept
|
||||
{
|
||||
return RGB(_red, _green, _blue);
|
||||
}
|
||||
|
||||
@@ -91,13 +91,13 @@ public:
|
||||
return _meta == ColorType::IsRgb;
|
||||
}
|
||||
|
||||
void SetColor(const COLORREF rgbColor);
|
||||
void SetIndex(const BYTE index);
|
||||
void SetDefault();
|
||||
void SetColor(const COLORREF rgbColor) noexcept;
|
||||
void SetIndex(const BYTE index) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(std::basic_string_view<COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
const bool brighten) const;
|
||||
const bool brighten) const noexcept;
|
||||
|
||||
constexpr BYTE GetIndex() const noexcept
|
||||
{
|
||||
@@ -113,7 +113,7 @@ private:
|
||||
BYTE _green;
|
||||
BYTE _blue;
|
||||
|
||||
COLORREF _GetRGB() const;
|
||||
COLORREF _GetRGB() const noexcept;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "precomp.h"
|
||||
#include "UnicodeStorage.hpp"
|
||||
|
||||
UnicodeStorage::UnicodeStorage() :
|
||||
UnicodeStorage::UnicodeStorage() noexcept :
|
||||
_map{}
|
||||
{
|
||||
}
|
||||
@@ -35,7 +35,7 @@ void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
|
||||
// - erases key and its associated data from the storage
|
||||
// Arguments:
|
||||
// - key - the key to remove
|
||||
void UnicodeStorage::Erase(const key_type key) noexcept
|
||||
void UnicodeStorage::Erase(const key_type key)
|
||||
{
|
||||
_map.erase(key);
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ public:
|
||||
using key_type = typename COORD;
|
||||
using mapped_type = typename std::vector<wchar_t>;
|
||||
|
||||
UnicodeStorage();
|
||||
UnicodeStorage() noexcept;
|
||||
|
||||
const mapped_type& GetText(const key_type key) const;
|
||||
|
||||
void StoreGlyph(const key_type key, const mapped_type& glyph);
|
||||
|
||||
void Erase(const key_type key) noexcept;
|
||||
void Erase(const key_type key);
|
||||
|
||||
void Remap(const std::map<SHORT, SHORT>& rowMap, const std::optional<SHORT> width);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// - Constructor to set default properties for Cursor
|
||||
// Arguments:
|
||||
// - ulSize - The height of the cursor within this buffer
|
||||
Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) :
|
||||
Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
|
||||
_parentBuffer{ parentBuffer },
|
||||
_cPosition{ 0 },
|
||||
_fHasMoved(false),
|
||||
@@ -87,36 +87,36 @@ ULONG Cursor::GetSize() const noexcept
|
||||
return _ulSize;
|
||||
}
|
||||
|
||||
void Cursor::SetHasMoved(const bool fHasMoved)
|
||||
void Cursor::SetHasMoved(const bool fHasMoved) noexcept
|
||||
{
|
||||
_fHasMoved = fHasMoved;
|
||||
}
|
||||
|
||||
void Cursor::SetIsVisible(const bool fIsVisible)
|
||||
void Cursor::SetIsVisible(const bool fIsVisible) noexcept
|
||||
{
|
||||
_fIsVisible = fIsVisible;
|
||||
_RedrawCursor();
|
||||
}
|
||||
|
||||
void Cursor::SetIsOn(const bool fIsOn)
|
||||
void Cursor::SetIsOn(const bool fIsOn) noexcept
|
||||
{
|
||||
_fIsOn = fIsOn;
|
||||
_RedrawCursorAlways();
|
||||
}
|
||||
|
||||
void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed)
|
||||
void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed) noexcept
|
||||
{
|
||||
_fBlinkingAllowed = fBlinkingAllowed;
|
||||
_RedrawCursorAlways();
|
||||
}
|
||||
|
||||
void Cursor::SetIsDouble(const bool fIsDouble)
|
||||
void Cursor::SetIsDouble(const bool fIsDouble) noexcept
|
||||
{
|
||||
_fIsDouble = fIsDouble;
|
||||
_RedrawCursor();
|
||||
}
|
||||
|
||||
void Cursor::SetIsConversionArea(const bool fIsConversionArea)
|
||||
void Cursor::SetIsConversionArea(const bool fIsConversionArea) noexcept
|
||||
{
|
||||
// Functionally the same as "Hide cursor"
|
||||
// Never called with TRUE, it's only used in the creation of a
|
||||
@@ -125,19 +125,19 @@ void Cursor::SetIsConversionArea(const bool fIsConversionArea)
|
||||
_RedrawCursorAlways();
|
||||
}
|
||||
|
||||
void Cursor::SetIsPopupShown(const bool fIsPopupShown)
|
||||
void Cursor::SetIsPopupShown(const bool fIsPopupShown) noexcept
|
||||
{
|
||||
// Functionally the same as "Hide cursor"
|
||||
_fIsPopupShown = fIsPopupShown;
|
||||
_RedrawCursorAlways();
|
||||
}
|
||||
|
||||
void Cursor::SetDelay(const bool fDelay)
|
||||
void Cursor::SetDelay(const bool fDelay) noexcept
|
||||
{
|
||||
_fDelay = fDelay;
|
||||
}
|
||||
|
||||
void Cursor::SetSize(const ULONG ulSize)
|
||||
void Cursor::SetSize(const ULONG ulSize) noexcept
|
||||
{
|
||||
_ulSize = ulSize;
|
||||
_RedrawCursor();
|
||||
@@ -195,7 +195,7 @@ void Cursor::_RedrawCursorAlways() noexcept
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
void Cursor::SetPosition(const COORD cPosition)
|
||||
void Cursor::SetPosition(const COORD cPosition) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.X = cPosition.X;
|
||||
@@ -204,50 +204,50 @@ void Cursor::SetPosition(const COORD cPosition)
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
|
||||
void Cursor::SetXPosition(const int NewX)
|
||||
void Cursor::SetXPosition(const int NewX) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.X = (SHORT)NewX;
|
||||
_cPosition.X = gsl::narrow<SHORT>(NewX);
|
||||
_RedrawCursor();
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
|
||||
void Cursor::SetYPosition(const int NewY)
|
||||
void Cursor::SetYPosition(const int NewY) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.Y = (SHORT)NewY;
|
||||
_cPosition.Y = gsl::narrow<SHORT>(NewY);
|
||||
_RedrawCursor();
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
|
||||
void Cursor::IncrementXPosition(const int DeltaX)
|
||||
void Cursor::IncrementXPosition(const int DeltaX) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.X += (SHORT)DeltaX;
|
||||
_cPosition.X += gsl::narrow<SHORT>(DeltaX);
|
||||
_RedrawCursor();
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
|
||||
void Cursor::IncrementYPosition(const int DeltaY)
|
||||
void Cursor::IncrementYPosition(const int DeltaY) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.Y += (SHORT)DeltaY;
|
||||
_cPosition.Y += gsl::narrow<SHORT>(DeltaY);
|
||||
_RedrawCursor();
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
|
||||
void Cursor::DecrementXPosition(const int DeltaX)
|
||||
void Cursor::DecrementXPosition(const int DeltaX) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.X -= (SHORT)DeltaX;
|
||||
_cPosition.X -= gsl::narrow<SHORT>(DeltaX);
|
||||
_RedrawCursor();
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
|
||||
void Cursor::DecrementYPosition(const int DeltaY)
|
||||
void Cursor::DecrementYPosition(const int DeltaY) noexcept
|
||||
{
|
||||
_RedrawCursor();
|
||||
_cPosition.Y -= (SHORT)DeltaY;
|
||||
_cPosition.Y -= gsl::narrow<SHORT>(DeltaY);
|
||||
_RedrawCursor();
|
||||
ResetDelayEOLWrap();
|
||||
}
|
||||
@@ -262,7 +262,7 @@ void Cursor::DecrementYPosition(const int DeltaY)
|
||||
// - OtherCursor - The cursor to copy properties from
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Cursor::CopyProperties(const Cursor& OtherCursor)
|
||||
void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept
|
||||
{
|
||||
// We shouldn't copy the position as it will be already rearranged by the resize operation.
|
||||
//_cPosition = pOtherCursor->_cPosition;
|
||||
@@ -288,34 +288,34 @@ void Cursor::CopyProperties(const Cursor& OtherCursor)
|
||||
_color = OtherCursor._color;
|
||||
}
|
||||
|
||||
void Cursor::DelayEOLWrap(const COORD coordDelayedAt)
|
||||
void Cursor::DelayEOLWrap(const COORD coordDelayedAt) noexcept
|
||||
{
|
||||
_coordDelayedAt = coordDelayedAt;
|
||||
_fDelayedEolWrap = true;
|
||||
}
|
||||
|
||||
void Cursor::ResetDelayEOLWrap()
|
||||
void Cursor::ResetDelayEOLWrap() noexcept
|
||||
{
|
||||
_coordDelayedAt = { 0 };
|
||||
_fDelayedEolWrap = false;
|
||||
}
|
||||
|
||||
COORD Cursor::GetDelayedAtPosition() const
|
||||
COORD Cursor::GetDelayedAtPosition() const noexcept
|
||||
{
|
||||
return _coordDelayedAt;
|
||||
}
|
||||
|
||||
bool Cursor::IsDelayedEOLWrap() const
|
||||
bool Cursor::IsDelayedEOLWrap() const noexcept
|
||||
{
|
||||
return _fDelayedEolWrap;
|
||||
}
|
||||
|
||||
void Cursor::StartDeferDrawing()
|
||||
void Cursor::StartDeferDrawing() noexcept
|
||||
{
|
||||
_fDeferCursorRedraw = true;
|
||||
}
|
||||
|
||||
void Cursor::EndDeferDrawing()
|
||||
void Cursor::EndDeferDrawing() noexcept
|
||||
{
|
||||
if (_fHaveDeferredCursorRedraw)
|
||||
{
|
||||
@@ -325,27 +325,27 @@ void Cursor::EndDeferDrawing()
|
||||
_fDeferCursorRedraw = FALSE;
|
||||
}
|
||||
|
||||
const CursorType Cursor::GetType() const
|
||||
const CursorType Cursor::GetType() const noexcept
|
||||
{
|
||||
return _cursorType;
|
||||
}
|
||||
|
||||
const bool Cursor::IsUsingColor() const
|
||||
const bool Cursor::IsUsingColor() const noexcept
|
||||
{
|
||||
return GetColor() != INVALID_COLOR;
|
||||
}
|
||||
|
||||
const COLORREF Cursor::GetColor() const
|
||||
const COLORREF Cursor::GetColor() const noexcept
|
||||
{
|
||||
return _color;
|
||||
}
|
||||
|
||||
void Cursor::SetColor(const unsigned int color)
|
||||
void Cursor::SetColor(const unsigned int color) noexcept
|
||||
{
|
||||
_color = (COLORREF)color;
|
||||
_color = gsl::narrow_cast<COLORREF>(color);
|
||||
}
|
||||
|
||||
void Cursor::SetType(const CursorType type)
|
||||
void Cursor::SetType(const CursorType type) noexcept
|
||||
{
|
||||
_cursorType = type;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class Cursor final
|
||||
public:
|
||||
static const unsigned int s_InvertCursorColor = INVALID_COLOR;
|
||||
|
||||
Cursor(const ULONG ulSize, TextBuffer& parentBuffer);
|
||||
Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept;
|
||||
|
||||
~Cursor();
|
||||
|
||||
@@ -50,41 +50,41 @@ public:
|
||||
ULONG GetSize() const noexcept;
|
||||
COORD GetPosition() const noexcept;
|
||||
|
||||
const CursorType GetType() const;
|
||||
const bool IsUsingColor() const;
|
||||
const COLORREF GetColor() const;
|
||||
const CursorType GetType() const noexcept;
|
||||
const bool IsUsingColor() const noexcept;
|
||||
const COLORREF GetColor() const noexcept;
|
||||
|
||||
void StartDeferDrawing();
|
||||
void EndDeferDrawing();
|
||||
void StartDeferDrawing() noexcept;
|
||||
void EndDeferDrawing() noexcept;
|
||||
|
||||
void SetHasMoved(const bool fHasMoved);
|
||||
void SetIsVisible(const bool fIsVisible);
|
||||
void SetIsOn(const bool fIsOn);
|
||||
void SetBlinkingAllowed(const bool fIsOn);
|
||||
void SetIsDouble(const bool fIsDouble);
|
||||
void SetIsConversionArea(const bool fIsConversionArea);
|
||||
void SetIsPopupShown(const bool fIsPopupShown);
|
||||
void SetDelay(const bool fDelay);
|
||||
void SetSize(const ULONG ulSize);
|
||||
void SetHasMoved(const bool fHasMoved) noexcept;
|
||||
void SetIsVisible(const bool fIsVisible) noexcept;
|
||||
void SetIsOn(const bool fIsOn) noexcept;
|
||||
void SetBlinkingAllowed(const bool fIsOn) noexcept;
|
||||
void SetIsDouble(const bool fIsDouble) noexcept;
|
||||
void SetIsConversionArea(const bool fIsConversionArea) noexcept;
|
||||
void SetIsPopupShown(const bool fIsPopupShown) noexcept;
|
||||
void SetDelay(const bool fDelay) noexcept;
|
||||
void SetSize(const ULONG ulSize) noexcept;
|
||||
void SetStyle(const ULONG ulSize, const COLORREF color, const CursorType type) noexcept;
|
||||
|
||||
void SetPosition(const COORD cPosition);
|
||||
void SetXPosition(const int NewX);
|
||||
void SetYPosition(const int NewY);
|
||||
void IncrementXPosition(const int DeltaX);
|
||||
void IncrementYPosition(const int DeltaY);
|
||||
void DecrementXPosition(const int DeltaX);
|
||||
void DecrementYPosition(const int DeltaY);
|
||||
void SetPosition(const COORD cPosition) noexcept;
|
||||
void SetXPosition(const int NewX) noexcept;
|
||||
void SetYPosition(const int NewY) noexcept;
|
||||
void IncrementXPosition(const int DeltaX) noexcept;
|
||||
void IncrementYPosition(const int DeltaY) noexcept;
|
||||
void DecrementXPosition(const int DeltaX) noexcept;
|
||||
void DecrementYPosition(const int DeltaY) noexcept;
|
||||
|
||||
void CopyProperties(const Cursor& OtherCursor);
|
||||
void CopyProperties(const Cursor& OtherCursor) noexcept;
|
||||
|
||||
void DelayEOLWrap(const COORD coordDelayedAt);
|
||||
void ResetDelayEOLWrap();
|
||||
COORD GetDelayedAtPosition() const;
|
||||
bool IsDelayedEOLWrap() const;
|
||||
void DelayEOLWrap(const COORD coordDelayedAt) noexcept;
|
||||
void ResetDelayEOLWrap() noexcept;
|
||||
COORD GetDelayedAtPosition() const noexcept;
|
||||
bool IsDelayedEOLWrap() const noexcept;
|
||||
|
||||
void SetColor(const unsigned int color);
|
||||
void SetType(const CursorType type);
|
||||
void SetColor(const unsigned int color) noexcept;
|
||||
void SetType(const CursorType type) noexcept;
|
||||
|
||||
private:
|
||||
TextBuffer& _parentBuffer;
|
||||
|
||||
@@ -49,7 +49,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
|
||||
// - OtherBuffer - The text buffer to copy properties from
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer)
|
||||
void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) noexcept
|
||||
{
|
||||
GetCursor().CopyProperties(OtherBuffer.GetCursor());
|
||||
}
|
||||
@@ -60,9 +60,9 @@ void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer)
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - Total number of rows in the buffer
|
||||
UINT TextBuffer::TotalRowCount() const
|
||||
UINT TextBuffer::TotalRowCount() const noexcept
|
||||
{
|
||||
return static_cast<UINT>(_storage.size());
|
||||
return gsl::narrow<UINT>(_storage.size());
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -78,7 +78,7 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
|
||||
|
||||
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
|
||||
const size_t offsetIndex = (_firstRow + index) % totalRows;
|
||||
return _storage[offsetIndex];
|
||||
return _storage.at(offsetIndex);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -90,7 +90,11 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const
|
||||
// - reference to the requested row. Asserts if out of bounds.
|
||||
ROW& TextBuffer::GetRowByOffset(const size_t index)
|
||||
{
|
||||
return const_cast<ROW&>(static_cast<const TextBuffer*>(this)->GetRowByOffset(index));
|
||||
const size_t totalRows = TotalRowCount();
|
||||
|
||||
// Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows.
|
||||
const size_t offsetIndex = (_firstRow + index) % totalRows;
|
||||
return _storage.at(offsetIndex);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -542,7 +546,7 @@ bool TextBuffer::IncrementCircularBuffer()
|
||||
_renderTarget.TriggerCircling();
|
||||
|
||||
// First, clean out the old "first row" as it will become the "last row" of the buffer after the circle is performed.
|
||||
bool fSuccess = _storage.at(_firstRow).Reset(_currentAttributes);
|
||||
const bool fSuccess = _storage.at(_firstRow).Reset(_currentAttributes);
|
||||
if (fSuccess)
|
||||
{
|
||||
// Now proceed to increment.
|
||||
@@ -584,9 +588,9 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View
|
||||
// Search the given viewport by starting at the bottom.
|
||||
coordEndOfText.Y = viewport.BottomInclusive();
|
||||
|
||||
const ROW* pCurrRow = &GetRowByOffset(coordEndOfText.Y);
|
||||
const auto& currRow = GetRowByOffset(coordEndOfText.Y);
|
||||
// The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character)
|
||||
coordEndOfText.X = static_cast<short>(pCurrRow->GetCharRow().MeasureRight()) - 1;
|
||||
coordEndOfText.X = gsl::narrow<short>(currRow.GetCharRow().MeasureRight()) - 1;
|
||||
|
||||
// If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text.
|
||||
const auto viewportTop = viewport.Top();
|
||||
@@ -594,10 +598,10 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View
|
||||
while (fDoBackUp)
|
||||
{
|
||||
coordEndOfText.Y--;
|
||||
pCurrRow = &GetRowByOffset(coordEndOfText.Y);
|
||||
const auto& backupRow = GetRowByOffset(coordEndOfText.Y);
|
||||
// We need to back up to the previous row if this line is empty, AND there are more rows
|
||||
|
||||
coordEndOfText.X = static_cast<short>(pCurrRow->GetCharRow().MeasureRight()) - 1;
|
||||
coordEndOfText.X = gsl::narrow<short>(backupRow.GetCharRow().MeasureRight()) - 1;
|
||||
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
|
||||
}
|
||||
|
||||
@@ -640,7 +644,7 @@ COORD TextBuffer::_GetPreviousFromCursor() const
|
||||
return coordPosition;
|
||||
}
|
||||
|
||||
const SHORT TextBuffer::GetFirstRowIndex() const
|
||||
const SHORT TextBuffer::GetFirstRowIndex() const noexcept
|
||||
{
|
||||
return _firstRow;
|
||||
}
|
||||
@@ -649,7 +653,7 @@ const Viewport TextBuffer::GetSize() const
|
||||
return Viewport::FromDimensions({ 0, 0 }, { gsl::narrow<SHORT>(_storage.at(0).size()), gsl::narrow<SHORT>(_storage.size()) });
|
||||
}
|
||||
|
||||
void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex)
|
||||
void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex) noexcept
|
||||
{
|
||||
_firstRow = FirstRowIndex;
|
||||
}
|
||||
@@ -755,12 +759,12 @@ void TextBuffer::ScrollRows(const SHORT firstRow, const SHORT size, const SHORT
|
||||
_RefreshRowIDs(std::nullopt);
|
||||
}
|
||||
|
||||
Cursor& TextBuffer::GetCursor()
|
||||
Cursor& TextBuffer::GetCursor() noexcept
|
||||
{
|
||||
return _cursor;
|
||||
}
|
||||
|
||||
const Cursor& TextBuffer::GetCursor() const
|
||||
const Cursor& TextBuffer::GetCursor() const noexcept
|
||||
{
|
||||
return _cursor;
|
||||
}
|
||||
@@ -795,7 +799,7 @@ void TextBuffer::Reset()
|
||||
// - newSize - new size of screen.
|
||||
// Return Value:
|
||||
// - Success if successful. Invalid parameter if screen buffer size is unexpected. No memory if allocation failed.
|
||||
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize) noexcept
|
||||
[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize)
|
||||
{
|
||||
RETURN_HR_IF(E_INVALIDARG, newSize.X < 0 || newSize.Y < 0);
|
||||
|
||||
@@ -812,7 +816,7 @@ void TextBuffer::Reset()
|
||||
// rotate rows until the top row is at index 0
|
||||
try
|
||||
{
|
||||
const ROW& newTopRow = _storage[TopRowIndex];
|
||||
const ROW& newTopRow = _storage.at(TopRowIndex);
|
||||
while (&newTopRow != &_storage.front())
|
||||
{
|
||||
_storage.push_back(std::move(_storage.front()));
|
||||
@@ -843,12 +847,12 @@ void TextBuffer::Reset()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const
|
||||
const UnicodeStorage& TextBuffer::GetUnicodeStorage() const noexcept
|
||||
{
|
||||
return _unicodeStorage;
|
||||
}
|
||||
|
||||
UnicodeStorage& TextBuffer::GetUnicodeStorage()
|
||||
UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _unicodeStorage;
|
||||
}
|
||||
@@ -923,7 +927,7 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
|
||||
}
|
||||
|
||||
THROW_HR_IF(E_FAIL, Row.GetId() == _firstRow);
|
||||
return _storage[prevRowIndex];
|
||||
return _storage.at(prevRowIndex);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -932,7 +936,7 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row)
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - This buffer's current render target.
|
||||
Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget()
|
||||
Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcept
|
||||
{
|
||||
return _renderTarget;
|
||||
}
|
||||
@@ -977,9 +981,9 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe
|
||||
std::vector<COLORREF> selectionBkAttr;
|
||||
|
||||
// preallocate to avoid reallocs
|
||||
selectionText.reserve(highlight.Width() + 2); // + 2 for \r\n if we munged it
|
||||
selectionFgAttr.reserve(highlight.Width() + 2);
|
||||
selectionBkAttr.reserve(highlight.Width() + 2);
|
||||
selectionText.reserve(gsl::narrow<size_t>(highlight.Width()) + 2); // + 2 for \r\n if we munged it
|
||||
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)
|
||||
@@ -998,6 +1002,8 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe
|
||||
selectionBkAttr.push_back(CellBkAttr);
|
||||
}
|
||||
}
|
||||
#pragma warning(suppress : 26444)
|
||||
// TODO GH 2675: figure out why there's custom construction/destruction happening here
|
||||
it++;
|
||||
}
|
||||
|
||||
@@ -1059,6 +1065,10 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: GH 602 the font name needs to be passed and stored around as an actual bounded type, not an implicit bounds on LF_FACESIZE
|
||||
const auto faceLength = wcsnlen_s(fontFaceName, LF_FACESIZE);
|
||||
const std::wstring_view faceNameView{ fontFaceName, faceLength };
|
||||
|
||||
std::ostringstream htmlBuilder;
|
||||
|
||||
// First we have to add some standard
|
||||
@@ -1084,12 +1094,9 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
htmlBuilder << ";";
|
||||
|
||||
htmlBuilder << "font-family:";
|
||||
if (fontFaceName[0] != '\0')
|
||||
{
|
||||
htmlBuilder << "'";
|
||||
htmlBuilder << ConvertToA(CP_UTF8, fontFaceName);
|
||||
htmlBuilder << "',";
|
||||
}
|
||||
htmlBuilder << "'";
|
||||
htmlBuilder << ConvertToA(CP_UTF8, faceNameView);
|
||||
htmlBuilder << "',";
|
||||
// even with different font, add monospace as fallback
|
||||
htmlBuilder << "monospace;";
|
||||
|
||||
@@ -1109,7 +1116,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
bool hasWrittenAnyText = false;
|
||||
std::optional<COLORREF> fgColor = std::nullopt;
|
||||
std::optional<COLORREF> bkColor = std::nullopt;
|
||||
for (UINT row = 0; row < rows.text.size(); row++)
|
||||
for (size_t row = 0; row < rows.text.size(); row++)
|
||||
{
|
||||
size_t startOffset = 0;
|
||||
|
||||
@@ -1118,25 +1125,25 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
htmlBuilder << "<BR>";
|
||||
}
|
||||
|
||||
for (UINT col = 0; col < rows.text[row].length(); col++)
|
||||
for (size_t col = 0; col < rows.text.at(row).length(); col++)
|
||||
{
|
||||
// do not include \r nor \n as they don't have attributes
|
||||
// and are not HTML friendly. For line break use '<BR>' instead.
|
||||
bool isLastCharInRow =
|
||||
col == rows.text[row].length() - 1 ||
|
||||
rows.text[row][col + 1] == '\r' ||
|
||||
rows.text[row][col + 1] == '\n';
|
||||
const bool isLastCharInRow =
|
||||
col == rows.text.at(row).length() - 1 ||
|
||||
rows.text.at(row).at(col + 1) == '\r' ||
|
||||
rows.text.at(row).at(col + 1) == '\n';
|
||||
|
||||
bool colorChanged = false;
|
||||
if (!fgColor.has_value() || rows.FgAttr[row][col] != fgColor.value())
|
||||
if (!fgColor.has_value() || rows.FgAttr.at(row).at(col) != fgColor.value())
|
||||
{
|
||||
fgColor = rows.FgAttr[row][col];
|
||||
fgColor = rows.FgAttr.at(row).at(col);
|
||||
colorChanged = true;
|
||||
}
|
||||
|
||||
if (!bkColor.has_value() || rows.BkAttr[row][col] != bkColor.value())
|
||||
if (!bkColor.has_value() || rows.BkAttr.at(row).at(col) != bkColor.value())
|
||||
{
|
||||
bkColor = rows.BkAttr[row][col];
|
||||
bkColor = rows.BkAttr.at(row).at(col);
|
||||
colorChanged = true;
|
||||
}
|
||||
|
||||
@@ -1145,7 +1152,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
{
|
||||
// note: this should be escaped (for '<', '>', and '&'),
|
||||
// however MS Word doesn't appear to support HTML entities
|
||||
htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text[row].data() + startOffset, col - startOffset + includeCurrent));
|
||||
htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent));
|
||||
startOffset = col;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
~TextBuffer() = default;
|
||||
|
||||
// Used for duplicating properties to another text buffer
|
||||
void CopyProperties(const TextBuffer& OtherBuffer);
|
||||
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
|
||||
|
||||
// row manipulation
|
||||
const ROW& GetRowByOffset(const size_t index) const;
|
||||
@@ -107,16 +107,16 @@ public:
|
||||
COORD GetLastNonSpaceCharacter() const;
|
||||
COORD GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const;
|
||||
|
||||
Cursor& GetCursor();
|
||||
const Cursor& GetCursor() const;
|
||||
Cursor& GetCursor() noexcept;
|
||||
const Cursor& GetCursor() const noexcept;
|
||||
|
||||
const SHORT GetFirstRowIndex() const;
|
||||
const SHORT GetFirstRowIndex() const noexcept;
|
||||
|
||||
const Microsoft::Console::Types::Viewport GetSize() const;
|
||||
|
||||
void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta);
|
||||
|
||||
UINT TotalRowCount() const;
|
||||
UINT TotalRowCount() const noexcept;
|
||||
|
||||
[[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept;
|
||||
|
||||
@@ -124,12 +124,12 @@ public:
|
||||
|
||||
void Reset();
|
||||
|
||||
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept;
|
||||
[[nodiscard]] HRESULT ResizeTraditional(const COORD newSize);
|
||||
|
||||
const UnicodeStorage& GetUnicodeStorage() const;
|
||||
UnicodeStorage& GetUnicodeStorage();
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
|
||||
Microsoft::Console::Render::IRenderTarget& GetRenderTarget();
|
||||
Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept;
|
||||
|
||||
class TextAndColor
|
||||
{
|
||||
@@ -165,7 +165,7 @@ private:
|
||||
|
||||
Microsoft::Console::Render::IRenderTarget& _renderTarget;
|
||||
|
||||
void _SetFirstRowIndex(const SHORT FirstRowIndex);
|
||||
void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept;
|
||||
|
||||
COORD _GetPreviousFromCursor() const;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ TextBufferCellIterator::operator bool() const noexcept
|
||||
// - it - The other iterator to compare to this one.
|
||||
// Return Value:
|
||||
// - True if it's the same text buffer and same cell position. False otherwise.
|
||||
bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const noexcept
|
||||
bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const
|
||||
{
|
||||
return _pos == it._pos &&
|
||||
&_buffer == &it._buffer &&
|
||||
@@ -81,7 +81,7 @@ bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const
|
||||
// - it - The other iterator to compare to this one.
|
||||
// Return Value:
|
||||
// - True if it's the same text buffer and different cell position or if they're different buffers. False otherwise.
|
||||
bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const noexcept
|
||||
bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const
|
||||
{
|
||||
return !(*this == it);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ void TextBufferCellIterator::_SetPos(const COORD newPos)
|
||||
|
||||
if (newPos.X != _pos.X)
|
||||
{
|
||||
const ptrdiff_t diff = newPos.X - _pos.X;
|
||||
const auto diff = gsl::narrow_cast<ptrdiff_t>(newPos.X) - gsl::narrow_cast<ptrdiff_t>(_pos.X);
|
||||
_attrIter += diff;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,10 @@ public:
|
||||
TextBufferCellIterator(const TextBuffer& buffer, COORD pos);
|
||||
TextBufferCellIterator(const TextBuffer& buffer, COORD pos, const Microsoft::Console::Types::Viewport limits);
|
||||
|
||||
~TextBufferCellIterator() = default;
|
||||
|
||||
operator bool() const noexcept;
|
||||
|
||||
bool operator==(const TextBufferCellIterator& it) const noexcept;
|
||||
bool operator!=(const TextBufferCellIterator& it) const noexcept;
|
||||
bool operator==(const TextBufferCellIterator& it) const;
|
||||
bool operator!=(const TextBufferCellIterator& it) const;
|
||||
|
||||
TextBufferCellIterator& operator+=(const ptrdiff_t& movement);
|
||||
TextBufferCellIterator& operator-=(const ptrdiff_t& movement);
|
||||
|
||||
@@ -16,7 +16,7 @@ using namespace Microsoft::Console::Types;
|
||||
// - Narrows the view of a cell iterator into a text only iterator.
|
||||
// Arguments:
|
||||
// - A cell iterator
|
||||
TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) :
|
||||
TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) noexcept :
|
||||
TextBufferCellIterator(cellIt)
|
||||
{
|
||||
}
|
||||
@@ -25,7 +25,8 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel
|
||||
// - Returns the text information from the text buffer position addressed by this iterator.
|
||||
// Return Value:
|
||||
// - Read only UTF-16 text data
|
||||
const std::wstring_view TextBufferTextIterator::operator*() const
|
||||
// TODO GH 2682, fix design so this doesn't have to be suppressed.
|
||||
[[gsl::suppress(26434)]] const std::wstring_view TextBufferTextIterator::operator*() const noexcept
|
||||
{
|
||||
return _view.Chars();
|
||||
}
|
||||
@@ -34,7 +35,8 @@ const std::wstring_view TextBufferTextIterator::operator*() const
|
||||
// - Returns the text information from the text buffer position addressed by this iterator.
|
||||
// Return Value:
|
||||
// - Read only UTF-16 text data
|
||||
const std::wstring_view* TextBufferTextIterator::operator->() const
|
||||
// TODO GH 2682, fix design so this doesn't have to be suppressed.
|
||||
[[gsl::suppress(26434)]] const std::wstring_view* TextBufferTextIterator::operator->() const noexcept
|
||||
{
|
||||
return &_view.Chars();
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ class SCREEN_INFORMATION;
|
||||
class TextBufferTextIterator final : public TextBufferCellIterator
|
||||
{
|
||||
public:
|
||||
TextBufferTextIterator(const TextBufferCellIterator& cellIter);
|
||||
TextBufferTextIterator(const TextBufferCellIterator& cellIter) noexcept;
|
||||
|
||||
const std::wstring_view operator*() const;
|
||||
const std::wstring_view* operator->() const;
|
||||
const std::wstring_view operator*() const noexcept;
|
||||
const std::wstring_view* operator->() const noexcept;
|
||||
|
||||
protected:
|
||||
#if UNIT_TESTING
|
||||
|
||||
@@ -32,6 +32,9 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TEST_METHOD(TryCreateWinRTType);
|
||||
TEST_METHOD(ValidateProfilesExist);
|
||||
TEST_METHOD(FindMissingProfile);
|
||||
TEST_METHOD(MakeSettingsForProfileThatDoesntExist);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
TEST_METHOD(ValidateDefaultProfileExists);
|
||||
TEST_METHOD(ValidateDuplicateProfiles);
|
||||
TEST_METHOD(ValidateManyWarnings);
|
||||
@@ -127,6 +130,154 @@ namespace TerminalAppLocalTests
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::FindMissingProfile()
|
||||
{
|
||||
// Test that CascadiaSettings::FindProfile returns null for a GUID that
|
||||
// doesn't exist
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
|
||||
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
const Profile* const profile1 = settings->FindProfile(guid1);
|
||||
const Profile* const profile2 = settings->FindProfile(guid2);
|
||||
const Profile* const profile3 = settings->FindProfile(guid3);
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile1);
|
||||
VERIFY_IS_NOT_NULL(profile2);
|
||||
VERIFY_IS_NULL(profile3);
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", profile1->GetName());
|
||||
VERIFY_ARE_EQUAL(L"profile1", profile2->GetName());
|
||||
}
|
||||
|
||||
void SettingsTests::MakeSettingsForProfileThatDoesntExist()
|
||||
{
|
||||
// Test that MakeSettings throws when the GUID doesn't exist
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
|
||||
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid3 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-3333-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings = settings->MakeSettings(guid1);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings = settings->MakeSettings(guid2);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings = settings->MakeSettings(guid3);
|
||||
VERIFY_IS_TRUE(false, L"This call to MakeSettings should fail");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(true, L"This call to MakeSettings successfully failed");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings = settings->MakeSettings(std::nullopt);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::MakeSettingsForDefaultProfileThatDoesntExist()
|
||||
{
|
||||
// Test that MakeSettings _doesnt_ throw when we load settings with a
|
||||
// defaultProfile that's not in the list, we validate the settings, and
|
||||
// then call MakeSettings(nullopt). The validation should ensure that
|
||||
// the default profile is something reasonable
|
||||
const std::string settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 2
|
||||
}
|
||||
]
|
||||
})" };
|
||||
const auto settingsJsonObj = VerifyParseSucceeded(settingsString);
|
||||
auto settings = CascadiaSettings::FromJson(settingsJsonObj);
|
||||
settings->_ValidateSettings();
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings->_warnings.size());
|
||||
VERIFY_ARE_EQUAL(2u, settings->_profiles.size());
|
||||
VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid());
|
||||
try
|
||||
{
|
||||
auto terminalSettings = settings->MakeSettings(std::nullopt);
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"This call to MakeSettings should succeed");
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::ValidateDefaultProfileExists()
|
||||
{
|
||||
const std::string goodProfiles{ R"(
|
||||
|
||||
@@ -31,7 +31,8 @@ namespace TerminalAppLocalTests
|
||||
// deploying the appx takes a bit, so use sparingly (though it will
|
||||
// deploy once per class when used like this.)
|
||||
BEGIN_TEST_CLASS(TabTests)
|
||||
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_METHOD_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_METHOD_PROPERTY(L"UAP:Host", L"PackagedCwa")
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TerminalApp.LocalTests.AppxManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
@@ -43,6 +44,8 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(TryCreateXamlObjects);
|
||||
TEST_METHOD(TryCreateTab);
|
||||
|
||||
TEST_METHOD(TryDuplicateBadTab);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
@@ -107,4 +110,16 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(newTab);
|
||||
}
|
||||
|
||||
void TabTests::TryDuplicateBadTab()
|
||||
{
|
||||
// Create a tab with a profile with GUID A
|
||||
// Reload the settings so that GUID A is no longer in the list of profiles
|
||||
// Try calling _DuplicateTabViewItem on tab A
|
||||
// No new tab should be created (and more importantly, the app should not crash)
|
||||
|
||||
// This is a tests that was inspired by GH#2455, but at the time,
|
||||
// GH#2472 was still not solved, so this test was not possible to be
|
||||
// authored.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<!-- If you encounter errors deploying the test because these aren't
|
||||
installed, try deploying the actual app package first. Sideloading this test
|
||||
package doesn't install these dependencies if they're not already installed.
|
||||
-->
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,9 @@
|
||||
|
||||
#include "Tab.h"
|
||||
#include "CascadiaSettings.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "App.g.h"
|
||||
#include "App.base.h"
|
||||
#include "ScopedResourceLoader.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
@@ -25,8 +25,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
public:
|
||||
App();
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
~App() = default;
|
||||
|
||||
void Create();
|
||||
void LoadSettings();
|
||||
@@ -34,15 +33,15 @@ namespace winrt::TerminalApp::implementation
|
||||
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
|
||||
bool GetShowTabsInTitlebar();
|
||||
|
||||
~App() = default;
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
|
||||
hstring GetTitle();
|
||||
hstring Title();
|
||||
void TitlebarClicked();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs);
|
||||
DECLARE_EVENT(LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::UIElement);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
|
||||
private:
|
||||
@@ -50,96 +49,37 @@ namespace winrt::TerminalApp::implementation
|
||||
// the ctor, you're going to have a bad time. It'll mysteriously fail to
|
||||
// activate the app.
|
||||
// ALSO: If you add any UIElements as roots here, make sure they're
|
||||
// updated in _ApplyTheme. The two roots currently are _root and _tabRow
|
||||
// (which is a root when the tabs are in the titlebar.)
|
||||
Windows::UI::Xaml::Controls::Control _root{ nullptr };
|
||||
Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr };
|
||||
TerminalApp::TabRowControl _tabRow{ nullptr };
|
||||
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
|
||||
Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
|
||||
// updated in _ApplyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
|
||||
std::vector<std::shared_ptr<Tab>> _tabs;
|
||||
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
|
||||
|
||||
std::unique_ptr<::TerminalApp::CascadiaSettings> _settings;
|
||||
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
|
||||
|
||||
HRESULT _settingsLoadedResult;
|
||||
winrt::hstring _settingsLoadExceptionText{};
|
||||
|
||||
bool _loadedInitialSettings;
|
||||
std::shared_mutex _dialogLock;
|
||||
|
||||
ScopedResourceLoader _resourceLoader;
|
||||
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
std::shared_mutex _dialogLock;
|
||||
|
||||
std::atomic<bool> _settingsReloadQueued{ false };
|
||||
|
||||
void _CreateNewTabFlyout();
|
||||
void _OpenNewTabDropdown();
|
||||
|
||||
fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& titleElement,
|
||||
const winrt::Windows::Foundation::IInspectable& contentElement,
|
||||
const winrt::hstring& closeButtonText);
|
||||
void _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
|
||||
void _ShowAboutDialog();
|
||||
fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& sender, winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);
|
||||
void _ShowLoadWarningsDialog();
|
||||
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
|
||||
|
||||
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
|
||||
void _LoadSettings();
|
||||
void _OpenSettings();
|
||||
|
||||
void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;
|
||||
|
||||
void _RegisterSettingsChange();
|
||||
fire_and_forget _DispatchReloadSettings();
|
||||
void _ReloadSettings();
|
||||
|
||||
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
void _UpdateTabView();
|
||||
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
|
||||
void _UpdateTitle(std::shared_ptr<Tab> tab);
|
||||
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
|
||||
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
|
||||
|
||||
void _OpenNewTab(std::optional<int> profileIndex);
|
||||
void _DuplicateTabViewItem();
|
||||
void _CloseFocusedTab();
|
||||
void _CloseFocusedPane();
|
||||
void _SelectNextTab(const bool bMoveRight);
|
||||
bool _SelectTab(const int tabIndex);
|
||||
|
||||
void _SetFocusedTabIndex(int tabIndex);
|
||||
int _GetFocusedTabIndex() const;
|
||||
|
||||
void _Scroll(int delta);
|
||||
bool _CopyText(const bool trimTrailingWhitespace);
|
||||
void _PasteText();
|
||||
void _SplitVertical(const std::optional<GUID>& profileGuid);
|
||||
void _SplitHorizontal(const std::optional<GUID>& profileGuid);
|
||||
void _SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid);
|
||||
|
||||
// Todo: add more event implementations here
|
||||
// MSFT:20641986: Add keybindings for New Window
|
||||
void _ScrollPage(int delta);
|
||||
void _ResizePane(const Direction& direction);
|
||||
void _MoveFocus(const Direction& direction);
|
||||
|
||||
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
|
||||
void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs);
|
||||
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
|
||||
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
|
||||
void _OnContentSizeChanged(const IInspectable& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e);
|
||||
|
||||
void _RemoveTabViewItem(const IInspectable& tabViewItem);
|
||||
|
||||
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
|
||||
|
||||
static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);
|
||||
@@ -150,30 +90,6 @@ namespace winrt::TerminalApp::implementation
|
||||
void _PasteFromClipboardHandler(const IInspectable& sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs);
|
||||
|
||||
static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
#pragma endregion
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,15 @@ namespace TerminalApp
|
||||
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
|
||||
String Title { get; };
|
||||
|
||||
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
|
||||
event Microsoft.Terminal.TerminalControl.TitleChangedEventArgs TitleChanged;
|
||||
event LastTabClosedEventArgs LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
|
||||
|
||||
String GetTitle();
|
||||
|
||||
void TitlebarClicked();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,112 +26,112 @@ namespace winrt
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void App::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_OpenNewTab(std::nullopt);
|
||||
args.Handled(true);
|
||||
}
|
||||
void App::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_OpenNewTabDropdown();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleDuplicateTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_DuplicateTabViewItem();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleCloseTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleCloseTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_CloseFocusedTab();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleClosePane(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleClosePane(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_CloseFocusedPane();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleScrollUp(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_Scroll(-1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleScrollDown(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleScrollDown(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_Scroll(1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleNextTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_SelectNextTab(true);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandlePrevTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_SelectNextTab(false);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleSplitVertical(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleSplitVertical(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_SplitVertical(std::nullopt);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleSplitHorizontal(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleSplitHorizontal(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_SplitHorizontal(std::nullopt);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleScrollUpPage(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_ScrollPage(-1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleScrollDownPage(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleScrollDownPage(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_ScrollPage(1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleOpenSettings(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_OpenSettings();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandlePasteText(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandlePasteText(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_PasteText();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void App::_HandleNewTabWithProfile(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleNewTabWithProfile(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::NewTabWithProfileArgs>())
|
||||
{
|
||||
@@ -140,8 +140,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void App::_HandleSwitchToTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleSwitchToTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SwitchToTabArgs>())
|
||||
{
|
||||
@@ -150,8 +150,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void App::_HandleResizePane(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleResizePane(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::ResizePaneArgs>())
|
||||
{
|
||||
@@ -160,8 +160,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void App::_HandleMoveFocus(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleMoveFocus(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::MoveFocusArgs>())
|
||||
{
|
||||
@@ -170,8 +170,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void App::_HandleCopyText(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleCopyText(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::CopyTextArgs>())
|
||||
{
|
||||
|
||||
@@ -451,7 +451,7 @@ TerminalSettings CascadiaSettings::MakeSettings(std::optional<GUID> profileGuidA
|
||||
const Profile* const profile = FindProfile(profileGuid);
|
||||
if (profile == nullptr)
|
||||
{
|
||||
throw E_INVALIDARG;
|
||||
THROW_HR(E_INVALIDARG);
|
||||
}
|
||||
|
||||
TerminalSettings result = profile->CreateTerminalSettings(_globals.GetColorSchemes());
|
||||
@@ -756,7 +756,7 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles()
|
||||
|
||||
// Try collecting all the unique guids. If we ever encounter a guid that's
|
||||
// already in the set, then we need to delete that profile.
|
||||
for (int i = 0; i < _profiles.size(); i++)
|
||||
for (size_t i = 0; i < _profiles.size(); i++)
|
||||
{
|
||||
if (!uniqueGuids.insert(_profiles.at(i).GetGuid()).second)
|
||||
{
|
||||
|
||||
@@ -164,26 +164,26 @@ bool Pane::ResizePane(const Direction& direction)
|
||||
{
|
||||
return _Resize(direction);
|
||||
}
|
||||
else
|
||||
|
||||
// If neither of our children were the focused leaf, then recurse into
|
||||
// our children and see if they can handle the resize.
|
||||
// For each child, if it has a focused descendant, try having that child
|
||||
// handle the resize.
|
||||
// If the child wasn't able to handle the resize, it's possible that
|
||||
// there were no descendants with a separator the correct direction. If
|
||||
// our separator _is_ the correct direction, then we should be the pane
|
||||
// to resize. Otherwise, just return false, as we couldn't handle it
|
||||
// either.
|
||||
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
|
||||
{
|
||||
// If neither of our children were the focused leaf, then recurse into
|
||||
// our children and see if they can handle the resize.
|
||||
// For each child, if it has a focused descendant, try having that child
|
||||
// handle the resize.
|
||||
// If the child wasn't able to handle the resize, it's possible that
|
||||
// there were no descendants with a separator the correct direction. If
|
||||
// our separator _is_ the correct direction, then we should be the pane
|
||||
// to resize. Otherwise, just return false, as we couldn't handle it
|
||||
// either.
|
||||
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->ResizePane(direction) || _Resize(direction);
|
||||
}
|
||||
else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->ResizePane(direction) || _Resize(direction);
|
||||
}
|
||||
return _firstChild->ResizePane(direction) || _Resize(direction);
|
||||
}
|
||||
|
||||
if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->ResizePane(direction) || _Resize(direction);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -253,26 +253,26 @@ bool Pane::NavigateFocus(const Direction& direction)
|
||||
{
|
||||
return _NavigateFocus(direction);
|
||||
}
|
||||
else
|
||||
|
||||
// If neither of our children were the focused leaf, then recurse into
|
||||
// our children and see if they can handle the focus move.
|
||||
// For each child, if it has a focused descendant, try having that child
|
||||
// handle the focus move.
|
||||
// If the child wasn't able to handle the focus move, it's possible that
|
||||
// there were no descendants with a separator the correct direction. If
|
||||
// our separator _is_ the correct direction, then we should be the pane
|
||||
// to move focus into our other child. Otherwise, just return false, as
|
||||
// we couldn't handle it either.
|
||||
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
|
||||
{
|
||||
// If neither of our children were the focused leaf, then recurse into
|
||||
// our children and see if they can handle the focus move.
|
||||
// For each child, if it has a focused descendant, try having that child
|
||||
// handle the focus move.
|
||||
// If the child wasn't able to handle the focus move, it's possible that
|
||||
// there were no descendants with a separator the correct direction. If
|
||||
// our separator _is_ the correct direction, then we should be the pane
|
||||
// to move focus into our other child. Otherwise, just return false, as
|
||||
// we couldn't handle it either.
|
||||
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction);
|
||||
}
|
||||
else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction);
|
||||
}
|
||||
return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction);
|
||||
}
|
||||
|
||||
if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -348,15 +348,13 @@ std::shared_ptr<Pane> Pane::GetFocusedPane()
|
||||
{
|
||||
return _lastFocused ? shared_from_this() : nullptr;
|
||||
}
|
||||
else
|
||||
|
||||
auto firstFocused = _firstChild->GetFocusedPane();
|
||||
if (firstFocused != nullptr)
|
||||
{
|
||||
auto firstFocused = _firstChild->GetFocusedPane();
|
||||
if (firstFocused != nullptr)
|
||||
{
|
||||
return firstFocused;
|
||||
}
|
||||
return _secondChild->GetFocusedPane();
|
||||
return firstFocused;
|
||||
}
|
||||
return _secondChild->GetFocusedPane();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -778,110 +776,58 @@ void Pane::_ApplySplitDefinitions()
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the pane can be split vertically
|
||||
// - Determines whether the pane can be split
|
||||
// Arguments:
|
||||
// - splitType: what type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the pane can be split vertically. False otherwise.
|
||||
bool Pane::CanSplitVertical()
|
||||
// - True if the pane can be split. False otherwise.
|
||||
bool Pane::CanSplit(SplitState splitType)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
if (_IsLeaf())
|
||||
{
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->CanSplitVertical();
|
||||
}
|
||||
else if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->CanSplitVertical();
|
||||
}
|
||||
|
||||
return false;
|
||||
return _CanSplit(splitType);
|
||||
}
|
||||
|
||||
return _CanSplit(SplitState::Vertical);
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->CanSplit(splitType);
|
||||
}
|
||||
|
||||
if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->CanSplit(splitType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Vertically split the focused pane in our tree of panes, and place the given
|
||||
// - Split the focused pane in our tree of panes, and place the given
|
||||
// TermControl into the newly created pane. If we're the focused pane, then
|
||||
// we'll create two new children, and place them side-by-side in our Grid.
|
||||
// Arguments:
|
||||
// - splitType: what type of split we want to create.
|
||||
// - profile: The profile GUID to associate with the newly created pane.
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::SplitVertical(const GUID& profile, const TermControl& control)
|
||||
void Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
{
|
||||
// If we're not the leaf, recurse into our children to split them.
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
_firstChild->SplitVertical(profile, control);
|
||||
_firstChild->Split(splitType, profile, control);
|
||||
}
|
||||
else if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
_secondChild->SplitVertical(profile, control);
|
||||
_secondChild->Split(splitType, profile, control);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_Split(SplitState::Vertical, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the pane can be split horizontally
|
||||
// Arguments:
|
||||
// - splitType: what type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the pane can be split horizontally. False otherwise.
|
||||
bool Pane::CanSplitHorizontal()
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->CanSplitHorizontal();
|
||||
}
|
||||
else if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->CanSplitHorizontal();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return _CanSplit(SplitState::Horizontal);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Horizontally split the focused pane in our tree of panes, and place the given
|
||||
// TermControl into the newly created pane. If we're the focused pane, then
|
||||
// we'll create two new children, and place them side-by-side in our Grid.
|
||||
// Arguments:
|
||||
// - profile: The profile GUID to associate with the newly created pane.
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
_firstChild->SplitHorizontal(profile, control);
|
||||
}
|
||||
else if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
_secondChild->SplitHorizontal(profile, control);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_Split(SplitState::Horizontal, profile, control);
|
||||
_Split(splitType, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -892,8 +838,6 @@ void Pane::SplitHorizontal(const GUID& profile, const TermControl& control)
|
||||
// - True if the pane can be split. False otherwise.
|
||||
bool Pane::_CanSplit(SplitState splitType)
|
||||
{
|
||||
const bool changeWidth = _splitState == SplitState::Vertical;
|
||||
|
||||
const Size actualSize{ gsl::narrow_cast<float>(_root.ActualWidth()),
|
||||
gsl::narrow_cast<float>(_root.ActualHeight()) };
|
||||
|
||||
@@ -1006,14 +950,12 @@ Size Pane::_GetMinSize() const
|
||||
{
|
||||
return _control.MinimumSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto firstSize = _firstChild->_GetMinSize();
|
||||
const auto secondSize = _secondChild->_GetMinSize();
|
||||
const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0);
|
||||
const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0);
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
const auto firstSize = _firstChild->_GetMinSize();
|
||||
const auto secondSize = _secondChild->_GetMinSize();
|
||||
const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0);
|
||||
const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0);
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
DEFINE_EVENT(Pane, Closed, _closedHandlers, ConnectionClosedEventArgs);
|
||||
|
||||
@@ -49,11 +49,8 @@ public:
|
||||
bool ResizePane(const winrt::TerminalApp::Direction& direction);
|
||||
bool NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
bool CanSplitHorizontal();
|
||||
void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
bool CanSplitVertical();
|
||||
void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
bool CanSplit(SplitState splitType);
|
||||
void Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
void Close();
|
||||
|
||||
|
||||
@@ -204,47 +204,28 @@ void Tab::Scroll(const int delta)
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the focused pane has sufficient space to be split vertically.
|
||||
// - Determines whether the focused pane has sufficient space to be split.
|
||||
// Arguments:
|
||||
// - splitType: The type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the focused pane can be split horizontally. False otherwise.
|
||||
bool Tab::CanAddVerticalSplit()
|
||||
// - True if the focused pane can be split. False otherwise.
|
||||
bool Tab::CanSplitPane(Pane::SplitState splitType)
|
||||
{
|
||||
return _rootPane->CanSplitVertical();
|
||||
return _rootPane->CanSplit(splitType);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Vertically split the focused pane in our tree of panes, and place the
|
||||
// - Split the focused pane in our tree of panes, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
// Arguments:
|
||||
// - splitType: The type of split we want to create.
|
||||
// - profile: The profile GUID to associate with the newly created pane.
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::AddVerticalSplit(const GUID& profile, TermControl& control)
|
||||
void Tab::SplitPane(Pane::SplitState splitType, const GUID& profile, TermControl& control)
|
||||
{
|
||||
_rootPane->SplitVertical(profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the focused pane has sufficient space to be split horizontally.
|
||||
// Return Value:
|
||||
// - True if the focused pane can be split horizontally. False otherwise.
|
||||
bool Tab::CanAddHorizontalSplit()
|
||||
{
|
||||
return _rootPane->CanSplitHorizontal();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Horizontally split the focused pane in our tree of panes, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
// Arguments:
|
||||
// - profile: The profile GUID to associate with the newly created pane.
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::AddHorizontalSplit(const GUID& profile, TermControl& control)
|
||||
{
|
||||
_rootPane->SplitHorizontal(profile, control);
|
||||
_rootPane->Split(splitType, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -19,10 +19,9 @@ public:
|
||||
void SetFocused(const bool focused);
|
||||
|
||||
void Scroll(const int delta);
|
||||
bool CanAddVerticalSplit();
|
||||
void AddVerticalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
bool CanAddHorizontalSplit();
|
||||
void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
bool CanSplitPane(Pane::SplitState splitType);
|
||||
void SplitPane(Pane::SplitState splitType, const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
void UpdateFocus();
|
||||
void UpdateIcon(const winrt::hstring iconPath);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,144 @@
|
||||
#include "winrt/Microsoft.UI.Xaml.Controls.h"
|
||||
|
||||
#include "TerminalPage.g.h"
|
||||
#include "Tab.h"
|
||||
#include "CascadiaSettings.h"
|
||||
#include "Profile.h"
|
||||
#include "ScopedResourceLoader.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TerminalPage : TerminalPageT<TerminalPage>
|
||||
{
|
||||
public:
|
||||
TerminalPage();
|
||||
|
||||
TerminalPage(std::shared_ptr<ScopedResourceLoader> resourceLoader);
|
||||
|
||||
void SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI);
|
||||
|
||||
void Create();
|
||||
|
||||
hstring Title();
|
||||
|
||||
void ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey);
|
||||
|
||||
void TitlebarClicked();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(ShowDialog, _showDialogHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::Controls::ContentDialog);
|
||||
|
||||
private:
|
||||
// If you add controls here, but forget to null them either here or in
|
||||
// the ctor, you're going to have a bad time. It'll mysteriously fail to
|
||||
// activate the app.
|
||||
// ALSO: If you add any UIElements as roots here, make sure they're
|
||||
// updated in App::_ApplyTheme. The roots currently is _tabRow
|
||||
// (which is a root when the tabs are in the titlebar.)
|
||||
Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr };
|
||||
TerminalApp::TabRowControl _tabRow{ nullptr };
|
||||
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
|
||||
Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
|
||||
|
||||
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
|
||||
|
||||
std::vector<std::shared_ptr<Tab>> _tabs;
|
||||
|
||||
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
|
||||
|
||||
void _ShowAboutDialog();
|
||||
|
||||
void _CreateNewTabFlyout();
|
||||
void _OpenNewTabDropdown();
|
||||
void _OpenNewTab(std::optional<int> profileIndex);
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
|
||||
|
||||
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;
|
||||
|
||||
void _UpdateTitle(std::shared_ptr<Tab> tab);
|
||||
void _UpdateTabIcon(std::shared_ptr<Tab> tab);
|
||||
void _UpdateTabView();
|
||||
void _DuplicateTabViewItem();
|
||||
void _RemoveTabViewItem(const IInspectable& tabViewItem);
|
||||
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
|
||||
|
||||
void _SelectNextTab(const bool bMoveRight);
|
||||
bool _SelectTab(const int tabIndex);
|
||||
void _MoveFocus(const Direction& direction);
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl();
|
||||
int _GetFocusedTabIndex() const;
|
||||
void _SetFocusedTabIndex(int tabIndex);
|
||||
void _CloseFocusedTab();
|
||||
void _CloseFocusedPane();
|
||||
|
||||
// Todo: add more event implementations here
|
||||
// MSFT:20641986: Add keybindings for New Window
|
||||
void _Scroll(int delta);
|
||||
void _SplitVertical(const std::optional<GUID>& profileGuid);
|
||||
void _SplitHorizontal(const std::optional<GUID>& profileGuid);
|
||||
void _SplitPane(const Pane::SplitState splitType, const std::optional<GUID>& profileGuid);
|
||||
void _ResizePane(const Direction& direction);
|
||||
void _ScrollPage(int delta);
|
||||
static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);
|
||||
void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord);
|
||||
|
||||
void _CopyToClipboardHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData);
|
||||
void _PasteFromClipboardHandler(const IInspectable& sender,
|
||||
const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs);
|
||||
bool _CopyText(const bool trimTrailingWhitespace);
|
||||
void _PasteText();
|
||||
static fire_and_forget PasteFromClipboard(winrt::Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs);
|
||||
|
||||
void _OpenSettings();
|
||||
fire_and_forget LaunchSettings();
|
||||
|
||||
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);
|
||||
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
|
||||
void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs);
|
||||
void _OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e);
|
||||
void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs);
|
||||
|
||||
void _RefreshUIForSettingsReload();
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
|
||||
#pragma endregion
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
import "..\App.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
TerminalPage();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.Controls.ContentDialog> ShowDialog;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,37 +574,41 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
// - S_OK otherwise
|
||||
HRESULT AzureConnection::_TenantChoiceHelper()
|
||||
{
|
||||
const auto tenantListAsArray = _tenantList.as_array();
|
||||
_maxSize = tenantListAsArray.size();
|
||||
for (int i = 0; i < _maxSize; i++)
|
||||
try
|
||||
{
|
||||
const auto& tenant = tenantListAsArray.at(i);
|
||||
const auto [tenantId, tenantDisplayName] = _crackTenant(tenant);
|
||||
_outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str()));
|
||||
const auto tenantListAsArray = _tenantList.as_array();
|
||||
_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);
|
||||
_outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str()));
|
||||
}
|
||||
_outputHandlers(winrt::to_hstring(enterTenant));
|
||||
// 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) || _closing.load();
|
||||
});
|
||||
// User might have closed the tab while we waited for input
|
||||
if (_closing.load())
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const auto& chosenTenant = tenantListAsArray.at(_tenantNumber);
|
||||
std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant);
|
||||
|
||||
// We have to refresh now that we have the tenantID
|
||||
const auto refreshResponse = _RefreshTokens();
|
||||
_accessToken = refreshResponse.at(L"access_token").as_string();
|
||||
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
|
||||
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
|
||||
|
||||
_state = State::StoreTokens;
|
||||
return S_OK;
|
||||
}
|
||||
_outputHandlers(winrt::to_hstring(enterTenant));
|
||||
// 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) || _closing.load();
|
||||
});
|
||||
// User might have closed the tab while we waited for input
|
||||
if (_closing.load())
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const auto& chosenTenant = tenantListAsArray.at(_tenantNumber);
|
||||
std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant);
|
||||
|
||||
// We have to refresh now that we have the tenantID
|
||||
const auto refreshResponse = _RefreshTokens();
|
||||
_accessToken = refreshResponse.at(L"access_token").as_string();
|
||||
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
|
||||
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
|
||||
|
||||
_state = State::StoreTokens;
|
||||
return S_OK;
|
||||
CATCH_RETURN();
|
||||
}
|
||||
|
||||
// Method description:
|
||||
|
||||
@@ -108,6 +108,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
si,
|
||||
extraEnvVars));
|
||||
|
||||
_startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Create our own output handling thread
|
||||
// This must be done after the pipes are populated.
|
||||
// Each connection needs to make sure to drain the output from its backing host.
|
||||
@@ -209,6 +211,21 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_recievedFirstByte)
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> delta = now - _startTime;
|
||||
|
||||
TraceLoggingWrite(g_hTerminalConnectionProvider,
|
||||
"RecievedFirstByte",
|
||||
TraceLoggingDescription("An event emitted when the connection recieves the first byte"),
|
||||
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
|
||||
TraceLoggingFloat64(delta.count(), "Duration"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
_recievedFirstByte = true;
|
||||
}
|
||||
|
||||
// Convert buffer to hstring
|
||||
auto hstr{ winrt::to_hstring(strView) };
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
|
||||
bool _connected{};
|
||||
std::atomic<bool> _closing{ false };
|
||||
bool _recievedFirstByte{ false };
|
||||
std::chrono::high_resolution_clock::time_point _startTime{};
|
||||
|
||||
wil::unique_hfile _inPipe; // The pipe for writing input to
|
||||
wil::unique_hfile _outPipe; // The pipe for reading output from
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="init.cpp"/>
|
||||
<ClCompile Include="AzureConnection.cpp" Condition="'$(Platform)'!='ARM64'" />
|
||||
<ClCompile Include="AzureConnection-ARM64.cpp" Condition="'$(Platform)'=='ARM64'" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
|
||||
31
src/cascadia/TerminalConnection/init.cpp
Normal file
31
src/cascadia/TerminalConnection/init.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// Note: Generate GUID using TlgGuid.exe tool
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hTerminalConnectionProvider,
|
||||
"Microsoft.Windows.Terminal.Connection",
|
||||
// {e912fe7b-eeb6-52a5-c628-abe388e5f792}
|
||||
(0xe912fe7b, 0xeeb6, 0x52a5, 0xc6, 0x28, 0xab, 0xe3, 0x88, 0xe5, 0xf7, 0x92),
|
||||
TraceLoggingOptionMicrosoftTelemetry());
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hInstDll);
|
||||
TraceLoggingRegister(g_hTerminalConnectionProvider);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (g_hTerminalConnectionProvider)
|
||||
{
|
||||
TraceLoggingUnregister(g_hTerminalConnectionProvider);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -19,3 +19,7 @@
|
||||
#include "winrt/Windows.Security.Credentials.h"
|
||||
#include "winrt/Windows.Foundation.Collections.h"
|
||||
#include <Windows.h>
|
||||
|
||||
#include <TraceLoggingProvider.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider);
|
||||
#include <telemetry/ProjectTelemetry.h>
|
||||
|
||||
@@ -732,7 +732,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
const auto ptr = args.Pointer();
|
||||
const auto point = args.GetCurrentPoint(_root);
|
||||
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
// Ignore mouse events while the terminal does not have focus.
|
||||
// This prevents the user from selecting and copying text if they
|
||||
@@ -818,7 +818,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
const auto ptr = args.Pointer();
|
||||
const auto point = args.GetCurrentPoint(_root);
|
||||
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
if (point.Properties().IsLeftButtonPressed())
|
||||
{
|
||||
@@ -897,7 +897,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
const auto ptr = args.Pointer();
|
||||
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
|
||||
{
|
||||
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
|
||||
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
|
||||
@@ -1443,7 +1443,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
// send data up for clipboard
|
||||
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData));
|
||||
auto copyArgs = winrt::make_self<CopyToClipboardEventArgs>(winrt::hstring(textData.data(), gsl::narrow<winrt::hstring::size_type>(textData.size())), winrt::to_hstring(htmlData));
|
||||
_clipboardCopyHandlers(*this, *copyArgs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
// transfer ownership of UiaTextRanges to this new vector
|
||||
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges);
|
||||
int count = providers.size();
|
||||
int count = gsl::narrow<int>(providers.size());
|
||||
|
||||
std::vector<XamlAutomation::ITextRangeProvider> vec;
|
||||
vec.reserve(count);
|
||||
|
||||
@@ -81,7 +81,7 @@ void AppHost::Initialize()
|
||||
_app.TitleChanged({ this, &AppHost::AppTitleChanged });
|
||||
_app.LastTabClosed({ this, &AppHost::LastTabClosed });
|
||||
|
||||
AppTitleChanged(_app.GetTitle());
|
||||
_window->UpdateTitle(_app.Title());
|
||||
|
||||
// Set up the content of the application. If the app has a custom titlebar,
|
||||
// set that content as well.
|
||||
@@ -93,10 +93,11 @@ void AppHost::Initialize()
|
||||
// - Called when the app's title changes. Fires off a window message so we can
|
||||
// update the window's title on the main thread.
|
||||
// Arguments:
|
||||
// - sender: unused
|
||||
// - newTitle: the string to use as the new window title
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::AppTitleChanged(winrt::hstring newTitle)
|
||||
void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle)
|
||||
{
|
||||
_window->UpdateTitle(newTitle.c_str());
|
||||
}
|
||||
@@ -104,10 +105,11 @@ void AppHost::AppTitleChanged(winrt::hstring newTitle)
|
||||
// Method Description:
|
||||
// - Called when no tab is remaining to close the window.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// - sender: unused
|
||||
// - LastTabClosedEventArgs: unused
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::LastTabClosed()
|
||||
void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/)
|
||||
{
|
||||
_window->Close();
|
||||
}
|
||||
@@ -199,6 +201,13 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
|
||||
// If we can't resize the window, that's really okay. We can just go on with
|
||||
// the originally proposed window size.
|
||||
LOG_LAST_ERROR_IF(!succeeded);
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hWindowsTerminalProvider,
|
||||
"WindowCreated",
|
||||
TraceLoggingDescription("Event emitted upon creating the application window"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -209,7 +218,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
|
||||
// - arg: the UIElement to use as the new Titlebar content.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::_UpdateTitleBarContent(const winrt::TerminalApp::App&, const winrt::Windows::UI::Xaml::UIElement& arg)
|
||||
void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::UIElement& arg)
|
||||
{
|
||||
if (_useNonClientArea)
|
||||
{
|
||||
|
||||
@@ -14,8 +14,8 @@ public:
|
||||
AppHost() noexcept;
|
||||
virtual ~AppHost();
|
||||
|
||||
void AppTitleChanged(winrt::hstring newTitle);
|
||||
void LastTabClosed();
|
||||
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();
|
||||
|
||||
private:
|
||||
@@ -25,7 +25,7 @@ private:
|
||||
winrt::TerminalApp::App _app;
|
||||
|
||||
void _HandleCreateWindow(const HWND hwnd, const RECT proposedRect);
|
||||
void _UpdateTitleBarContent(const winrt::TerminalApp::App& sender,
|
||||
void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::UI::Xaml::UIElement& arg);
|
||||
void _UpdateTheme(const winrt::TerminalApp::App&,
|
||||
const winrt::Windows::UI::Xaml::ElementTheme& arg);
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
|
||||
|
||||
#pragma region IUiaWindow
|
||||
void ChangeViewport(const SMALL_RECT NewWindow)
|
||||
void ChangeViewport(const SMALL_RECT /*NewWindow*/)
|
||||
{
|
||||
// TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider
|
||||
// Relevant comment from zadjii-msft:
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
return BaseWindow::GetHandle();
|
||||
};
|
||||
|
||||
[[nodiscard]] HRESULT SignalUia(_In_ EVENTID id) override { return E_NOTIMPL; };
|
||||
[[nodiscard]] HRESULT SignalUia(_In_ EVENTID /*id*/) override { return E_NOTIMPL; };
|
||||
[[nodiscard]] HRESULT UiaSetTextAreaFocus() override { return E_NOTIMPL; };
|
||||
|
||||
RECT GetWindowRect() const noexcept override
|
||||
|
||||
@@ -107,7 +107,7 @@ WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWind
|
||||
|
||||
#pragma region IRawElementProviderFragment
|
||||
|
||||
IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
|
||||
IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection /*direction*/, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
|
||||
{
|
||||
RETURN_IF_FAILED(_EnsureValidHwnd());
|
||||
*ppProvider = nullptr;
|
||||
@@ -139,7 +139,7 @@ IFACEMETHODIMP WindowUiaProvider::SetFocus()
|
||||
|
||||
IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/,
|
||||
_In_ double /*y*/,
|
||||
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
|
||||
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** /*ppProvider*/)
|
||||
{
|
||||
RETURN_IF_FAILED(_EnsureValidHwnd());
|
||||
|
||||
@@ -151,7 +151,7 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/,
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
|
||||
IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** /*ppProvider*/)
|
||||
{
|
||||
RETURN_IF_FAILED(_EnsureValidHwnd());
|
||||
// TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider
|
||||
|
||||
@@ -11,6 +11,15 @@ using namespace Windows::UI::Composition;
|
||||
using namespace Windows::UI::Xaml::Hosting;
|
||||
using namespace Windows::Foundation::Numerics;
|
||||
|
||||
// Note: Generate GUID using TlgGuid.exe tool - seriously, it won't work if you
|
||||
// just generate an arbitrary GUID
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hWindowsTerminalProvider,
|
||||
"Microsoft.Windows.Terminal.Win32Host",
|
||||
// {56c06166-2e2e-5f4d-7ff3-74f4b78c87d6}
|
||||
(0x56c06166, 0x2e2e, 0x5f4d, 0x7f, 0xf3, 0x74, 0xf4, 0xb7, 0x8c, 0x87, 0xd6),
|
||||
TraceLoggingOptionMicrosoftTelemetry());
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves the string resource from the current module with the given ID
|
||||
// from the resources files. See resource.h and the .rc definitions for valid IDs.
|
||||
@@ -94,6 +103,14 @@ static void EnsureNativeArchitecture()
|
||||
|
||||
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
||||
{
|
||||
TraceLoggingRegister(g_hWindowsTerminalProvider);
|
||||
TraceLoggingWrite(
|
||||
g_hWindowsTerminalProvider,
|
||||
"ExecutableStarted",
|
||||
TraceLoggingDescription("Event emitted immediately on startup"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
// Block the user from starting if they launched the incorrect architecture version of the project.
|
||||
// This should only be applicable to developer versions. The package installation process
|
||||
// should choose and install the correct one from the bundle.
|
||||
|
||||
@@ -52,3 +52,10 @@ Abstract:
|
||||
|
||||
#include <wil/resource.h>
|
||||
#include <wil/win32_helpers.h>
|
||||
|
||||
// Including TraceLogging essentials for the binary
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <winmeta.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider);
|
||||
#include <telemetry\ProjectTelemetry.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace TerminalAppUnitTests
|
||||
"\"name\" : \"Campbell\","
|
||||
"\"purple\" : \"#881798\","
|
||||
"\"red\" : \"#C50F1F\","
|
||||
"\"white\" : \"#CCCCCC\","
|
||||
"\"white\" : \"#CCC\","
|
||||
"\"yellow\" : \"#C19C00\""
|
||||
"}" };
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<application>
|
||||
<!-- Windows 10 1903 -->
|
||||
<!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
|
||||
<!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
|
||||
<maxversiontested Id="10.0.18362.0"/>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj /Zc:twoPhase- /std:c++17 </AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<PreprocessorDefinitions Condition="'$(ConfigurationType)'=='DynamicLibrary'">_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
||||
@@ -15,6 +15,7 @@ class PolicyTests
|
||||
#ifdef __INSIDE_WINDOWS
|
||||
BEGIN_TEST_METHOD(WrongWayVerbsUAP)
|
||||
TEST_METHOD_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_METHOD_PROPERTY(L"UAP:AppxManifest", L"MUA")
|
||||
END_TEST_METHOD();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<ProjectReference Include="..\..\types\lib\types.vcxproj">
|
||||
<Project>{18d09a24-8240-42d6-8cb6-236eee820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\winconpty\winconpty.vcxproj">
|
||||
<Project>{58a03bb2-df5a-4b66-91a0-7ef3ba01269a}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Host.Tests.Feature.rc" />
|
||||
@@ -56,4 +59,10 @@
|
||||
<Import Project="$(SolutionDir)src\common.build.dll.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.tests.props" />
|
||||
</Project>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(OutDir)\conpty.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1356,24 +1356,22 @@ void DoSrvPrivateAllowCursorBlinking(SCREEN_INFORMATION& screenInfo, const bool
|
||||
if (screenInfo.IsCursorInMargins(oldCursorPosition))
|
||||
{
|
||||
// Cursor is at the top of the viewport
|
||||
const COORD bufferSize = screenInfo.GetBufferSize().Dimensions();
|
||||
// Rectangle to cut out of the existing buffer
|
||||
// Rectangle to cut out of the existing buffer. This is inclusive.
|
||||
// It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width.
|
||||
SMALL_RECT srScroll;
|
||||
srScroll.Left = 0;
|
||||
srScroll.Right = bufferSize.X;
|
||||
srScroll.Right = SHORT_MAX;
|
||||
srScroll.Top = viewport.Top;
|
||||
srScroll.Bottom = viewport.Bottom - 1;
|
||||
srScroll.Bottom = viewport.Bottom;
|
||||
// Paste coordinate for cut text above
|
||||
COORD coordDestination;
|
||||
coordDestination.X = 0;
|
||||
coordDestination.Y = viewport.Top + 1;
|
||||
|
||||
SMALL_RECT srClip = viewport;
|
||||
|
||||
Status = NTSTATUS_FROM_HRESULT(ServiceLocator::LocateGlobals().api.ScrollConsoleScreenBufferWImpl(screenInfo,
|
||||
srScroll,
|
||||
coordDestination,
|
||||
srClip,
|
||||
srScroll,
|
||||
UNICODE_SPACE,
|
||||
screenInfo.GetAttributes().GetLegacyAttributes()));
|
||||
}
|
||||
@@ -2033,13 +2031,13 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert)
|
||||
const auto cursorPosition = textBuffer.GetCursor().GetPosition();
|
||||
if (screenInfo.IsCursorInMargins(cursorPosition))
|
||||
{
|
||||
const auto screenEdges = screenInfo.GetBufferSize().ToInclusive();
|
||||
// Rectangle to cut out of the existing buffer
|
||||
// Rectangle to cut out of the existing buffer. This is inclusive.
|
||||
// It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width.
|
||||
SMALL_RECT srScroll;
|
||||
srScroll.Left = 0;
|
||||
srScroll.Right = screenEdges.Right - screenEdges.Left;
|
||||
srScroll.Right = SHORT_MAX;
|
||||
srScroll.Top = cursorPosition.Y;
|
||||
srScroll.Bottom = screenEdges.Bottom;
|
||||
srScroll.Bottom = screenInfo.GetViewport().BottomInclusive();
|
||||
// Paste coordinate for cut text above
|
||||
COORD coordDestination;
|
||||
coordDestination.X = 0;
|
||||
@@ -2052,15 +2050,25 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert)
|
||||
coordDestination.Y = (cursorPosition.Y) - gsl::narrow<short>(count);
|
||||
}
|
||||
|
||||
SMALL_RECT srClip = screenEdges;
|
||||
srClip.Top = cursorPosition.Y;
|
||||
|
||||
LOG_IF_FAILED(ServiceLocator::LocateGlobals().api.ScrollConsoleScreenBufferWImpl(screenInfo,
|
||||
srScroll,
|
||||
coordDestination,
|
||||
srClip,
|
||||
UNICODE_SPACE,
|
||||
screenInfo.GetAttributes().GetLegacyAttributes()));
|
||||
// Here we previously called to ScrollConsoleScreenBufferWImpl to
|
||||
// perform the scrolling operation. However, that function only accepts
|
||||
// a WORD for the fill attributes. That means we'd lose 256/RGB fidelity
|
||||
// for fill attributes. So instead, we'll just call ScrollRegion
|
||||
// ourselves, with the same params that ScrollConsoleScreenBufferWImpl
|
||||
// would have.
|
||||
// See microsoft/terminal#832 for more context.
|
||||
try
|
||||
{
|
||||
LockConsole();
|
||||
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
||||
ScrollRegion(screenInfo,
|
||||
srScroll,
|
||||
srScroll,
|
||||
coordDestination,
|
||||
UNICODE_SPACE,
|
||||
screenInfo.GetAttributes());
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1231,10 +1231,13 @@ void SCREEN_INFORMATION::_InternalSetViewportSize(const COORD* const pcoordSize,
|
||||
|
||||
// See MSFT:19917443
|
||||
// If we're in terminal scrolling mode, and we've changed the height of the
|
||||
// viewport, the new viewport's bottom to the _virtualBottom
|
||||
// viewport, the new viewport's bottom to the _virtualBottom.
|
||||
// GH#1206 - Only do this if the viewport is _growing_ in height. This can
|
||||
// cause unexpected behavior if we try to anchor the _virtualBottom to a
|
||||
// position that will be greater than the height of the buffer.
|
||||
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto newViewport = Viewport::FromInclusive(srNewViewport);
|
||||
if (gci.IsTerminalScrolling() && newViewport.Height() != _viewport.Height())
|
||||
if (gci.IsTerminalScrolling() && newViewport.Height() >= _viewport.Height())
|
||||
{
|
||||
const short newTop = static_cast<short>(std::max(0, _virtualBottom - (newViewport.Height() - 1)));
|
||||
|
||||
|
||||
@@ -161,13 +161,23 @@ class ScreenBufferTests
|
||||
|
||||
TEST_METHOD(DontResetColorsAboveVirtualBottom);
|
||||
|
||||
TEST_METHOD(ScrollOperations);
|
||||
TEST_METHOD(InsertChars);
|
||||
TEST_METHOD(DeleteChars);
|
||||
|
||||
TEST_METHOD(ScrollUpInMargins);
|
||||
TEST_METHOD(ScrollDownInMargins);
|
||||
TEST_METHOD(InsertLinesInMargins);
|
||||
TEST_METHOD(DeleteLinesInMargins);
|
||||
TEST_METHOD(ReverseLineFeedInMargins);
|
||||
|
||||
TEST_METHOD(InsertDeleteLines256Colors);
|
||||
|
||||
TEST_METHOD(SetOriginMode);
|
||||
|
||||
TEST_METHOD(HardResetBuffer);
|
||||
|
||||
TEST_METHOD(RestoreDownAltBufferWithTerminalScrolling);
|
||||
};
|
||||
|
||||
void ScreenBufferTests::SingleAlternateBufferCreationTest()
|
||||
@@ -3051,6 +3061,499 @@ void ScreenBufferTests::DontResetColorsAboveVirtualBottom()
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void _FillLine(COORD position, T fillContent, TextAttribute fillAttr)
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& row = si.GetTextBuffer().GetRowByOffset(position.Y);
|
||||
row.WriteCells({ fillContent, fillAttr }, position.X, false);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void _FillLine(int line, T fillContent, TextAttribute fillAttr)
|
||||
{
|
||||
_FillLine({ 0, gsl::narrow<SHORT>(line) }, fillContent, fillAttr);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void _FillLines(int startLine, int endLine, T fillContent, TextAttribute fillAttr)
|
||||
{
|
||||
for (auto line = startLine; line < endLine; ++line)
|
||||
{
|
||||
_FillLine(line, fillContent, fillAttr);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool _ValidateLineContains(COORD position, T expectedContent, TextAttribute expectedAttr)
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto actual = si.GetCellLineDataAt(position);
|
||||
auto expected = OutputCellIterator{ expectedContent, expectedAttr };
|
||||
while (actual && expected)
|
||||
{
|
||||
if (actual->Chars() != expected->Chars() || actual->TextAttr() != expected->TextAttr())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
++actual;
|
||||
++expected;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
bool _ValidateLineContains(int line, T expectedContent, TextAttribute expectedAttr)
|
||||
{
|
||||
return _ValidateLineContains({ 0, gsl::narrow<SHORT>(line) }, expectedContent, expectedAttr);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
auto _ValidateLinesContain(int startLine, int endLine, T expectedContent, TextAttribute expectedAttr)
|
||||
{
|
||||
for (auto line = startLine; line < endLine; ++line)
|
||||
{
|
||||
if (!_ValidateLineContains(line, expectedContent, expectedAttr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
void ScreenBufferTests::ScrollOperations()
|
||||
{
|
||||
enum ScrollType : int
|
||||
{
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
InsertLine,
|
||||
DeleteLine,
|
||||
ReverseIndex
|
||||
};
|
||||
enum ScrollDirection : int
|
||||
{
|
||||
Up,
|
||||
Down
|
||||
};
|
||||
|
||||
ScrollType scrollType;
|
||||
ScrollDirection scrollDirection;
|
||||
int scrollMagnitude;
|
||||
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:scrollType", L"{0, 1, 2, 3, 4}")
|
||||
TEST_METHOD_PROPERTY(L"Data:scrollMagnitude", L"{1, 2, 5}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollType", (int&)scrollType));
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollMagnitude", scrollMagnitude));
|
||||
|
||||
std::wstringstream escapeSequence;
|
||||
switch (scrollType)
|
||||
{
|
||||
case ScrollUp:
|
||||
Log::Comment(L"Testing scroll up (SU).");
|
||||
escapeSequence << "\x1b[" << scrollMagnitude << "S";
|
||||
scrollDirection = Up;
|
||||
break;
|
||||
case ScrollDown:
|
||||
Log::Comment(L"Testing scroll down (SD).");
|
||||
escapeSequence << "\x1b[" << scrollMagnitude << "T";
|
||||
scrollDirection = Down;
|
||||
break;
|
||||
case InsertLine:
|
||||
Log::Comment(L"Testing insert line (IL).");
|
||||
escapeSequence << "\x1b[" << scrollMagnitude << "L";
|
||||
scrollDirection = Down;
|
||||
break;
|
||||
case DeleteLine:
|
||||
Log::Comment(L"Testing delete line (DL).");
|
||||
escapeSequence << "\x1b[" << scrollMagnitude << "M";
|
||||
scrollDirection = Up;
|
||||
break;
|
||||
case ReverseIndex:
|
||||
Log::Comment(L"Testing reverse index (RI).");
|
||||
for (auto i = 0; i < scrollMagnitude; ++i)
|
||||
{
|
||||
escapeSequence << "\x1bM";
|
||||
}
|
||||
scrollDirection = Down;
|
||||
break;
|
||||
default:
|
||||
VERIFY_FAIL();
|
||||
return;
|
||||
}
|
||||
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
const auto& cursor = si.GetTextBuffer().GetCursor();
|
||||
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
const auto bufferWidth = si.GetBufferSize().Width();
|
||||
const auto bufferHeight = si.GetBufferSize().Height();
|
||||
|
||||
// Move the viewport down a few lines, and only cover part of the buffer width.
|
||||
si.SetViewport(Viewport::FromDimensions({ 5, 10 }, { bufferWidth - 10, 10 }), true);
|
||||
const auto viewportStart = si.GetViewport().Top();
|
||||
const auto viewportEnd = si.GetViewport().BottomExclusive();
|
||||
|
||||
// Fill the entire buffer with Zs. Blue on Green.
|
||||
const auto bufferChar = L'Z';
|
||||
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
|
||||
_FillLines(0, bufferHeight, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport with a range of letters to see if they move. Red on Blue.
|
||||
const auto viewportAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE };
|
||||
auto viewportChar = L'A';
|
||||
auto viewportLine = viewportStart;
|
||||
while (viewportLine < viewportEnd)
|
||||
{
|
||||
_FillLine(viewportLine++, viewportChar++, viewportAttr);
|
||||
}
|
||||
|
||||
// Set the background color so that it will be used to fill the revealed area.
|
||||
si.SetAttributes({ BACKGROUND_RED });
|
||||
|
||||
// Place the cursor in the center.
|
||||
auto cursorPos = COORD{ bufferWidth / 2, (viewportStart + viewportEnd) / 2 };
|
||||
// Unless this is reverse index, which has to be be at the top of the viewport.
|
||||
if (scrollType == ReverseIndex)
|
||||
{
|
||||
cursorPos.Y = viewportStart;
|
||||
}
|
||||
|
||||
Log::Comment(L"Set the cursor position and perform the operation.");
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition(cursorPos, true));
|
||||
stateMachine.ProcessString(escapeSequence.str());
|
||||
|
||||
Log::Comment(L"Verify cursor didn't move.");
|
||||
VERIFY_ARE_EQUAL(cursorPos, cursor.GetPosition());
|
||||
|
||||
Log::Comment(L"Field of Zs outside viewport should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLinesContain(0, viewportStart, bufferChar, bufferAttr));
|
||||
VERIFY_IS_TRUE(_ValidateLinesContain(viewportEnd, bufferHeight, bufferChar, bufferAttr));
|
||||
|
||||
// Depending on the direction of scrolling, lines are either deleted or inserted.
|
||||
const auto deletedLines = scrollDirection == Up ? scrollMagnitude : 0;
|
||||
const auto insertedLines = scrollDirection == Down ? scrollMagnitude : 0;
|
||||
|
||||
// Insert and delete operations only scroll the viewport below the cursor position.
|
||||
const auto scrollStart = (scrollType == InsertLine || scrollType == DeleteLine) ? cursorPos.Y : viewportStart;
|
||||
|
||||
// Reset the viewport character and line number for the verification loop.
|
||||
viewportChar = L'A';
|
||||
viewportLine = viewportStart;
|
||||
|
||||
Log::Comment(L"Lines above the scrolled area should remain unchanged.");
|
||||
while (viewportLine < scrollStart)
|
||||
{
|
||||
VERIFY_IS_TRUE(_ValidateLineContains(viewportLine++, viewportChar++, viewportAttr));
|
||||
}
|
||||
|
||||
Log::Comment(L"Scrolled area should have moved up/down by given magnitude.");
|
||||
viewportChar += gsl::narrow<wchar_t>(deletedLines); // Characters dropped when deleting
|
||||
viewportLine += gsl::narrow<SHORT>(insertedLines); // Lines skipped when inserting
|
||||
while (viewportLine < viewportEnd - deletedLines)
|
||||
{
|
||||
VERIFY_IS_TRUE(_ValidateLineContains(viewportLine++, viewportChar++, viewportAttr));
|
||||
}
|
||||
|
||||
Log::Comment(L"The revealed area should now be blank, with default buffer attributes.");
|
||||
const auto revealedStart = scrollDirection == Up ? viewportEnd - deletedLines : scrollStart;
|
||||
const auto revealedEnd = revealedStart + scrollMagnitude;
|
||||
VERIFY_IS_TRUE(_ValidateLinesContain(revealedStart, revealedEnd, L' ', si.GetAttributes()));
|
||||
}
|
||||
|
||||
void ScreenBufferTests::InsertChars()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
// Set the buffer width to 40, with a centered viewport of 20.
|
||||
const auto bufferWidth = 40;
|
||||
const auto bufferHeight = si.GetBufferSize().Height();
|
||||
const auto viewportStart = 10;
|
||||
const auto viewportEnd = viewportStart + 20;
|
||||
VERIFY_SUCCEEDED(si.ResizeScreenBuffer({ bufferWidth, bufferHeight }, false));
|
||||
si.SetViewport(Viewport::FromExclusive({ viewportStart, 0, viewportEnd, 25 }), true);
|
||||
|
||||
Log::Comment(
|
||||
L"Test 1: Fill the line with Qs. Write some text within the viewport boundaries. "
|
||||
L"Then insert 5 spaces at the cursor. Watch spaces get inserted, text slides right "
|
||||
L"out of the viewport, pushing some of the Qs out of the buffer.");
|
||||
|
||||
const auto insertLine = SHORT{ 10 };
|
||||
auto insertPos = SHORT{ 20 };
|
||||
|
||||
// Place the cursor in the center of the line.
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ insertPos, insertLine }, true));
|
||||
|
||||
// Save the cursor position. It shouldn't move for the rest of the test.
|
||||
const auto& cursor = si.GetTextBuffer().GetCursor();
|
||||
auto expectedCursor = cursor.GetPosition();
|
||||
|
||||
// Fill the entire line with Qs. Blue on Green.
|
||||
const auto bufferChar = L'Q';
|
||||
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
|
||||
_FillLine(insertLine, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport range with text. Red on Blue.
|
||||
const auto textChars = L"ABCDEFGHIJKLMNOPQRST";
|
||||
const auto textAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE };
|
||||
_FillLine({ viewportStart, insertLine }, textChars, textAttr);
|
||||
|
||||
// Set the background color so that it will be used to fill the revealed area.
|
||||
si.SetAttributes({ BACKGROUND_RED });
|
||||
|
||||
// Insert 5 spaces at the cursor position.
|
||||
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
// After: QQQQQQQQQQABCDEFGHIJ KLMNOPQRSTQQQQQ
|
||||
Log::Comment(L"Inserting 5 spaces in the middle of the line.");
|
||||
auto before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
|
||||
stateMachine.ProcessString(L"\x1b[5@");
|
||||
auto after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
|
||||
Log::Comment(before.c_str(), L"Before");
|
||||
Log::Comment(after.c_str(), L" After");
|
||||
|
||||
// Verify cursor didn't move.
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation.");
|
||||
|
||||
// Verify the updated structure of the line.
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ 0, insertLine }, L"QQQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs left of the viewport should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, insertLine }, L"ABCDEFGHIJ", textAttr),
|
||||
L"First half of the alphabet should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ insertPos, insertLine }, L" ", si.GetAttributes()),
|
||||
L"Spaces should be inserted with the current attributes at the cursor position.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ insertPos + 5, insertLine }, L"KLMNOPQRST", textAttr),
|
||||
L"Second half of the alphabet should have moved to the right by the number of spaces inserted.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd + 5, insertLine }, L"QQQQQ", bufferAttr),
|
||||
L"Field of Qs right of the viewport should be moved right, half pushed outside the buffer.");
|
||||
|
||||
Log::Comment(
|
||||
L"Test 2: Inserting at the exact end of the line. Same line structure. "
|
||||
L"Move cursor to right edge of window and insert > 1 space. "
|
||||
L"Only 1 should be inserted, everything else unchanged.");
|
||||
|
||||
// Move cursor to right edge.
|
||||
insertPos = bufferWidth - 1;
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ insertPos, insertLine }, true));
|
||||
expectedCursor = cursor.GetPosition();
|
||||
|
||||
// Fill the entire line with Qs. Blue on Green.
|
||||
_FillLine(insertLine, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport range with text. Red on Blue.
|
||||
_FillLine({ viewportStart, insertLine }, textChars, textAttr);
|
||||
|
||||
// Set the background color so that it will be used to fill the revealed area.
|
||||
si.SetAttributes({ BACKGROUND_RED });
|
||||
|
||||
// Insert 5 spaces at the right edge. Only 1 should be inserted.
|
||||
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
// After: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
|
||||
Log::Comment(L"Inserting 5 spaces at the right edge of the buffer.");
|
||||
before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
|
||||
stateMachine.ProcessString(L"\x1b[5@");
|
||||
after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
|
||||
Log::Comment(before.c_str(), L"Before");
|
||||
Log::Comment(after.c_str(), L" After");
|
||||
|
||||
// Verify cursor didn't move.
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation.");
|
||||
|
||||
// Verify the updated structure of the line.
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ 0, insertLine }, L"QQQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs left of the viewport should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, insertLine }, L"ABCDEFGHIJKLMNOPQRST", textAttr),
|
||||
L"Entire viewport range should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd, insertLine }, L"QQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs right of the viewport should remain unchanged except for the last spot.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ insertPos, insertLine }, L" ", si.GetAttributes()),
|
||||
L"One space should be inserted with the current attributes at the cursor postion.");
|
||||
|
||||
Log::Comment(
|
||||
L"Test 3: Inserting at the exact beginning of the line. Same line structure. "
|
||||
L"Move cursor to left edge of buffer and insert > buffer width of space. "
|
||||
L"The whole row should be replaced with spaces.");
|
||||
|
||||
// Move cursor to left edge.
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, insertLine }, true));
|
||||
expectedCursor = cursor.GetPosition();
|
||||
|
||||
// Fill the entire line with Qs. Blue on Green.
|
||||
_FillLine(insertLine, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport range with text. Red on Blue.
|
||||
_FillLine({ viewportStart, insertLine }, textChars, textAttr);
|
||||
|
||||
// Insert greater than the buffer width at the left edge. The entire line should be erased.
|
||||
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
// After:
|
||||
Log::Comment(L"Inserting 100 spaces at the left edge of the buffer.");
|
||||
before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
|
||||
stateMachine.ProcessString(L"\x1b[100@");
|
||||
after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText();
|
||||
Log::Comment(before.c_str(), L"Before");
|
||||
Log::Comment(after.c_str(), L" After");
|
||||
|
||||
// Verify cursor didn't move.
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation.");
|
||||
|
||||
// Verify the updated structure of the line.
|
||||
VERIFY_IS_TRUE(_ValidateLineContains(insertLine, L' ', si.GetAttributes()),
|
||||
L"A whole line of spaces was inserted at the start, erasing the line.");
|
||||
}
|
||||
|
||||
void ScreenBufferTests::DeleteChars()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
// Set the buffer width to 40, with a centered viewport of 20.
|
||||
const auto bufferWidth = 40;
|
||||
const auto bufferHeight = si.GetBufferSize().Height();
|
||||
const auto viewportStart = 10;
|
||||
const auto viewportEnd = viewportStart + 20;
|
||||
VERIFY_SUCCEEDED(si.ResizeScreenBuffer({ bufferWidth, bufferHeight }, false));
|
||||
si.SetViewport(Viewport::FromExclusive({ viewportStart, 0, viewportEnd, 25 }), true);
|
||||
|
||||
Log::Comment(
|
||||
L"Test 1: Fill the line with Qs. Write some text within the viewport boundaries. "
|
||||
L"Then delete 5 characters at the cursor. Watch the rest of the line slide left, "
|
||||
L"replacing the deleted characters, with spaces inserted at the end of the line.");
|
||||
|
||||
const auto deleteLine = SHORT{ 10 };
|
||||
auto deletePos = SHORT{ 20 };
|
||||
|
||||
// Place the cursor in the center of the line.
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ deletePos, deleteLine }, true));
|
||||
|
||||
// Save the cursor position. It shouldn't move for the rest of the test.
|
||||
const auto& cursor = si.GetTextBuffer().GetCursor();
|
||||
auto expectedCursor = cursor.GetPosition();
|
||||
|
||||
// Fill the entire line with Qs. Blue on Green.
|
||||
const auto bufferChar = L'Q';
|
||||
const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN };
|
||||
_FillLine(deleteLine, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport range with text. Red on Blue.
|
||||
const auto textChars = L"ABCDEFGHIJKLMNOPQRST";
|
||||
const auto textAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE };
|
||||
_FillLine({ viewportStart, deleteLine }, textChars, textAttr);
|
||||
|
||||
// Set the background color so that it will be used to fill the revealed area.
|
||||
si.SetAttributes({ BACKGROUND_RED });
|
||||
|
||||
// Delete 5 characters at the cursor position.
|
||||
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
// After: QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
Log::Comment(L"Deleting 5 characters in the middle of the line.");
|
||||
auto before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
|
||||
stateMachine.ProcessString(L"\x1b[5P");
|
||||
auto after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
|
||||
Log::Comment(before.c_str(), L"Before");
|
||||
Log::Comment(after.c_str(), L" After");
|
||||
|
||||
// Verify cursor didn't move.
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation.");
|
||||
|
||||
// Verify the updated structure of the line.
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ 0, deleteLine }, L"QQQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs left of the viewport should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, deleteLine }, L"ABCDEFGHIJ", textAttr),
|
||||
L"First half of the alphabet should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ deletePos, deleteLine }, L"PQRST", textAttr),
|
||||
L"Only half of the second part of the alphabet remains.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd - 5, deleteLine }, L"QQQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs right of the viewport should be moved left.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ bufferWidth - 5, deleteLine }, L" ", si.GetAttributes()),
|
||||
L"The rest of the line should be replaced with spaces with the current attributes.");
|
||||
|
||||
Log::Comment(
|
||||
L"Test 2: Deleting at the exact end of the line. Same line structure. "
|
||||
L"Move cursor to right edge of window and delete > 1 character. "
|
||||
L"Only 1 should be deleted, everything else unchanged.");
|
||||
|
||||
// Move cursor to right edge.
|
||||
deletePos = bufferWidth - 1;
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ deletePos, deleteLine }, true));
|
||||
expectedCursor = cursor.GetPosition();
|
||||
|
||||
// Fill the entire line with Qs. Blue on Green.
|
||||
_FillLine(deleteLine, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport range with text. Red on Blue.
|
||||
_FillLine({ viewportStart, deleteLine }, textChars, textAttr);
|
||||
|
||||
// Set the background color so that it will be used to fill the revealed area.
|
||||
si.SetAttributes({ BACKGROUND_RED });
|
||||
|
||||
// Delete 5 characters at the right edge. Only 1 should be deleted.
|
||||
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
// After: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ
|
||||
Log::Comment(L"Deleting 5 characters at the right edge of the buffer.");
|
||||
before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
|
||||
stateMachine.ProcessString(L"\x1b[5P");
|
||||
after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
|
||||
Log::Comment(before.c_str(), L"Before");
|
||||
Log::Comment(after.c_str(), L" After");
|
||||
|
||||
// Verify cursor didn't move.
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation.");
|
||||
|
||||
// Verify the updated structure of the line.
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ 0, deleteLine }, L"QQQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs left of the viewport should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, deleteLine }, L"ABCDEFGHIJKLMNOPQRST", textAttr),
|
||||
L"Entire viewport range should remain unchanged.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd, deleteLine }, L"QQQQQQQQQ", bufferAttr),
|
||||
L"Field of Qs right of the viewport should remain unchanged except for the last spot.");
|
||||
VERIFY_IS_TRUE(_ValidateLineContains({ deletePos, deleteLine }, L" ", si.GetAttributes()),
|
||||
L"One character should be erased with the current attributes at the cursor postion.");
|
||||
|
||||
Log::Comment(
|
||||
L"Test 3: Deleting at the exact beginning of the line. Same line structure. "
|
||||
L"Move cursor to left edge of buffer and delete > buffer width of characters. "
|
||||
L"The whole row should be replaced with spaces.");
|
||||
|
||||
// Move cursor to left edge.
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, deleteLine }, true));
|
||||
expectedCursor = cursor.GetPosition();
|
||||
|
||||
// Fill the entire line with Qs. Blue on Green.
|
||||
_FillLine(deleteLine, bufferChar, bufferAttr);
|
||||
|
||||
// Fill the viewport range with text. Red on Blue.
|
||||
_FillLine({ viewportStart, deleteLine }, textChars, textAttr);
|
||||
|
||||
// Delete greater than the buffer width at the left edge. The entire line should be erased.
|
||||
// Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
// After:
|
||||
Log::Comment(L"Deleting 100 characters at the left edge of the buffer.");
|
||||
before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
|
||||
stateMachine.ProcessString(L"\x1b[100P");
|
||||
after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText();
|
||||
Log::Comment(before.c_str(), L"Before");
|
||||
Log::Comment(after.c_str(), L" After");
|
||||
|
||||
// Verify cursor didn't move.
|
||||
VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation.");
|
||||
|
||||
// Verify the updated structure of the line.
|
||||
VERIFY_IS_TRUE(_ValidateLineContains(deleteLine, L' ', si.GetAttributes()),
|
||||
L"A whole line of spaces was inserted from the right, erasing the line.");
|
||||
}
|
||||
|
||||
void _CommonScrollingSetup()
|
||||
{
|
||||
// Used for testing MSFT:20204600
|
||||
@@ -3449,6 +3952,103 @@ void ScreenBufferTests::ReverseLineFeedInMargins()
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenBufferTests::InsertDeleteLines256Colors()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:insert", L"{false, true}")
|
||||
TEST_METHOD_PROPERTY(L"Data:colorStyle", L"{0, 1, 2}")
|
||||
END_TEST_METHOD_PROPERTIES();
|
||||
|
||||
// colorStyle will be used to control whether we use a color from the 16
|
||||
// color table, a color from the 256 color table, or a pure RGB color.
|
||||
const int Use16Color = 0;
|
||||
const int Use256Color = 1;
|
||||
const int UseRGBColor = 2;
|
||||
|
||||
bool insert;
|
||||
int colorStyle;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"insert", insert), L"whether to insert(true) or delete(false) lines");
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"colorStyle", colorStyle), L"controls whether to use the 16 color table, 256 table, or RGB colors");
|
||||
|
||||
// This test is largely taken from repro code from
|
||||
// https://github.com/microsoft/terminal/issues/832#issuecomment-507447272
|
||||
Log::Comment(
|
||||
L"Sets the attributes to a 256/RGB color, then scrolls some lines with"
|
||||
L" DL. Verifies the rows are cleared with the attributes we'd expect.");
|
||||
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& tbi = si.GetTextBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
auto& cursor = si.GetTextBuffer().GetCursor();
|
||||
|
||||
TextAttribute expectedAttr{ si.GetAttributes() };
|
||||
std::wstring sgrSeq = L"\x1b[48;5;2m";
|
||||
if (colorStyle == Use16Color)
|
||||
{
|
||||
expectedAttr.SetBackground(gci.GetColorTableEntry(2));
|
||||
}
|
||||
else if (colorStyle == Use256Color)
|
||||
{
|
||||
expectedAttr.SetBackground(gci.GetColorTableEntry(20));
|
||||
sgrSeq = L"\x1b[48;5;20m";
|
||||
}
|
||||
else if (colorStyle == UseRGBColor)
|
||||
{
|
||||
expectedAttr.SetBackground(RGB(1, 2, 3));
|
||||
sgrSeq = L"\x1b[48;2;1;2;3m";
|
||||
}
|
||||
|
||||
// Set some scrolling margins
|
||||
stateMachine.ProcessString(L"\x1b[1;3r");
|
||||
|
||||
// Set the BG color to the table index 2, as a 256-color sequence
|
||||
stateMachine.ProcessString(sgrSeq);
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedAttr, si.GetAttributes());
|
||||
|
||||
// Move to home
|
||||
stateMachine.ProcessString(L"\x1b[H");
|
||||
|
||||
// Insert/Delete 10 lines
|
||||
stateMachine.ProcessString(insert ? L"\x1b[10L" : L"\x1b[10M");
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cursor=%s", VerifyOutputTraits<COORD>::ToString(cursor.GetPosition()).GetBuffer()));
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"viewport=%s", VerifyOutputTraits<SMALL_RECT>::ToString(si.GetViewport().ToInclusive()).GetBuffer()));
|
||||
|
||||
VERIFY_ARE_EQUAL(0, cursor.GetPosition().X);
|
||||
VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y);
|
||||
|
||||
stateMachine.ProcessString(L"foo");
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cursor=%s", VerifyOutputTraits<COORD>::ToString(cursor.GetPosition()).GetBuffer()));
|
||||
VERIFY_ARE_EQUAL(3, cursor.GetPosition().X);
|
||||
VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y);
|
||||
{
|
||||
auto iter00 = tbi.GetCellDataAt({ 0, 0 });
|
||||
auto iter10 = tbi.GetCellDataAt({ 1, 0 });
|
||||
auto iter20 = tbi.GetCellDataAt({ 2, 0 });
|
||||
auto iter30 = tbi.GetCellDataAt({ 3, 0 });
|
||||
auto iter01 = tbi.GetCellDataAt({ 0, 1 });
|
||||
auto iter02 = tbi.GetCellDataAt({ 0, 2 });
|
||||
VERIFY_ARE_EQUAL(L"f", iter00->Chars());
|
||||
VERIFY_ARE_EQUAL(L"o", iter10->Chars());
|
||||
VERIFY_ARE_EQUAL(L"o", iter20->Chars());
|
||||
VERIFY_ARE_EQUAL(L"\x20", iter30->Chars());
|
||||
VERIFY_ARE_EQUAL(L"\x20", iter01->Chars());
|
||||
VERIFY_ARE_EQUAL(L"\x20", iter02->Chars());
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedAttr, iter00->TextAttr());
|
||||
VERIFY_ARE_EQUAL(expectedAttr, iter10->TextAttr());
|
||||
VERIFY_ARE_EQUAL(expectedAttr, iter20->TextAttr());
|
||||
VERIFY_ARE_EQUAL(expectedAttr, iter30->TextAttr());
|
||||
VERIFY_ARE_EQUAL(expectedAttr, iter01->TextAttr());
|
||||
VERIFY_ARE_EQUAL(expectedAttr, iter02->TextAttr());
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenBufferTests::SetOriginMode()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
@@ -3516,3 +4116,130 @@ void ScreenBufferTests::SetOriginMode()
|
||||
// Reset DECOM so we don't affect future tests
|
||||
stateMachine.ProcessString(L"\x1B[?6l");
|
||||
}
|
||||
|
||||
void ScreenBufferTests::HardResetBuffer()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
const auto& viewport = si.GetViewport();
|
||||
const auto& cursor = si.GetTextBuffer().GetCursor();
|
||||
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
|
||||
auto isBufferClear = [&]() {
|
||||
auto offset = 0;
|
||||
auto width = si.GetBufferSize().Width();
|
||||
for (auto iter = si.GetCellDataAt({}); iter; ++iter, ++offset)
|
||||
{
|
||||
if (iter->Chars() != L" " || iter->TextAttr() != TextAttribute{})
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Buffer not clear at (X:%d, Y:%d)",
|
||||
offset % width,
|
||||
offset / width));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto resetToInitialState = L"\033c";
|
||||
|
||||
Log::Comment(L"Start with a clear buffer, viewport and cursor at 0,0");
|
||||
si.SetAttributes(TextAttribute());
|
||||
si.ClearTextData();
|
||||
VERIFY_SUCCEEDED(si.SetViewportOrigin(true, { 0, 0 }, true));
|
||||
VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, 0 }, true));
|
||||
VERIFY_IS_TRUE(isBufferClear());
|
||||
|
||||
Log::Comment(L"Write a single line of text to the buffer");
|
||||
stateMachine.ProcessString(L"Hello World!\n");
|
||||
VERIFY_IS_FALSE(isBufferClear());
|
||||
VERIFY_ARE_EQUAL(COORD({ 0, 1 }), cursor.GetPosition());
|
||||
|
||||
Log::Comment(L"After a reset, buffer should be clear, with cursor at 0,0");
|
||||
stateMachine.ProcessString(resetToInitialState);
|
||||
VERIFY_IS_TRUE(isBufferClear());
|
||||
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition());
|
||||
|
||||
Log::Comment(L"Set the background color to red");
|
||||
stateMachine.ProcessString(L"\x1b[41m");
|
||||
Log::Comment(L"Write multiple pages of text to the buffer");
|
||||
for (auto i = 0; i < viewport.Height() * 2; i++)
|
||||
{
|
||||
stateMachine.ProcessString(L"Hello World!\n");
|
||||
}
|
||||
VERIFY_IS_FALSE(isBufferClear());
|
||||
VERIFY_IS_GREATER_THAN(viewport.Top(), viewport.Height());
|
||||
VERIFY_IS_GREATER_THAN(cursor.GetPosition().Y, viewport.Height());
|
||||
|
||||
Log::Comment(L"After a reset, buffer should be clear, with viewport and cursor at 0,0");
|
||||
stateMachine.ProcessString(resetToInitialState);
|
||||
VERIFY_IS_TRUE(isBufferClear());
|
||||
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), viewport.Origin());
|
||||
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition());
|
||||
}
|
||||
|
||||
void ScreenBufferTests::RestoreDownAltBufferWithTerminalScrolling()
|
||||
{
|
||||
// This is a test for microsoft/terminal#1206. Refer to that issue for more
|
||||
// context
|
||||
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
gci.SetTerminalScrolling(true);
|
||||
gci.LockConsole(); // Lock must be taken to manipulate buffer.
|
||||
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
|
||||
|
||||
auto& siMain = gci.GetActiveOutputBuffer();
|
||||
COORD const coordFontSize = siMain.GetScreenFontSize();
|
||||
siMain._virtualBottom = siMain._viewport.BottomInclusive();
|
||||
|
||||
auto originalView = siMain._viewport;
|
||||
|
||||
VERIFY_IS_NULL(siMain._psiMainBuffer);
|
||||
VERIFY_IS_NULL(siMain._psiAlternateBuffer);
|
||||
|
||||
Log::Comment(L"Create an alternate buffer");
|
||||
if (VERIFY_IS_TRUE(NT_SUCCESS(siMain.UseAlternateScreenBuffer())))
|
||||
{
|
||||
VERIFY_IS_NOT_NULL(siMain._psiAlternateBuffer);
|
||||
auto& altBuffer = *siMain._psiAlternateBuffer;
|
||||
VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top());
|
||||
VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom);
|
||||
|
||||
const COORD originalSize = originalView.Dimensions();
|
||||
const COORD doubledSize = { originalSize.X * 2, originalSize.Y * 2 };
|
||||
|
||||
// Create some RECTs, which are dimensions in pixels, because
|
||||
// ProcessResizeWindow needs to work on rects in screen _pixel_
|
||||
// dimensions, not character sizes.
|
||||
RECT originalClientRect{ 0 }, maximizedClientRect{ 0 };
|
||||
|
||||
originalClientRect.right = originalSize.X * coordFontSize.X;
|
||||
originalClientRect.bottom = originalSize.Y * coordFontSize.Y;
|
||||
|
||||
maximizedClientRect.right = doubledSize.X * coordFontSize.X;
|
||||
maximizedClientRect.bottom = doubledSize.Y * coordFontSize.Y;
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Emulate a maximize"));
|
||||
// Note that just calling _InternalSetViewportSize does not hit the
|
||||
// exceptional case here. There's other logic farther down the stack
|
||||
// that triggers it.
|
||||
altBuffer.ProcessResizeWindow(&maximizedClientRect, &originalClientRect);
|
||||
|
||||
VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top());
|
||||
VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom);
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Emulate a restore down"));
|
||||
|
||||
altBuffer.ProcessResizeWindow(&originalClientRect, &maximizedClientRect);
|
||||
|
||||
// Before the bugfix, this would fail, with the top being roughly 80,
|
||||
// halfway into the buffer, with the bottom being anchored to the old
|
||||
// size.
|
||||
VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top());
|
||||
VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,18 +1044,8 @@ class ViewportTests
|
||||
const auto original = Viewport::FromInclusive(srOriginal);
|
||||
const auto remove = original;
|
||||
|
||||
std::vector<Viewport> expected;
|
||||
expected.emplace_back(Viewport::FromDimensions(original.Origin(), { 0, 0 }));
|
||||
|
||||
const auto actual = Viewport::Subtract(original, remove);
|
||||
|
||||
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
|
||||
Log::Comment(L"Now validate that each viewport has the expected area.");
|
||||
for (size_t i = 0; i < expected.size(); i++)
|
||||
{
|
||||
const auto& exp = expected.at(i);
|
||||
const auto& act = actual.at(i);
|
||||
VERIFY_ARE_EQUAL(exp, act);
|
||||
}
|
||||
VERIFY_ARE_EQUAL(0u, actual.size(), L"There should be no viewports returned");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,16 +26,21 @@ constexpr COLORREF DEFAULT_BACKGROUND_WITH_ALPHA = OPACITY_OPAQUE | DEFAULT_BACK
|
||||
constexpr COLORREF POWERSHELL_BLUE = RGB(1, 36, 86);
|
||||
|
||||
constexpr short DEFAULT_HISTORY_SIZE = 9001;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26426)
|
||||
// TODO GH 2674, don't disable this warning, move to std::wstring_view or something like that.
|
||||
const std::wstring DEFAULT_FONT_FACE{ L"Consolas" };
|
||||
constexpr int DEFAULT_FONT_SIZE = 10;
|
||||
constexpr int DEFAULT_FONT_SIZE = 12;
|
||||
|
||||
constexpr int DEFAULT_ROWS = 30;
|
||||
constexpr int DEFAULT_COLS = 120;
|
||||
|
||||
const std::wstring DEFAULT_PADDING{ L"0, 0, 0, 0" };
|
||||
const std::wstring DEFAULT_PADDING{ L"8, 8, 8, 8" };
|
||||
const std::wstring DEFAULT_STARTING_DIRECTORY{ L"%USERPROFILE%" };
|
||||
|
||||
constexpr COLORREF DEFAULT_CURSOR_COLOR = COLOR_WHITE;
|
||||
constexpr COLORREF DEFAULT_CURSOR_HEIGHT = 25;
|
||||
|
||||
const std::wstring DEFAULT_WORD_DELIMITERS{ L" ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502" };
|
||||
#pragma warning(pop)
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
void InitializeCursorSize(const HWND hOptionsDlg)
|
||||
{
|
||||
unsigned int newRadioValue = IDD_CURSOR_ADVANCED;
|
||||
if (gpStateInfo->CursorType != 0)
|
||||
{
|
||||
// IDD_CURSOR_ADVANCED is used as a placeholder for when a
|
||||
// non-legacy shape is selected.
|
||||
newRadioValue = IDD_CURSOR_ADVANCED;
|
||||
}
|
||||
else if (gpStateInfo->CursorSize <= 25)
|
||||
{
|
||||
newRadioValue = IDD_CURSOR_SMALL;
|
||||
}
|
||||
else if (gpStateInfo->CursorSize <= 50)
|
||||
{
|
||||
newRadioValue = IDD_CURSOR_MEDIUM;
|
||||
}
|
||||
else
|
||||
{
|
||||
newRadioValue = IDD_CURSOR_LARGE;
|
||||
}
|
||||
CheckRadioButton(hOptionsDlg, IDD_CURSOR_SMALL, IDD_CURSOR_ADVANCED, newRadioValue);
|
||||
}
|
||||
|
||||
bool OptionsCommandCallback(HWND hDlg, const unsigned int Item, const unsigned int Notification, HWND hControlWindow)
|
||||
{
|
||||
UINT Value;
|
||||
@@ -147,6 +171,9 @@ INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lPara
|
||||
switch (wMsg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
// Initialize the global handle to this dialog
|
||||
g_hOptionsDlg = hDlg;
|
||||
|
||||
CheckDlgButton(hDlg, IDD_HISTORY_NODUP, gpStateInfo->HistoryNoDup);
|
||||
CheckDlgButton(hDlg, IDD_QUICKEDIT, gpStateInfo->QuickEdit);
|
||||
CheckDlgButton(hDlg, IDD_INSERT, gpStateInfo->InsertMode);
|
||||
@@ -167,19 +194,7 @@ INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lPara
|
||||
CreateAndAssociateToolTipToControl(IDD_INTERCEPT_COPY_PASTE, hDlg, IDS_TOOLTIP_INTERCEPT_COPY_PASTE);
|
||||
|
||||
// initialize cursor radio buttons
|
||||
if (gpStateInfo->CursorSize <= 25)
|
||||
{
|
||||
Item = IDD_CURSOR_SMALL;
|
||||
}
|
||||
else if (gpStateInfo->CursorSize <= 50)
|
||||
{
|
||||
Item = IDD_CURSOR_MEDIUM;
|
||||
}
|
||||
else
|
||||
{
|
||||
Item = IDD_CURSOR_LARGE;
|
||||
}
|
||||
CheckRadioButton(hDlg, IDD_CURSOR_SMALL, IDD_CURSOR_LARGE, Item);
|
||||
InitializeCursorSize(hDlg);
|
||||
|
||||
SetDlgItemInt(hDlg, IDD_HISTORY_SIZE, gpStateInfo->HistoryBufferSize, FALSE);
|
||||
SendDlgItemMessage(hDlg, IDD_HISTORY_SIZE, EM_LIMITTEXT, 3, 0);
|
||||
|
||||
@@ -6,7 +6,7 @@ Module Name:
|
||||
- OptionsPage.h
|
||||
|
||||
Abstract:
|
||||
- This module contains the definitions for console options dialog.
|
||||
- This module contains the definitions for console options dialog.
|
||||
|
||||
Author(s):
|
||||
Mike Griese (migrie) Oct-2016
|
||||
@@ -16,3 +16,4 @@ Author(s):
|
||||
|
||||
void ToggleV2OptionsControls(__in const HWND hDlg);
|
||||
INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
|
||||
void InitializeCursorSize(const HWND hOptionsDlg);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "precomp.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "OptionsPage.h" // For InitializeCursorSize
|
||||
#include "ColorControl.h"
|
||||
#include <functional>
|
||||
|
||||
@@ -323,10 +324,22 @@ bool TerminalDlgCommand(const HWND hDlg, const WORD item, const WORD command) no
|
||||
case IDD_TERMINAL_UNDERSCORE:
|
||||
case IDD_TERMINAL_EMPTYBOX:
|
||||
case IDD_TERMINAL_SOLIDBOX:
|
||||
{
|
||||
gpStateInfo->CursorType = item - IDD_TERMINAL_LEGACY_CURSOR;
|
||||
UpdateApplyButton(hDlg);
|
||||
|
||||
// See GH#1219 - When the cursor state is something other than legacy,
|
||||
// we need to manually check the "IDD_CURSOR_ADVANCED" radio button on
|
||||
// the Options page. This will prevent the Options page from manually
|
||||
// resetting the cursor to legacy.
|
||||
if (g_hOptionsDlg != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
InitializeCursorSize(g_hOptionsDlg);
|
||||
}
|
||||
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case IDD_DISABLE_SCROLLFORWARD:
|
||||
gpStateInfo->TerminalScrolling = IsDlgButtonChecked(hDlg, IDD_DISABLE_SCROLLFORWARD);
|
||||
UpdateApplyButton(hDlg);
|
||||
|
||||
@@ -42,6 +42,8 @@ BEGIN
|
||||
WS_TABSTOP | WS_GROUP
|
||||
AUTORADIOBUTTON "&Medium", IDD_CURSOR_MEDIUM, 14, 33, 84, 10,
|
||||
AUTORADIOBUTTON "&Large", IDD_CURSOR_LARGE, 14, 43, 84, 10,
|
||||
// IDD_CURSOR_ADVANCED is a hidden control, see GH#1219
|
||||
AUTORADIOBUTTON "", IDD_CURSOR_ADVANCED, 14, 53, 0, 0,
|
||||
|
||||
GROUPBOX "Command History", -1, 115, 11, 120, 56, WS_GROUP
|
||||
LTEXT "&Buffer Size:", -1, 119, 25, 78, 9
|
||||
@@ -106,6 +108,8 @@ BEGIN
|
||||
WS_TABSTOP | WS_GROUP
|
||||
AUTORADIOBUTTON "&Medium", IDD_CURSOR_MEDIUM, 14, 33, 84, 10,
|
||||
AUTORADIOBUTTON "&Large", IDD_CURSOR_LARGE, 14, 43, 84, 10,
|
||||
// IDD_CURSOR_ADVANCED is a hidden control, see GH#1219
|
||||
AUTORADIOBUTTON "", IDD_CURSOR_ADVANCED, 14, 53, 0, 0,
|
||||
|
||||
GROUPBOX "Command History", -1, 115, 11, 120, 56, WS_GROUP
|
||||
LTEXT "&Buffer Size:", -1, 119, 25, 78, 9
|
||||
|
||||
@@ -43,6 +43,7 @@ Revision History:
|
||||
#define IDD_LANGUAGE_GROUPBOX 116
|
||||
#define DID_SETTINGS_COMCTL5 117
|
||||
#define DID_SETTINGS2_COMCTL5 118
|
||||
#define IDD_CURSOR_ADVANCED 119
|
||||
|
||||
#define DID_FONTDLG 200
|
||||
#define IDD_STATIC 201
|
||||
|
||||
@@ -55,3 +55,4 @@ COLORREF g_fakeBackgroundColor = RGB(12, 12, 12); // Default black
|
||||
COLORREF g_fakeCursorColor = RGB(242, 242, 242); // Default bright white
|
||||
|
||||
HWND g_hTerminalDlg = static_cast<HWND>(INVALID_HANDLE_VALUE);
|
||||
HWND g_hOptionsDlg = static_cast<HWND>(INVALID_HANDLE_VALUE);
|
||||
|
||||
@@ -53,3 +53,4 @@ extern COLORREF g_fakeBackgroundColor;
|
||||
extern COLORREF g_fakeCursorColor;
|
||||
|
||||
extern HWND g_hTerminalDlg;
|
||||
extern HWND g_hOptionsDlg;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<ClInclude Include="..\..\inc\IRenderData.hpp" />
|
||||
<ClInclude Include="..\..\inc\IRenderEngine.hpp" />
|
||||
<ClInclude Include="..\..\inc\IRenderer.hpp" />
|
||||
<ClInclude Include="..\..\inc\IRenderTarget.hpp" />
|
||||
<ClInclude Include="..\..\inc\RenderEngineBase.hpp" />
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
<ClInclude Include="..\renderer.hpp" />
|
||||
|
||||
@@ -80,6 +80,9 @@
|
||||
<ClInclude Include="..\..\inc\Cluster.hpp">
|
||||
<Filter>Header Files\inc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\inc\IRenderTarget.hpp">
|
||||
<Filter>Header Files\inc</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
|
||||
@@ -20,16 +20,16 @@ using namespace Microsoft::Console::Render;
|
||||
// - font - The DirectWrite font face to use while calculating layout (by default, will fallback if necessary)
|
||||
// - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume.
|
||||
// - width - The count of pixels available per column (the expected pixel width of every column)
|
||||
CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
IDWriteTextAnalyzer1* const analyzer,
|
||||
IDWriteTextFormat* const format,
|
||||
IDWriteFontFace1* const font,
|
||||
CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
|
||||
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
|
||||
gsl::not_null<IDWriteTextFormat*> const format,
|
||||
gsl::not_null<IDWriteFontFace1*> const font,
|
||||
std::basic_string_view<Cluster> const clusters,
|
||||
size_t const width) :
|
||||
_factory{ factory },
|
||||
_analyzer{ analyzer },
|
||||
_format{ format },
|
||||
_font{ font },
|
||||
_factory{ factory.get() },
|
||||
_analyzer{ analyzer.get() },
|
||||
_format{ format.get() },
|
||||
_font{ font.get() },
|
||||
_localeName{},
|
||||
_numberSubstitution{},
|
||||
_readingDirection{ DWRITE_READING_DIRECTION_LEFT_TO_RIGHT },
|
||||
@@ -39,7 +39,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
_width{ width }
|
||||
{
|
||||
// Fetch the locale name out once now from the format
|
||||
_localeName.resize(format->GetLocaleNameLength() + 1); // +1 for null
|
||||
_localeName.resize(gsl::narrow_cast<size_t>(format->GetLocaleNameLength()) + 1); // +1 for null
|
||||
THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow<UINT32>(_localeName.size())));
|
||||
|
||||
for (const auto& cluster : clusters)
|
||||
@@ -58,6 +58,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// - S_OK or suitable DirectX/DirectWrite/Direct2D result code.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetColumns(_Out_ UINT32* columns)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, columns);
|
||||
*columns = 0;
|
||||
|
||||
RETURN_IF_FAILED(_AnalyzeRuns());
|
||||
@@ -88,7 +89,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::Draw(_In_opt_ void* clientDrawingContext,
|
||||
_In_ IDWriteTextRenderer* renderer,
|
||||
FLOAT originX,
|
||||
FLOAT originY)
|
||||
FLOAT originY) noexcept
|
||||
{
|
||||
RETURN_IF_FAILED(_AnalyzeRuns());
|
||||
RETURN_IF_FAILED(_ShapeGlyphRuns());
|
||||
@@ -146,7 +147,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
}
|
||||
|
||||
// Resequence the resulting runs in order before returning to caller.
|
||||
size_t totalRuns = _runs.size();
|
||||
const size_t totalRuns = _runs.size();
|
||||
std::vector<LinkedRun> runs;
|
||||
runs.resize(totalRuns);
|
||||
|
||||
@@ -178,7 +179,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
const auto textLength = gsl::narrow<UINT32>(_text.size());
|
||||
|
||||
// Estimate the maximum number of glyph indices needed to hold a string.
|
||||
UINT32 estimatedGlyphCount = _EstimateGlyphCount(textLength);
|
||||
const UINT32 estimatedGlyphCount = _EstimateGlyphCount(textLength);
|
||||
|
||||
_glyphIndices.resize(estimatedGlyphCount);
|
||||
_glyphOffsets.resize(estimatedGlyphCount);
|
||||
@@ -230,9 +231,9 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// will shape as if the line is not broken.
|
||||
|
||||
Run& run = _runs.at(runIndex);
|
||||
UINT32 textStart = run.textStart;
|
||||
UINT32 textLength = run.textLength;
|
||||
UINT32 maxGlyphCount = static_cast<UINT32>(_glyphIndices.size() - glyphStart);
|
||||
const UINT32 textStart = run.textStart;
|
||||
const UINT32 textLength = run.textLength;
|
||||
UINT32 maxGlyphCount = gsl::narrow<UINT32>(_glyphIndices.size() - glyphStart);
|
||||
UINT32 actualGlyphCount = 0;
|
||||
|
||||
run.glyphStart = glyphStart;
|
||||
@@ -268,7 +269,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
do
|
||||
{
|
||||
hr = _analyzer->GetGlyphs(
|
||||
&_text[textStart],
|
||||
&_text.at(textStart),
|
||||
textLength,
|
||||
run.fontFace.Get(),
|
||||
run.isSideways, // isSideways,
|
||||
@@ -280,10 +281,10 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
nullptr, // featureLengths
|
||||
0, // featureCount
|
||||
maxGlyphCount, // maxGlyphCount
|
||||
&_glyphClusters[textStart],
|
||||
&textProps[0],
|
||||
&_glyphIndices[glyphStart],
|
||||
&glyphProps[0],
|
||||
&_glyphClusters.at(textStart),
|
||||
&textProps.at(0),
|
||||
&_glyphIndices.at(glyphStart),
|
||||
&glyphProps.at(0),
|
||||
&actualGlyphCount);
|
||||
tries++;
|
||||
|
||||
@@ -291,7 +292,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
{
|
||||
// Try again using a larger buffer.
|
||||
maxGlyphCount = _EstimateGlyphCount(maxGlyphCount);
|
||||
UINT32 totalGlyphsArrayCount = glyphStart + maxGlyphCount;
|
||||
const UINT32 totalGlyphsArrayCount = glyphStart + maxGlyphCount;
|
||||
|
||||
glyphProps.resize(maxGlyphCount);
|
||||
_glyphIndices.resize(totalGlyphsArrayCount);
|
||||
@@ -306,19 +307,19 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
|
||||
// Get the placement of the all the glyphs.
|
||||
|
||||
_glyphAdvances.resize(std::max(static_cast<size_t>(glyphStart + actualGlyphCount), _glyphAdvances.size()));
|
||||
_glyphOffsets.resize(std::max(static_cast<size_t>(glyphStart + actualGlyphCount), _glyphOffsets.size()));
|
||||
_glyphAdvances.resize(std::max(gsl::narrow_cast<size_t>(glyphStart) + gsl::narrow_cast<size_t>(actualGlyphCount), _glyphAdvances.size()));
|
||||
_glyphOffsets.resize(std::max(gsl::narrow_cast<size_t>(glyphStart) + gsl::narrow_cast<size_t>(actualGlyphCount), _glyphOffsets.size()));
|
||||
|
||||
const auto fontSizeFormat = _format->GetFontSize();
|
||||
const auto fontSize = fontSizeFormat * run.fontScale;
|
||||
|
||||
hr = _analyzer->GetGlyphPlacements(
|
||||
&_text[textStart],
|
||||
&_glyphClusters[textStart],
|
||||
&textProps[0],
|
||||
&_text.at(textStart),
|
||||
&_glyphClusters.at(textStart),
|
||||
&textProps.at(0),
|
||||
textLength,
|
||||
&_glyphIndices[glyphStart],
|
||||
&glyphProps[0],
|
||||
&_glyphIndices.at(glyphStart),
|
||||
&glyphProps.at(0),
|
||||
actualGlyphCount,
|
||||
run.fontFace.Get(),
|
||||
fontSize,
|
||||
@@ -329,8 +330,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
NULL, // features
|
||||
NULL, // featureRangeLengths
|
||||
0, // featureRanges
|
||||
&_glyphAdvances[glyphStart],
|
||||
&_glyphOffsets[glyphStart]);
|
||||
&_glyphAdvances.at(glyphStart),
|
||||
&_glyphOffsets.at(glyphStart));
|
||||
|
||||
RETURN_IF_FAILED(hr);
|
||||
|
||||
@@ -391,13 +392,13 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++)
|
||||
{
|
||||
// Advance is how wide in pixels the glyph is
|
||||
auto& advance = _glyphAdvances[i];
|
||||
auto& advance = _glyphAdvances.at(i);
|
||||
|
||||
// Offsets is how far to move the origin (in pixels) from where it is
|
||||
auto& offset = _glyphOffsets[i];
|
||||
auto& offset = _glyphOffsets.at(i);
|
||||
|
||||
// Get how many columns we expected the glyph to have and mutiply into pixels.
|
||||
const auto columns = _textClusterColumns[i];
|
||||
const auto columns = _textClusterColumns.at(i);
|
||||
const auto advanceExpected = static_cast<float>(columns * _width);
|
||||
|
||||
// If what we expect is bigger than what we have... pad it out.
|
||||
@@ -419,7 +420,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// We need to retrieve the design information for this specific glyph so we can figure out the appropriate
|
||||
// height proportional to the width that we desire.
|
||||
INT32 advanceInDesignUnits;
|
||||
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices[i], &advanceInDesignUnits));
|
||||
RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices.at(i), &advanceInDesignUnits));
|
||||
|
||||
// When things are drawn, we want the font size (as specified in the base font in the original format)
|
||||
// to be scaled by some factor.
|
||||
@@ -467,6 +468,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
IDWriteTextRenderer* renderer,
|
||||
const D2D_POINT_2F origin) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, renderer);
|
||||
|
||||
try
|
||||
{
|
||||
// We're going to start from the origin given and walk to the right for each
|
||||
@@ -477,21 +480,21 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
|
||||
{
|
||||
// Get the run
|
||||
Run& run = _runs.at(runIndex);
|
||||
const Run& run = _runs.at(runIndex);
|
||||
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun = { 0 };
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = _glyphAdvances.data() + run.glyphStart;
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = _glyphIndices.data() + run.glyphStart;
|
||||
glyphRun.glyphOffsets = _glyphOffsets.data() + run.glyphStart;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription = { 0 };
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
@@ -539,7 +542,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// Return Value:
|
||||
// - An estimate of how many glyph spaces may be required in the shaping arrays
|
||||
// to hold the data from a string of the given length.
|
||||
[[nodiscard]] UINT32 CustomTextLayout::_EstimateGlyphCount(const UINT32 textLength) noexcept
|
||||
[[nodiscard]] constexpr UINT32 CustomTextLayout::_EstimateGlyphCount(const UINT32 textLength) noexcept
|
||||
{
|
||||
// This formula is from https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nf-dwrite-idwritetextanalyzer-getglyphs
|
||||
// and is the recommended formula for estimating buffer size for glyph count.
|
||||
@@ -561,12 +564,15 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, textString);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
|
||||
|
||||
*textString = nullptr;
|
||||
*textLength = 0;
|
||||
|
||||
if (textPosition < _text.size())
|
||||
{
|
||||
*textString = _text.data() + textPosition;
|
||||
*textString = &_text.at(textPosition);
|
||||
*textLength = gsl::narrow<UINT32>(_text.size()) - textPosition;
|
||||
}
|
||||
|
||||
@@ -585,8 +591,11 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// - S_OK or appropriate STL/GSL failure code.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetTextBeforePosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength)
|
||||
_Out_ UINT32* textLength) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, textString);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
|
||||
|
||||
*textString = nullptr;
|
||||
*textLength = 0;
|
||||
|
||||
@@ -606,7 +615,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The reading direction held for this layout from construction
|
||||
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE CustomTextLayout::GetParagraphReadingDirection()
|
||||
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE CustomTextLayout::GetParagraphReadingDirection() noexcept
|
||||
{
|
||||
return _readingDirection;
|
||||
}
|
||||
@@ -622,8 +631,11 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// - S_OK or appropriate STL/GSL failure code.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetLocaleName(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_Outptr_result_z_ WCHAR const** localeName)
|
||||
_Outptr_result_z_ WCHAR const** localeName) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, localeName);
|
||||
|
||||
*localeName = _localeName.data();
|
||||
*textLength = gsl::narrow<UINT32>(_text.size()) - textPosition;
|
||||
|
||||
@@ -641,8 +653,11 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// - S_OK or appropriate STL/GSL failure code.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetNumberSubstitution(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution)
|
||||
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, textLength);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, numberSubstitution);
|
||||
|
||||
*numberSubstitution = nullptr;
|
||||
*textLength = gsl::narrow<UINT32>(_text.size()) - textPosition;
|
||||
|
||||
@@ -799,7 +814,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
RETURN_IF_FAILED(format1->GetFontCollection(&collection));
|
||||
|
||||
std::wstring familyName;
|
||||
familyName.resize(format1->GetFontFamilyNameLength() + 1);
|
||||
familyName.resize(gsl::narrow_cast<size_t>(format1->GetFontFamilyNameLength()) + 1);
|
||||
RETURN_IF_FAILED(format1->GetFontFamilyName(familyName.data(), gsl::narrow<UINT32>(familyName.size())));
|
||||
|
||||
const auto weight = format1->GetFontWeight();
|
||||
@@ -912,7 +927,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
if (textLength < runTextLength)
|
||||
{
|
||||
runTextLength = textLength; // Limit to what's actually left.
|
||||
UINT32 runTextStart = run.textStart;
|
||||
const UINT32 runTextStart = run.textStart;
|
||||
|
||||
_SplitCurrentRun(runTextStart + runTextLength);
|
||||
}
|
||||
@@ -940,12 +955,12 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory,
|
||||
// - <none> - Updates internal state
|
||||
void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition)
|
||||
{
|
||||
if (_runIndex < _runs.size() && _runs[_runIndex].ContainsTextPosition(textPosition))
|
||||
if (_runIndex < _runs.size() && _runs.at(_runIndex).ContainsTextPosition(textPosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_runIndex = static_cast<UINT32>(
|
||||
_runIndex = gsl::narrow<UINT32>(
|
||||
std::find(_runs.begin(), _runs.end(), textPosition) - _runs.begin());
|
||||
}
|
||||
|
||||
@@ -957,13 +972,13 @@ void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition)
|
||||
// - <none> - Updates internal state, the back half will be selected after running
|
||||
void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
|
||||
{
|
||||
UINT32 runTextStart = _runs.at(_runIndex).textStart;
|
||||
const UINT32 runTextStart = _runs.at(_runIndex).textStart;
|
||||
|
||||
if (splitPosition <= runTextStart)
|
||||
return; // no change
|
||||
|
||||
// Grow runs by one.
|
||||
size_t totalRuns = _runs.size();
|
||||
const size_t totalRuns = _runs.size();
|
||||
try
|
||||
{
|
||||
_runs.resize(totalRuns + 1);
|
||||
@@ -974,16 +989,16 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
|
||||
}
|
||||
|
||||
// Copy the old run to the end.
|
||||
LinkedRun& frontHalf = _runs[_runIndex];
|
||||
LinkedRun& frontHalf = _runs.at(_runIndex);
|
||||
LinkedRun& backHalf = _runs.back();
|
||||
backHalf = frontHalf;
|
||||
|
||||
// Adjust runs' text positions and lengths.
|
||||
UINT32 splitPoint = splitPosition - runTextStart;
|
||||
const UINT32 splitPoint = splitPosition - runTextStart;
|
||||
backHalf.textStart += splitPoint;
|
||||
backHalf.textLength -= splitPoint;
|
||||
frontHalf.textLength = splitPoint;
|
||||
frontHalf.nextRunIndex = static_cast<UINT32>(totalRuns);
|
||||
_runIndex = static_cast<UINT32>(totalRuns);
|
||||
frontHalf.nextRunIndex = gsl::narrow<UINT32>(totalRuns);
|
||||
_runIndex = gsl::narrow<UINT32>(totalRuns);
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace Microsoft::Console::Render
|
||||
public:
|
||||
// Based on the Windows 7 SDK sample at https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/multimedia/DirectWrite/CustomLayout
|
||||
|
||||
CustomTextLayout(IDWriteFactory1* const factory,
|
||||
IDWriteTextAnalyzer1* const analyzer,
|
||||
IDWriteTextFormat* const format,
|
||||
IDWriteFontFace1* const font,
|
||||
CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
|
||||
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
|
||||
gsl::not_null<IDWriteTextFormat*> const format,
|
||||
gsl::not_null<IDWriteFontFace1*> const font,
|
||||
const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters,
|
||||
size_t const width);
|
||||
|
||||
@@ -32,43 +32,43 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE Draw(_In_opt_ void* clientDrawingContext,
|
||||
_In_ IDWriteTextRenderer* renderer,
|
||||
FLOAT originX,
|
||||
FLOAT originY);
|
||||
FLOAT originY) noexcept;
|
||||
|
||||
// IDWriteTextAnalysisSource methods
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength) override;
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength) override;
|
||||
[[nodiscard]] virtual DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override;
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition,
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition,
|
||||
_Outptr_result_buffer_(*textLength) WCHAR const** textString,
|
||||
_Out_ UINT32* textLength) noexcept override;
|
||||
[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() noexcept override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_Outptr_result_z_ WCHAR const** localeName) noexcept override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_Outptr_result_z_ WCHAR const** localeName) override;
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition,
|
||||
_Out_ UINT32* textLength,
|
||||
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) override;
|
||||
_COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept override;
|
||||
|
||||
// IDWriteTextAnalysisSink methods
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_ DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) override;
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override;
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
UINT8 explicitLevel,
|
||||
UINT8 resolvedLevel) override;
|
||||
[[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_ IDWriteNumberSubstitution* numberSubstitution) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_ DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
UINT8 explicitLevel,
|
||||
UINT8 resolvedLevel) override;
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition,
|
||||
UINT32 textLength,
|
||||
_In_ IDWriteNumberSubstitution* numberSubstitution) override;
|
||||
|
||||
protected:
|
||||
// A single contiguous run of characters containing the same analysis results.
|
||||
struct Run
|
||||
{
|
||||
Run() :
|
||||
Run() noexcept :
|
||||
textStart(),
|
||||
textLength(),
|
||||
glyphStart(),
|
||||
@@ -93,12 +93,12 @@ namespace Microsoft::Console::Render
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1> fontFace;
|
||||
FLOAT fontScale;
|
||||
|
||||
inline bool ContainsTextPosition(UINT32 desiredTextPosition) const
|
||||
inline bool ContainsTextPosition(UINT32 desiredTextPosition) const noexcept
|
||||
{
|
||||
return desiredTextPosition >= textStart && desiredTextPosition < textStart + textLength;
|
||||
}
|
||||
|
||||
inline bool operator==(UINT32 desiredTextPosition) const
|
||||
inline bool operator==(UINT32 desiredTextPosition) const noexcept
|
||||
{
|
||||
// Search by text position using std::find
|
||||
return ContainsTextPosition(desiredTextPosition);
|
||||
@@ -108,7 +108,7 @@ namespace Microsoft::Console::Render
|
||||
// Single text analysis run, which points to the next run.
|
||||
struct LinkedRun : Run
|
||||
{
|
||||
LinkedRun() :
|
||||
LinkedRun() noexcept :
|
||||
nextRunIndex(0)
|
||||
{
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace Microsoft::Console::Render
|
||||
IDWriteTextRenderer* renderer,
|
||||
const D2D_POINT_2F origin) noexcept;
|
||||
|
||||
[[nodiscard]] static UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
|
||||
[[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
|
||||
|
||||
private:
|
||||
const ::Microsoft::WRL::ComPtr<IDWriteFactory1> _factory;
|
||||
|
||||
@@ -21,8 +21,10 @@ using namespace Microsoft::Console::Render;
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::IsPixelSnappingDisabled(void* /*clientDrawingContext*/,
|
||||
_Out_ BOOL* isDisabled)
|
||||
_Out_ BOOL* isDisabled) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, isDisabled);
|
||||
|
||||
*isDisabled = false;
|
||||
return S_OK;
|
||||
}
|
||||
@@ -38,9 +40,12 @@ using namespace Microsoft::Console::Render;
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::GetPixelsPerDip(void* clientDrawingContext,
|
||||
_Out_ FLOAT* pixelsPerDip)
|
||||
_Out_ FLOAT* pixelsPerDip) noexcept
|
||||
{
|
||||
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pixelsPerDip);
|
||||
|
||||
const DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
|
||||
|
||||
float dpiX, dpiY;
|
||||
drawingContext->renderTarget->GetDpi(&dpiX, &dpiY);
|
||||
@@ -58,12 +63,24 @@ using namespace Microsoft::Console::Render;
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::GetCurrentTransform(void* clientDrawingContext,
|
||||
DWRITE_MATRIX* transform)
|
||||
DWRITE_MATRIX* transform) noexcept
|
||||
{
|
||||
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, transform);
|
||||
|
||||
const DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
|
||||
|
||||
// Retrieve as D2D1 matrix then copy into DWRITE matrix.
|
||||
D2D1_MATRIX_3X2_F d2d1Matrix{ 0 };
|
||||
drawingContext->renderTarget->GetTransform(&d2d1Matrix);
|
||||
|
||||
transform->dx = d2d1Matrix.dx;
|
||||
transform->dy = d2d1Matrix.dy;
|
||||
transform->m11 = d2d1Matrix.m11;
|
||||
transform->m12 = d2d1Matrix.m12;
|
||||
transform->m21 = d2d1Matrix.m21;
|
||||
transform->m22 = d2d1Matrix.m22;
|
||||
|
||||
// Matrix structures are defined identically
|
||||
drawingContext->renderTarget->GetTransform((D2D1_MATRIX_3X2_F*)transform);
|
||||
return S_OK;
|
||||
}
|
||||
#pragma endregion
|
||||
@@ -88,17 +105,16 @@ using namespace Microsoft::Console::Render;
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_UNDERLINE* underline,
|
||||
IUnknown* clientDrawingEffect)
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
_FillRectangle(clientDrawingContext,
|
||||
clientDrawingEffect,
|
||||
baselineOriginX,
|
||||
baselineOriginY + underline->offset,
|
||||
underline->width,
|
||||
underline->thickness,
|
||||
underline->readingDirection,
|
||||
underline->flowDirection);
|
||||
return S_OK;
|
||||
return _FillRectangle(clientDrawingContext,
|
||||
clientDrawingEffect,
|
||||
baselineOriginX,
|
||||
baselineOriginY + underline->offset,
|
||||
underline->width,
|
||||
underline->thickness,
|
||||
underline->readingDirection,
|
||||
underline->flowDirection);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -120,17 +136,16 @@ using namespace Microsoft::Console::Render;
|
||||
FLOAT baselineOriginX,
|
||||
FLOAT baselineOriginY,
|
||||
_In_ const DWRITE_STRIKETHROUGH* strikethrough,
|
||||
IUnknown* clientDrawingEffect)
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
_FillRectangle(clientDrawingContext,
|
||||
clientDrawingEffect,
|
||||
baselineOriginX,
|
||||
baselineOriginY + strikethrough->offset,
|
||||
strikethrough->width,
|
||||
strikethrough->thickness,
|
||||
strikethrough->readingDirection,
|
||||
strikethrough->flowDirection);
|
||||
return S_OK;
|
||||
return _FillRectangle(clientDrawingContext,
|
||||
clientDrawingEffect,
|
||||
baselineOriginX,
|
||||
baselineOriginY + strikethrough->offset,
|
||||
strikethrough->width,
|
||||
strikethrough->thickness,
|
||||
strikethrough->readingDirection,
|
||||
strikethrough->flowDirection);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -146,16 +161,17 @@ using namespace Microsoft::Console::Render;
|
||||
// - flowDirection - textual flow information that could affect the rectangle
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
IUnknown* clientDrawingEffect,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float thickness,
|
||||
DWRITE_READING_DIRECTION /*readingDirection*/,
|
||||
DWRITE_FLOW_DIRECTION /*flowDirection*/)
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
IUnknown* clientDrawingEffect,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float thickness,
|
||||
DWRITE_READING_DIRECTION /*readingDirection*/,
|
||||
DWRITE_FLOW_DIRECTION /*flowDirection*/) noexcept
|
||||
{
|
||||
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext);
|
||||
|
||||
// Get brush
|
||||
ID2D1Brush* brush = drawingContext->foregroundBrush;
|
||||
@@ -165,8 +181,10 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
brush = static_cast<ID2D1Brush*>(clientDrawingEffect);
|
||||
}
|
||||
|
||||
D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness);
|
||||
const D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness);
|
||||
drawingContext->renderTarget->FillRectangle(&rect, brush);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -189,8 +207,10 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
IDWriteInlineObject* inlineObject,
|
||||
BOOL isSideways,
|
||||
BOOL isRightToLeft,
|
||||
IUnknown* clientDrawingEffect)
|
||||
IUnknown* clientDrawingEffect) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, inlineObject);
|
||||
|
||||
return inlineObject->Draw(clientDrawingContext,
|
||||
this,
|
||||
originX,
|
||||
@@ -233,12 +253,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
|
||||
// Since we've delegated the drawing of the background of the text into this function, the origin passed in isn't actually the baseline.
|
||||
// It's the top left corner. Save that off first.
|
||||
D2D1_POINT_2F origin = D2D1::Point2F(baselineOriginX, baselineOriginY);
|
||||
const D2D1_POINT_2F origin = D2D1::Point2F(baselineOriginX, baselineOriginY);
|
||||
|
||||
// Then make a copy for the baseline origin (which is part way down the left side of the text, not the top or bottom).
|
||||
// We'll use this baseline Origin for drawing the actual text.
|
||||
D2D1_POINT_2F baselineOrigin = origin;
|
||||
baselineOrigin.y += drawingContext->spacing.baseline;
|
||||
const D2D1_POINT_2F baselineOrigin{ origin.x, origin.y + drawingContext->spacing.baseline };
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
|
||||
RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
|
||||
@@ -249,11 +268,9 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
rect.bottom = rect.top + drawingContext->cellSize.height;
|
||||
rect.left = origin.x;
|
||||
rect.right = rect.left;
|
||||
const auto advancesSpan = gsl::make_span(glyphRun->glyphAdvances, glyphRun->glyphCount);
|
||||
|
||||
for (UINT32 i = 0; i < glyphRun->glyphCount; i++)
|
||||
{
|
||||
rect.right += glyphRun->glyphAdvances[i];
|
||||
}
|
||||
rect.right = std::accumulate(advancesSpan.cbegin(), advancesSpan.cend(), rect.right);
|
||||
|
||||
d2dContext->FillRectangle(rect, drawingContext->backgroundBrush);
|
||||
|
||||
@@ -270,7 +287,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
RETURN_IF_FAILED(drawingContext->dwriteFactory->QueryInterface(dwriteFactory4.GetAddressOf()));
|
||||
|
||||
// The list of glyph image formats this renderer is prepared to support.
|
||||
DWRITE_GLYPH_IMAGE_FORMATS supportedFormats =
|
||||
const DWRITE_GLYPH_IMAGE_FORMATS supportedFormats =
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_CFF |
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
|
||||
@@ -283,14 +300,14 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
// Determine whether there are any color glyph runs within glyphRun. If
|
||||
// there are, glyphRunEnumerator can be used to iterate through them.
|
||||
::Microsoft::WRL::ComPtr<IDWriteColorGlyphRunEnumerator1> glyphRunEnumerator;
|
||||
HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin,
|
||||
glyphRun,
|
||||
glyphRunDescription,
|
||||
supportedFormats,
|
||||
measuringMode,
|
||||
nullptr,
|
||||
0,
|
||||
&glyphRunEnumerator);
|
||||
const HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin,
|
||||
glyphRun,
|
||||
glyphRunDescription,
|
||||
supportedFormats,
|
||||
measuringMode,
|
||||
nullptr,
|
||||
0,
|
||||
&glyphRunEnumerator);
|
||||
|
||||
// If the analysis found no color glyphs in the run, just draw normally.
|
||||
if (hr == DWRITE_E_NOCOLOR)
|
||||
@@ -320,7 +337,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
DWRITE_COLOR_GLYPH_RUN1 const* colorRun;
|
||||
RETURN_IF_FAILED(glyphRunEnumerator->GetCurrentRun(&colorRun));
|
||||
|
||||
D2D1_POINT_2F currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY);
|
||||
const D2D1_POINT_2F currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY);
|
||||
|
||||
switch (colorRun->glyphImageFormat)
|
||||
{
|
||||
@@ -357,7 +374,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
// This run is solid-color outlines, either from non-color
|
||||
// glyphs or from COLR glyph layers. Use Direct2D to draw them.
|
||||
|
||||
ID2D1Brush* layerBrush;
|
||||
ID2D1Brush* layerBrush{ nullptr };
|
||||
// The rule is "if 0xffff, use current brush." See:
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/dwrite_2/ns-dwrite_2-dwrite_color_glyph_run
|
||||
if (colorRun->paletteIndex == 0xFFFF)
|
||||
@@ -414,6 +431,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
|
||||
ID2D1Brush* brush)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRunDescription);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, brush);
|
||||
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
|
||||
RETURN_IF_FAILED(clientDrawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
|
||||
|
||||
@@ -433,8 +455,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE /*measuringMode*/,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/)
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
|
||||
|
||||
// This is regular text but manually
|
||||
::Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
|
||||
clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf());
|
||||
@@ -473,8 +498,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE /*measuringMode*/,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/)
|
||||
_In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun);
|
||||
|
||||
// This is glow text manually
|
||||
::Microsoft::WRL::ComPtr<ID2D1Factory> d2dFactory;
|
||||
clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user