Compare commits
182 Commits
dev/migrie
...
release-0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e50eba041c | ||
|
|
af1d06adec | ||
|
|
8f0bd3caf3 | ||
|
|
949ad80e51 | ||
|
|
565b9b49eb | ||
|
|
4411ce50e9 | ||
|
|
c33a138a90 | ||
|
|
da5a6280b3 | ||
|
|
b6a6a0376f | ||
|
|
983f9b5a47 | ||
|
|
e22487d10b | ||
|
|
0e36ce4d60 | ||
|
|
f0f19f9fd0 | ||
|
|
171f00c265 | ||
|
|
2b1a35a890 | ||
|
|
7ae815ccac | ||
|
|
714d79e2c8 | ||
|
|
62d7f11b4a | ||
|
|
99a8337185 | ||
|
|
2915be5b51 | ||
|
|
71debc158b | ||
|
|
b3ecb7392f | ||
|
|
6dec0b66d2 | ||
|
|
ffad815f4a | ||
|
|
f00a8ae086 | ||
|
|
663a1bbe6e | ||
|
|
0c2ae7015f | ||
|
|
d1e9de7882 | ||
|
|
8ab666c5fc | ||
|
|
9ed3da8b3b | ||
|
|
cc8faaf04f | ||
|
|
efa68abd3d | ||
|
|
a8e352ed87 | ||
|
|
790f49460a | ||
|
|
9d8a82d863 | ||
|
|
eccd55b8b3 | ||
|
|
ebdcfbd940 | ||
|
|
6a4c737686 | ||
|
|
d552959378 | ||
|
|
13406b746b | ||
|
|
a404778271 | ||
|
|
c9f148c4b6 | ||
|
|
1177815f81 | ||
|
|
306e751639 | ||
|
|
fe4c80b27d | ||
|
|
d2ca3c1fb0 | ||
|
|
9a84521965 | ||
|
|
e2994ff890 | ||
|
|
7068b3124d | ||
|
|
db79758092 | ||
|
|
58b52ef69e | ||
|
|
c274b38dcc | ||
|
|
ddcc06e911 | ||
|
|
4dd476ecbd | ||
|
|
bd1604a0b5 | ||
|
|
3e8a1a78bc | ||
|
|
d26865f460 | ||
|
|
357e835f5d | ||
|
|
d2dcdef620 | ||
|
|
f0c110593a | ||
|
|
3df292430f | ||
|
|
fee3fdf322 | ||
|
|
9dc922fc37 | ||
|
|
52d7de38b1 | ||
|
|
53b6f143b3 | ||
|
|
388b975663 | ||
|
|
94213ad94c | ||
|
|
5dfc021d8e | ||
|
|
6f36f8b23f | ||
|
|
a4c24f82e6 | ||
|
|
de9231a88b | ||
|
|
15505337d8 | ||
|
|
a34c47a493 | ||
|
|
64943bd033 | ||
|
|
86e0ea73e2 | ||
|
|
8c8672c87a | ||
|
|
26decf13d0 | ||
|
|
126d489af9 | ||
|
|
581d63fa8d | ||
|
|
c0399b2c2e | ||
|
|
46ac1918ec | ||
|
|
891b34de07 | ||
|
|
c879cd3f97 | ||
|
|
444de5b166 | ||
|
|
634687bae3 | ||
|
|
566ed8ddbb | ||
|
|
f2a99c56c9 | ||
|
|
5ad1deb696 | ||
|
|
d6790c023f | ||
|
|
06f2706c40 | ||
|
|
18c5fce43d | ||
|
|
9c7e8ce1e3 | ||
|
|
60b94a481e | ||
|
|
926a2e3d80 | ||
|
|
5c55bb8d02 | ||
|
|
4991b9f1b2 | ||
|
|
73462c3986 | ||
|
|
9e5792ba51 | ||
|
|
a02a5d9986 | ||
|
|
d80500f112 | ||
|
|
6f7ad99d51 | ||
|
|
35d7d20a07 | ||
|
|
b293b2bada | ||
|
|
01c0736843 | ||
|
|
1925173b02 | ||
|
|
12c2819e6a | ||
|
|
df26c677ef | ||
|
|
5d17557edf | ||
|
|
6708556079 | ||
|
|
c9050416f6 | ||
|
|
b664761c79 | ||
|
|
b9233c03d1 | ||
|
|
7b23d8e66c | ||
|
|
82dd0b978a | ||
|
|
200e90d1c6 | ||
|
|
dd2fbef39d | ||
|
|
cd40faa88f | ||
|
|
aa682bfd12 | ||
|
|
0691c21876 | ||
|
|
94fc40ed31 | ||
|
|
dec5c11e19 | ||
|
|
53c81a08f8 | ||
|
|
505ceaccf6 | ||
|
|
8ae65f5444 | ||
|
|
9756c7c3f7 | ||
|
|
de7a0686b3 | ||
|
|
a82d6b8c69 | ||
|
|
a9f384931e | ||
|
|
b97db63030 | ||
|
|
64c98db024 | ||
|
|
621d841538 | ||
|
|
06bd7e22da | ||
|
|
17495fcda3 | ||
|
|
abf3ee5d6e | ||
|
|
0ce08aff32 | ||
|
|
33361698f7 | ||
|
|
847d6b56ad | ||
|
|
3294a8f7b1 | ||
|
|
5d906d9f3e | ||
|
|
4dd9f9c180 | ||
|
|
a2f8a943b4 | ||
|
|
5c4e8f52fb | ||
|
|
6831120755 | ||
|
|
52534c94cc | ||
|
|
083be43700 | ||
|
|
1caece74ab | ||
|
|
23bea9e5b5 | ||
|
|
258c8b407c | ||
|
|
0dc1c5b163 | ||
|
|
0df6415e5b | ||
|
|
6f4b98acb4 | ||
|
|
eafa884fc4 | ||
|
|
86c9e586fe | ||
|
|
6b728cd6d0 | ||
|
|
2c8b3243dc | ||
|
|
1386148191 | ||
|
|
275b651c8f | ||
|
|
7faf3342e0 | ||
|
|
9ed9e7c8aa | ||
|
|
60c6a9fb8f | ||
|
|
a862f3196f | ||
|
|
f5071439a3 | ||
|
|
a9d57ef9ea | ||
|
|
a1bd7967e2 | ||
|
|
90a3d99512 | ||
|
|
0c8a4df963 | ||
|
|
1c412d42b3 | ||
|
|
277acc3383 | ||
|
|
8afc5b2f59 | ||
|
|
b096a57387 | ||
|
|
6b415126fd | ||
|
|
bfb1484708 | ||
|
|
56c35945b9 | ||
|
|
9102c5d030 | ||
|
|
b84a073464 | ||
|
|
b37d6c03ac | ||
|
|
dfaaa44789 | ||
|
|
7128e873a4 | ||
|
|
a62c6cd22b | ||
|
|
5705347640 | ||
|
|
5a57c5a6c9 | ||
|
|
4b439cf290 |
4
.github/ISSUE_TEMPLATE/Bug_Report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug report 🐛
|
||||
name: "Bug report 🐛"
|
||||
about: Report errors or unexpected behavior
|
||||
title: "Bug Report (IF I DO NOT CHANGE THIS THE ISSUE WILL BE AUTO-CLOSED)"
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Documentation Issue 📚
|
||||
name: "Documentation Issue 📚"
|
||||
about: Report issues in our documentation
|
||||
title: "Documentation Issue"
|
||||
title: ''
|
||||
labels: Issue-Docs
|
||||
assignees: ''
|
||||
|
||||
|
||||
69
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -1,34 +1,35 @@
|
||||
---
|
||||
name: Feature Request/Idea 🚀
|
||||
about: Suggest a new feature or improvement (this does not mean you have to implement it)
|
||||
title: "Feature Request"
|
||||
labels: Issue-Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
|
||||
I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
|
||||
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
|
||||
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
|
||||
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
|
||||
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
|
||||
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.
|
||||
|
||||
All good? Then proceed!
|
||||
-->
|
||||
|
||||
# Description of the new feature/enhancement
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the problem is that the new feature would solve.
|
||||
Describe why and how a user would use this new functionality (if applicable).
|
||||
-->
|
||||
|
||||
# Proposed technical implementation details (optional)
|
||||
|
||||
<!--
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
---
|
||||
name: "Feature Request/Idea 🚀"
|
||||
about: Suggest a new feature or improvement (this does not mean you have to implement
|
||||
it)
|
||||
title: ''
|
||||
labels: Issue-Feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
|
||||
|
||||
I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
|
||||
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
|
||||
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
|
||||
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
|
||||
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
|
||||
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.
|
||||
|
||||
All good? Then proceed!
|
||||
-->
|
||||
|
||||
# Description of the new feature/enhancement
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the problem is that the new feature would solve.
|
||||
Describe why and how a user would use this new functionality (if applicable).
|
||||
-->
|
||||
|
||||
# Proposed technical implementation details (optional)
|
||||
|
||||
<!--
|
||||
A clear and concise description of what you want to happen.
|
||||
-->
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/Guidance_Issue.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Community Guidance Request ✨
|
||||
about: Suggest somewhere the Windows Terminal Team needs to provide community guidance through new documentation or process.
|
||||
title: "Guidance"
|
||||
labels: Issue-Docs
|
||||
assignees: 'bitcrazed'
|
||||
|
||||
---
|
||||
|
||||
<!-- What needs to change? Who is responsible for it? Why is it an open question? -->
|
||||
303
OpenConsole.sln
207
README.md
@@ -1,157 +1,166 @@
|
||||
# Welcome\!
|
||||
#### This repository contains the source code for:
|
||||
# Welcome to the Windows Terminal, Console and Command-Line repo
|
||||
|
||||
* [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701)
|
||||
* The Windows console host (`conhost.exe`)
|
||||
* Components shared between the two projects
|
||||
* [ColorTool](https://github.com/Microsoft/Terminal/tree/master/src/tools/ColorTool)
|
||||
* [Sample projects](https://github.com/Microsoft/Terminal/tree/master/samples) that show how to consume the Windows Console APIs
|
||||
|
||||
#### Other related repositories include:
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
This repository contains the source code for:
|
||||
|
||||
## Installation
|
||||
* [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701)
|
||||
* The Windows console host (`conhost.exe`)
|
||||
* Components shared between the two projects
|
||||
* [ColorTool](https://github.com/Microsoft/Terminal/tree/master/src/tools/ColorTool)
|
||||
* [Sample projects](https://github.com/Microsoft/Terminal/tree/master/samples) that show how to consume the Windows Console APIs
|
||||
|
||||
_(Note: in order to run the Windows Terminal, you'll need to be running at least Windows build 18362 or higher.)_
|
||||
Related repositories include:
|
||||
|
||||
### Microsoft Store
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
* [Cascadia Code Font](https://github.com/Microsoft/Cascadia-Code)
|
||||
|
||||
Download the Microsoft Terminal free from the Microsoft Store and it'll be continuously updated. Or, feel free to side-load [releases](https://github.com/microsoft/terminal/releases) from GitHub, but note they won't auto-update.
|
||||
## Installing and running Windows Terminal
|
||||
|
||||
<a href='//www.microsoft.com/store/apps/9n0dx20hk701?cid=storebadge&ocid=badge'><img src='https://assets.windowsphone.com/85864462-9c82-451e-9355-a3d5f874397a/English_get-it-from-MS_InvariantCulture_Default.png' alt='English badge' width="284" height="104" style='width: 284px; height: 104px;'/></a>
|
||||
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
|
||||
|
||||
### Chocolatey (Unofficial)
|
||||
### Manually installing builds from this repository
|
||||
|
||||
Download and upgrade the Windows Terminal from [Chocolatey](https://chocolatey.org).
|
||||
For users who are unable to install Terminal from the Microsoft Store, Terminal builds can be manually downloaded from this repository's [Releases page](https://github.com/microsoft/terminal/releases).
|
||||
|
||||
> ⚠ Note: If you install Terminal manually:
|
||||
>
|
||||
> * Be sure to install the [Desktop Bridge VC++ v14 Redistributable Package](https://www.microsoft.com/en-us/download/details.aspx?id=53175) otherwise Terminal may not install and/or run and may crash at startup
|
||||
> * Terminal will not auto-update when new builds are released so you will need to regularly install the latest Terminal release to receive all the latest fixes and improvements!
|
||||
|
||||
### Install via Chocolatey (unofficial)
|
||||
|
||||
[Chocolatey](https://chocolatey.org) users can download and install the latest Terminal release by installing the `microsoft-windows-terminal` package:
|
||||
|
||||
To install Windows Terminal, run the following command from the command line or from PowerShell:
|
||||
```powershell
|
||||
choco install microsoft-windows-terminal
|
||||
```
|
||||
|
||||
To upgrade Windows Terminal, run the following command from the command line or from PowerShell:
|
||||
To upgrade Windows Terminal using Chocolatey, run the following:
|
||||
|
||||
```powershell
|
||||
choco upgrade microsoft-windows-terminal
|
||||
```
|
||||
|
||||
If you have any issues when installing/upgrading the package please go to the [package page](https://chocolatey.org/packages/microsoft-windows-terminal) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
|
||||
If you have any issues when installing/upgrading the package please go to the [Windows Terminal package page](https://chocolatey.org/packages/microsoft-windows-terminal) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
|
||||
|
||||
### Build Status
|
||||
---
|
||||
|
||||
## Project Build Status
|
||||
|
||||
Project|Build Status
|
||||
---|---
|
||||
Terminal|[](https://dev.azure.com/ms/Terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
|
||||
# Terminal & Console Overview
|
||||
---
|
||||
|
||||
## Windows Terminal v1.0 Roadmap
|
||||
|
||||
The plan for delivering Windows Terminal v1.0 [is described here](/doc/terminal-v1-roadmap.md), and will be updated as the project proceeds.
|
||||
|
||||
---
|
||||
|
||||
## Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the code:
|
||||
|
||||
## Windows Terminal
|
||||
### Windows Terminal
|
||||
|
||||
Windows Terminal is a new, modern, feature-rich, productive terminal application for command-line users. It includes many of the features most frequently requested by the Windows command-line community including support for tabs, rich text, globalization, configurability, theming & styling, and more.
|
||||
|
||||
The Terminal will also need to meet our goals and measures to ensure it remains fast, and efficient, and doesn't consume vast amounts of memory or power.
|
||||
The Terminal will also need to meet our goals and measures to ensure it remains fast and efficient, and doesn't consume vast amounts of memory or power.
|
||||
|
||||
## The Windows console host
|
||||
### The Windows Console Host
|
||||
|
||||
The Windows console host, `conhost.exe`, is Windows' original command-line user experience. It implements Windows' command-line infrastructure, and is responsible for hosting the Windows Console API, input engine, rendering engine, and user preferences. The console host code in this repository is the actual source from which the `conhost.exe` in Windows itself is built.
|
||||
The Windows Console host, `conhost.exe`, is Windows' original command-line user experience. It also hosts Windows' command-line infrastructure and the Windows Console API server, input engine, rendering engine, user preferences, etc. The console host code in this repository is the actual source from which the `conhost.exe` in Windows itself is built.
|
||||
|
||||
Console's primary goal is to remain backwards-compatible with existing console subsystem applications.
|
||||
Since taking ownership of the Windows command-line in 2014, the team added several new features to the Console, including background transparency, line-based selection, support for [ANSI / Virtual Terminal sequences](https://en.wikipedia.org/wiki/ANSI_escape_code), [24-bit color](https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/), a [Pseudoconsole ("ConPTY")](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/), and more.
|
||||
|
||||
Since assuming ownership of the Windows command-line in 2014, the team has added several new features to the Console, including window transparency, line-based selection, support for [ANSI / Virtual Terminal sequences](https://en.wikipedia.org/wiki/ANSI_escape_code), [24-bit color](https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/), a [Pseudoconsole ("ConPTY")](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/), and more.
|
||||
|
||||
However, because the Console's primary goal is to maintain backward compatibility, we've been unable to add many of the features the community has been asking for, and which we've been wanting to add for the last several years--like tabs!
|
||||
However, because Windows Console's primary goal is to maintain backward compatibility, we have been unable to add many of the features the community (and the team) have been wanting for the last several years including tabs, unicode text, and emoji.
|
||||
|
||||
These limitations led us to create the new Windows Terminal.
|
||||
|
||||
## Shared Components
|
||||
> You can read more about the evolution of the command-line in general, and the Windows command-line specifically in [this accompanying series of blog posts](https://devblogs.microsoft.com/commandline/windows-command-line-backgrounder/) on the Command-Line team's blog.
|
||||
|
||||
While overhauling the Console, we've modernized its codebase considerably. We've cleanly separated logical entities into modules and classes, introduced some key extensibility points, replaced several old, home-grown collections and containers with safer, more efficient [STL containers](https://docs.microsoft.com/en-us/cpp/standard-library/stl-containers?view=vs-2019), and made the code simpler and safer by using Microsoft's [WIL](https://github.com/Microsoft/wil) header library.
|
||||
### Shared Components
|
||||
|
||||
This overhaul work resulted in the creation of several key components that would be useful for any terminal implementation on Windows, including a new DirectWrite-based text layout and rendering engine, a text buffer capable of storing both UTF-16 and UTF-8, and a VT parser/emitter.
|
||||
While overhauling Windows Console, we modernized its codebase considerably, cleanly separating logical entities into modules and classes, introduced some key extensibility points, replaced several old, home-grown collections and containers with safer, more efficient [STL containers](https://docs.microsoft.com/en-us/cpp/standard-library/stl-containers?view=vs-2019), and made the code simpler and safer by using Microsoft's [Windows Implementation Libraries - WIL](https://github.com/Microsoft/wil).
|
||||
|
||||
## Building a new terminal
|
||||
This overhaul resulted in several of Console's key components being available for re-use in any terminal implementation on Windows. These components include a new DirectWrite-based text layout and rendering engine, a text buffer capable of storing both UTF-16 and UTF-8, a VT parser/emitter, and more.
|
||||
|
||||
When we started building the new terminal application, we explored and evaluated several approaches and technology stacks. We ultimately decided that our goals would be best met by sticking with C++ and sharing the aforementioned modernized components, placing them atop the modern Windows application platform and UI framework.
|
||||
### Creating the new Windows Terminal
|
||||
|
||||
Further, we realized that this would allow us to build the terminal's renderer and input stack as a reusable Windows UI control that others can incorporate into their applications.
|
||||
When we started planning the new Windows Terminal application, we explored and evaluated several approaches and technology stacks. We ultimately decided that our goals would be best met by continuing our investment in our C++ codebase, which would allow us to reuse several of the aforementioned modernized components in both the existing Console and the new Terminal. Further, we realized that this would allow us to build much of the Terminal's core itself as a reusable UI control that others can incorporate into their own applications.
|
||||
|
||||
# FAQ
|
||||
The result of this work is contained within this repo and delivered as the Windows Terminal application you can download from the Microsoft Store, or [directly from this repo's releases](https://github.com/microsoft/terminal/releases).
|
||||
|
||||
## Where can I download Windows Terminal?
|
||||
---
|
||||
|
||||
### The Windows Terminal preview can be downloaded from the Microsoft Store.
|
||||
## Resources
|
||||
|
||||
[https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701)
|
||||
For more information about Windows Terminal, you may find some of these resources useful and interesting:
|
||||
|
||||
## I built and ran the new Terminal, but I just get a blank window app!
|
||||
* [Command-Line Blog](https://devblogs.microsoft.com/commandline)
|
||||
* [Command-Line Backgrounder Blog Series](https://devblogs.microsoft.com/commandline/windows-command-line-backgrounder/)
|
||||
* Windows Terminal Launch: [Terminal "Sizzle Video"](https://www.youtube.com/watch?v=8gw0rXPMMPE&list=PLEHMQNlPj-Jzh9DkNpqipDGCZZuOwrQwR&index=2&t=0s)
|
||||
* Windows Terminal Launch: [Build 2019 Session](https://www.youtube.com/watch?v=KMudkRcwjCw)
|
||||
* Run As Radio: [Show 645 - Windows Terminal with Richard Turner](http://www.runasradio.com/Shows/Show/645)
|
||||
* Azure Devops Podcast: [Episode 54 - Kayla Cinnamon and Rich Turner on DevOps on the Windows Terminal](http://azuredevopspodcast.clear-measure.com/kayla-cinnamon-and-rich-turner-on-devops-on-the-windows-terminal-team-episode-54)
|
||||
* Microsoft Ignite 2019 Session: [The Modern Windows Command Line: Windows Terminal - BRK3321](https://myignite.techcommunity.microsoft.com/sessions/81329?source=sessions)
|
||||
|
||||
Make sure you are building for your computer's architecture. If your box has a 64-bit Windows, change your Solution Platform to x64.
|
||||
To check your OS architecture go to Settings -> System -> About (or Win+X -> System) and under `Device specifications` check for the `System type`.
|
||||
---
|
||||
|
||||
## I built and ran the new Terminal, but it looks just like the old console! What gives?
|
||||
## FAQ
|
||||
|
||||
Firstly, make sure you're building & deploying `CascadiaPackage` in Visual Studio, _NOT_ `Host.EXE`. `OpenConsole.exe` is just `conhost.exe`, the same old console you know and love. `opencon.cmd` will launch `openconsole.exe`, and unfortunately, `openterm.cmd` is currently broken.
|
||||
### I built and ran the new Terminal, but it looks just like the old console
|
||||
|
||||
Secondly, try pressing <kbd>Ctrl</kbd> + <kbd>T</kbd>. The tabs are hidden when you only have one tab by default. In the future, the UI will be dramatically different, but for now, the defaults are _supposed_ to look like the console defaults.
|
||||
Cause: You're launching the incorrect solution in Visual Studio.
|
||||
|
||||
## I tried running WindowsTerminal.exe and it crashes!
|
||||
Solution: Make sure you're building & deploying the `CascadiaPackage` project in Visual Studio.
|
||||
|
||||
* Don't try to run it unpackaged. Make sure to build & deploy `CascadiaPackage` from Visual Studio, and run the Windows Terminal (Dev Build) app.
|
||||
* Make sure you're on the right version of Windows. You'll need to be on Insider's builds, or wait for the 1903 release, as the Windows Terminal **REQUIRES** features from the latest Windows release.
|
||||
> ⚠ Note: `OpenConsole.exe` is just a locally-built `conhost.exe`, the classic Windows Console that hosts Windows' command-line infrastructure. OpenConsole is used by Windows Terminal to connect to and communicate with command-line applications (via [ConPty](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/)).
|
||||
|
||||
# Getting Started
|
||||
---
|
||||
|
||||
## Debugging
|
||||
## Documentation
|
||||
|
||||
* To debug in VS, right click on CascadiaPackage (from VS Solution Explorer) and go to properties, in the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
All project documentation is located in the `./doc` folder. If you would like to contribute to the documentation, please submit a pull request.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We are excited to work alongside you, our amazing community, to build and enhance Windows Terminal\!
|
||||
|
||||
We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/doc/contributing.md). We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
|
||||
|
||||
> 👉 **Remember\!** Your contributions may be incorporated into future versions of Windows\! Because of this, all pull requests will be subject to the same level of scrutiny for quality, coding standards, performance, globalization, accessibility, and compatibility as those of our internal contributors.
|
||||
|
||||
> ⚠ **Note**: The Command-Line Team is actively working out of this repository and will be periodically re-structuring the code to make it easier to comprehend, navigate, build, test, and contribute to, so **DO expect significant changes to code layout on a regular basis**.
|
||||
|
||||
## Documentation
|
||||
|
||||
All documentation is located in the `./doc` folder. If you would like to contribute to the documentation, please submit a pull request.
|
||||
***BEFORE you start work on a feature/fix***, please read & follow our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/contributing.md) to help avoid any wasted or duplicate effort.
|
||||
|
||||
## Communicating with the Team
|
||||
|
||||
The easiest way to communicate with the team is via GitHub issues. Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before you do**.
|
||||
The easiest way to communicate with the team is via GitHub issues.
|
||||
|
||||
Please help us keep this repository clean, inclusive, and fun\! We will not tolerate any abusive, rude, disrespectful or inappropriate behavior. Read our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/) for more details.
|
||||
Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before creating a new issue.**
|
||||
|
||||
If you would like to ask a question that you feel doesn't warrant an issue (yet), please reach out to us via Twitter:
|
||||
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
* Kayla Cinnamon, Program Manager: [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
* Mike Griese, Developer: [@zadjii](https://twitter.com/zadjii)
|
||||
* Carlos Zamora, Developer: [@cazamor_msft](https://twitter.com/cazamor_msft)
|
||||
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
## Developer Guidance
|
||||
|
||||
* Kayla Cinnamon, Program Manager (especially for UX issues): [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
|
||||
# Developer Guidance
|
||||
|
||||
## Build Prerequisites
|
||||
|
||||
* You must be running Windows 1903 (build >= 10.0.18362.0) or above in order to run Windows Terminal.
|
||||
* You must have the [1903 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) (build 10.0.18362.0) installed.
|
||||
* You must have at least [VS 2019](https://visualstudio.microsoft.com/downloads/) installed.
|
||||
* You must install the following Workloads via the VS Installer. Opening the solution will [prompt you to install missing components automatically](https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/).
|
||||
- Desktop Development with C++
|
||||
- Universal Windows Platform Development
|
||||
- **The following Individual Components**
|
||||
- C++ (v142) Universal Windows Platform Tools
|
||||
|
||||
* You must also [enable Developer Mode in the Windows Settings app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) to locally install and run the Terminal app.
|
||||
## Prerequisites
|
||||
|
||||
* You must be running Windows 1903 (build >= 10.0.18362.0) or later to run Windows Terminal
|
||||
* You must [enable Developer Mode in the Windows Settings app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) to locally install and run Windows Terminal
|
||||
* You must have the [Windows 10 1903 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed
|
||||
* You must have at least [VS 2019](https://visualstudio.microsoft.com/downloads/) installed
|
||||
* You must install the following Workloads via the VS Installer. Note: Opening the solution in VS 2019 will [prompt you to install missing components automatically](https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/):
|
||||
* Desktop Development with C++
|
||||
* Universal Windows Platform Development
|
||||
* **The following Individual Components**
|
||||
* C++ (v142) Universal Windows Platform Tools
|
||||
|
||||
## Building the Code
|
||||
|
||||
@@ -161,9 +170,9 @@ This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-S
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using MSBuild. To build from the command line, find your shell below.
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
|
||||
|
||||
### PowerShell
|
||||
### Building in PowerShell
|
||||
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
@@ -171,27 +180,37 @@ Set-MsBuildDevEnvironment
|
||||
Invoke-OpenConsoleBuild
|
||||
```
|
||||
|
||||
### CMD
|
||||
### Building in Cmd
|
||||
|
||||
```shell
|
||||
.\tools\razzle.cmd
|
||||
bcz
|
||||
```
|
||||
|
||||
We've provided a set of convenience scripts as well as [README](./tools/README.md) in the **/tools** directory to help automate the process of building and running tests.
|
||||
## Debugging Terminal
|
||||
|
||||
## Coding Guidance
|
||||
To debug Terminal in VS, right click on `CascadiaPackage` (in the Solution Explorer) and go to properties. In the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
|
||||
Please review these brief docs below relating to our coding standards etc.
|
||||
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
|
||||
|
||||
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones\!)
|
||||
### Debugging
|
||||
|
||||
* To debug in VS, right click on CascadiaPackage (from VS Solution Explorer) and go to properties, in the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
|
||||
### Coding Guidance
|
||||
|
||||
Please review these brief docs below about our coding practices.
|
||||
|
||||
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or write some new ones!)
|
||||
|
||||
This is a work in progress as we learn what we'll need to provide people in order to be effective contributors to our project.
|
||||
|
||||
- [Coding Style](https://github.com/Microsoft/Terminal/blob/master/doc/STYLE.md)
|
||||
- [Code Organization](https://github.com/Microsoft/Terminal/blob/master/doc/ORGANIZATION.md)
|
||||
- [Exceptions in our legacy codebase](https://github.com/Microsoft/Terminal/blob/master/doc/EXCEPTIONS.md)
|
||||
- [Helpful smart pointers and macros for interfacing with Windows in WIL](https://github.com/Microsoft/Terminal/blob/master/doc/WIL.md)
|
||||
* [Coding Style](https://github.com/Microsoft/Terminal/blob/master/doc/STYLE.md)
|
||||
* [Code Organization](https://github.com/Microsoft/Terminal/blob/master/doc/ORGANIZATION.md)
|
||||
* [Exceptions in our legacy codebase](https://github.com/Microsoft/Terminal/blob/master/doc/EXCEPTIONS.md)
|
||||
* [Helpful smart pointers and macros for interfacing with Windows in WIL](https://github.com/Microsoft/Terminal/blob/master/doc/WIL.md)
|
||||
|
||||
---
|
||||
|
||||
# Code of Conduct
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ If you believe you have found a security vulnerability in any Microsoft-owned re
|
||||
|
||||
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).
|
||||
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 [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).
|
||||
|
||||
|
||||
@@ -36,16 +36,33 @@ steps:
|
||||
restoreDirectory: '$(Build.SourcesDirectory)/packages'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
displayName: 'Build solution **\OpenConsole.sln (no packages)'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
# Until there is a servicing release of Visual Studio 2019 Update 3, we must force the values of:
|
||||
# BuildingInsideVisualStudio
|
||||
# _WapBuildingInsideVisualStudio
|
||||
# GenerateAppxPackageOnBuild
|
||||
# because otherwise, they will cause a build instability where MSBuild considers all projects
|
||||
# to always be out-of-date.
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }} /p:BuildingInsideVisualStudio=false;_WapBuildingInsideVisualStudio=false;GenerateAppxPackageOnBuild=false"
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln (CascadiaPackage only)'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }} /p:BuildingInsideVisualStudio=false;_WapBuildingInsideVisualStudio=false;GenerateAppxPackageOnBuild=true /t:Terminal\\CascadiaPackage"
|
||||
clean: false # we're relying on build output fropm the previous run
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Check MSIX for common regressions'
|
||||
inputs:
|
||||
@@ -54,6 +71,14 @@ steps:
|
||||
$Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
|
||||
.\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
|
||||
|
||||
- task: powershell@2
|
||||
displayName: 'Source Index PDBs'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
errorActionPreference: silentlyContinue
|
||||
|
||||
- task: VSTest@2
|
||||
displayName: 'Run Unit Tests'
|
||||
inputs:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Target Name="_ConsoleMapWinmdsToManifestFiles" DependsOnTargets="ResolveAssemblyReferences">
|
||||
<ItemGroup>
|
||||
<!-- For each non-system .winmd file in References, generate a .manifest in IntDir for it. -->
|
||||
<_ConsoleWinmdManifest Include="@(ReferencePath->'$(IntDir)\%(FileName).manifest')" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'">
|
||||
<_ConsoleWinmdManifest Include="@(ReferencePath->'$(IntDir)\%(FileName).manifest')" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference' and '%(ReferencePath.Implementation)' != ''">
|
||||
<WinMDPath>%(ReferencePath.FullPath)</WinMDPath>
|
||||
<Implementation>%(ReferencePath.Implementation)</Implementation>
|
||||
</_ConsoleWinmdManifest>
|
||||
|
||||
85
build/scripts/Index-Pdbs.ps1
Normal file
@@ -0,0 +1,85 @@
|
||||
[CmdLetBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, Position=0)][string]$SearchDir,
|
||||
[Parameter(Mandatory=$true, Position=1)][string]$SourceRoot,
|
||||
[Parameter(Mandatory=$true, Position=2)][string]$CommitId,
|
||||
[string]$Organization = "microsoft",
|
||||
[string]$Repo = "terminal",
|
||||
[switch]$recursive
|
||||
)
|
||||
|
||||
$debuggerPath = (Get-ItemProperty -path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots" -name WindowsDebuggersRoot10).WindowsDebuggersRoot10
|
||||
$srcsrvPath = Join-Path $debuggerPath "x64\srcsrv"
|
||||
$srctoolExe = Join-Path $srcsrvPath "srctool.exe"
|
||||
$pdbstrExe = Join-Path $srcsrvPath "pdbstr.exe"
|
||||
|
||||
$fileTable = @{}
|
||||
foreach ($gitFile in & git ls-files)
|
||||
{
|
||||
$fileTable[$gitFile] = $gitFile
|
||||
}
|
||||
|
||||
$mappedFiles = New-Object System.Collections.ArrayList
|
||||
|
||||
foreach ($file in (Get-ChildItem -r:$recursive "$SearchDir\*.pdb"))
|
||||
{
|
||||
Write-Verbose "Found $file"
|
||||
|
||||
$ErrorActionPreference = "Continue" # Azure Pipelines defaults to "Stop", continue past errors in this script.
|
||||
|
||||
$allFiles = & $srctoolExe -r "$file"
|
||||
|
||||
# If the pdb didn't have enough files then skip it (the srctool output has a blank line even when there's no info
|
||||
# so check for less than 2 lines)
|
||||
if ($allFiles.Length -lt 2)
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
for ($i = 0; $i -lt $allFiles.Length; $i++)
|
||||
{
|
||||
if ($allFiles[$i].StartsWith($SourceRoot, [StringComparison]::OrdinalIgnoreCase))
|
||||
{
|
||||
$relative = $allFiles[$i].Substring($SourceRoot.Length).TrimStart("\")
|
||||
$relative = $relative.Replace("\", "/")
|
||||
|
||||
# Git urls are case-sensitive but the PDB might contain a lowercased version of the file path.
|
||||
# Look up the relative url in the output of "ls-files". If it's not there then it's not something
|
||||
# in git, so don't index it.
|
||||
$relative = $fileTable[$relative]
|
||||
if ($relative)
|
||||
{
|
||||
$mapping = $allFiles[$i] + "*$relative"
|
||||
$mappedFiles.Add($mapping)
|
||||
|
||||
Write-Verbose "Mapped path $($i): $mapping"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pdbstrFile = Join-Path "$env:TEMP" "pdbstr.txt"
|
||||
|
||||
Write-Verbose "pdbstr.txt = $pdbstrFile"
|
||||
|
||||
@"
|
||||
SRCSRV: ini ------------------------------------------------
|
||||
VERSION=2
|
||||
VERCTRL=http
|
||||
SRCSRV: variables ------------------------------------------
|
||||
ORGANIZATION=$Organization
|
||||
REPO=$Repo
|
||||
COMMITID=$CommitId
|
||||
HTTP_ALIAS=https://raw.githubusercontent.com/%ORGANIZATION%/%REPO%/%COMMITID%/
|
||||
HTTP_EXTRACT_TARGET=%HTTP_ALIAS%%var2%
|
||||
SRCSRVTRG=%HTTP_EXTRACT_TARGET%
|
||||
SRC_INDEX=public
|
||||
SRCSRV: source files ---------------------------------------
|
||||
$($mappedFiles -join "`r`n")
|
||||
SRCSRV: end ------------------------------------------------
|
||||
"@ | Set-Content $pdbstrFile
|
||||
|
||||
& $pdbstrExe -p:"$file" -w -s:srcsrv -i:$pdbstrFile
|
||||
}
|
||||
|
||||
# Return with exit 0 to override any weird error code from other tools
|
||||
Exit 0
|
||||
@@ -75,13 +75,11 @@ Try {
|
||||
Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
|
||||
}
|
||||
|
||||
If ($Manifest.Package.Identity.ProcessorArchitecture -Ne "arm64") {
|
||||
### ARM64 doesn't package cpprest_2_10.
|
||||
If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest_2_10.dll" -EA:Ignore)) -And
|
||||
($null -eq (Get-Item "$AppxPackageRootPath\cpprest_2_10d.dll" -EA:Ignore))) {
|
||||
Throw "Failed to find cpprest_2_10.dll -- check the WAP packaging project"
|
||||
}
|
||||
If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10.dll" -EA:Ignore)) -And
|
||||
($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10d.dll" -EA:Ignore))) {
|
||||
Throw "Failed to find cpprest142_2_10.dll -- check the WAP packaging project"
|
||||
}
|
||||
|
||||
} Finally {
|
||||
Remove-Item -Recurse -Force $AppxPackageRootPath
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ We drive the bot by tagging issues with specific labels which cause the bot engi
|
||||
Therefore, if you do file issues, or create PRs, please keep an eye on your GitHub notifications. If you do not respond to requests for information, your issues/PRs may be closed automatically.
|
||||
|
||||
---
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC). See [Security.md](../SECURITY.md) for more information.
|
||||
|
||||
## Before you start, file an issue
|
||||
|
||||
11
custom.props
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- This file is read by XES, which we use in our Release builds. -->
|
||||
<PropertyGroup Label="Version">
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2019</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>7</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -6,7 +6,7 @@
|
||||
## Abstract
|
||||
It should be possible to configure the terminal so that it doesn't send certain keystrokes as input to the terminal, and instead triggers certain actions. Examples of these actions could be copy/pasting text, opening a new tab, or changing the font size.
|
||||
|
||||
This spec describes a mechanism by which we could provide a common implementation of handling keyboard shortcuts like these. This common implementation could then be leveraged and extended by the UX implementation as to handle certain callbacks in the UX layer. For example, The TerminalCore doesn't have a concept of what a tab is, but the keymap abstraction could raise an event such that a WPF app could implement creating a new tab in its idomatic way, and UWP could implement them in their own way.
|
||||
This spec describes a mechanism by which we could provide a common implementation of handling keyboard shortcuts like these. This common implementation could then be leveraged and extended by the UX implementation as to handle certain callbacks in the UX layer. For example, The TerminalCore doesn't have a concept of what a tab is, but the keymap abstraction could raise an event such that a WPF app could implement creating a new tab in its idiomatic way, and UWP could implement them in their own way.
|
||||
|
||||
## Terminology
|
||||
* **Key Chord**: This is any possible keystroke that a user can input
|
||||
|
||||
@@ -1,180 +1,184 @@
|
||||
# Profiles.json Documentation
|
||||
|
||||
## Globals
|
||||
Properties listed below affect the entire window, regardless of the profile settings.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `alwaysShowTabs` | _Required_ | Boolean | `true` | When set to `true`, tabs are always displayed. When set to `false` and `showTabsInTitlebar` is set to `false`, tabs only appear after typing <kbd>Ctrl</kbd> + <kbd>T</kbd>. |
|
||||
| `copyOnSelect` | Optional | Boolean | `false` | When set to `true`, a selection is immediately copied to your clipboard upon creation. When set to `false`, the selection persists and awaits further action. |
|
||||
| `defaultProfile` | _Required_ | String | PowerShell guid | Sets the default profile. Opens by typing <kbd>Ctrl</kbd> + <kbd>T</kbd> or by clicking the '+' icon. The guid of the desired default profile is used as the value. |
|
||||
| `initialCols` | _Required_ | Integer | `120` | The number of columns displayed in the window upon first load. |
|
||||
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
|
||||
| `requestedTheme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `showTerminalTitleInTitlebar` | _Required_ | Boolean | `true` | When set to `true`, titlebar displays the title of the selected tab. When set to `false`, titlebar displays "Windows Terminal". |
|
||||
| `showTabsInTitlebar` | Optional | Boolean | `true` | When set to `true`, the tabs are moved into the titlebar and the titlebar disappears. When set to `false`, the titlebar sits above the tabs. |
|
||||
| `wordDelimiters` | Optional | String | <code> /\()"'-:,.;<>~!@#$%^&*|+=[]{}~?│</code><br>_(`│` is `U+2502 BOX DRAWINGS LIGHT VERTICAL`)_ | Determines the delimiters used in a double click selection. |
|
||||
|
||||
## Profiles
|
||||
Properties listed below are specific to each unique profile.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `acrylicOpacity` | _Required_ | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
|
||||
| `closeOnExit` | _Required_ | Boolean | `true` | When set to `true`, the selected tab closes when `exit` is typed. When set to `false`, the tab will remain open when `exit` is typed. |
|
||||
| `colorScheme` | _Required_ | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `commandline` | _Required_ | String | `powershell.exe` | Executable used in the 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 | `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 | `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. |
|
||||
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `backgroundImage` | Optional | String | | Sets the file location of the Image to draw over the window background. |
|
||||
| `backgroundImageAlignment` | Optional | String | `center` | Sets how the background image aligns to the boundaries of the window. Possible values: `"center"`, `"left"`, `"top"`, `"right"`, `"bottom"`, `"topLeft"`, `"topRight"`, `"bottomLeft"`, `"bottomRight"` |
|
||||
| `backgroundImageOpacity` | Optional | Number | `1.0` | Sets the transparency of the background image. Accepts floating point values from 0-1. |
|
||||
| `backgroundImageStretchMode` | Optional | String | `uniformToFill` | Sets how the background image is resized to fill the window. Possible values: `"none"`, `"fill"`, `"uniform"`, `"uniformToFill"` |
|
||||
| `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. 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. |
|
||||
|
||||
## Schemes
|
||||
Properties listed below are specific to each color scheme. [ColorTool](https://github.com/microsoft/terminal/tree/master/src/tools/ColorTool) is a great tool you can use to create and explore new color schemes. All colors use hex color format.
|
||||
|
||||
| Property | Necessity | Type | Description |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `name` | _Required_ | String | Name of the color scheme. |
|
||||
| `foreground` | _Required_ | String | Sets the foreground color of the color scheme. |
|
||||
| `background` | _Required_ | String | Sets the background color of the color scheme. |
|
||||
| `black` | _Required_ | String | Sets the color used as ANSI black. |
|
||||
| `blue` | _Required_ | String | Sets the color used as ANSI blue. |
|
||||
| `brightBlack` | _Required_ | String | Sets the color used as ANSI bright black. |
|
||||
| `brightBlue` | _Required_ | String | Sets the color used as ANSI bright blue. |
|
||||
| `brightCyan` | _Required_ | String | Sets the color used as ANSI bright cyan. |
|
||||
| `brightGreen` | _Required_ | String | Sets the color used as ANSI bright green. |
|
||||
| `brightPurple` | _Required_ | String | Sets the color used as ANSI bright purple. |
|
||||
| `brightRed` | _Required_ | String | Sets the color used as ANSI bright red. |
|
||||
| `brightWhite` | _Required_ | String | Sets the color used as ANSI bright white. |
|
||||
| `brightYellow` | _Required_ | String | Sets the color used as ANSI bright yellow. |
|
||||
| `cyan` | _Required_ | String | Sets the color used as ANSI cyan. |
|
||||
| `green` | _Required_ | String | Sets the color used as ANSI green. |
|
||||
| `purple` | _Required_ | String | Sets the color used as ANSI purple. |
|
||||
| `red` | _Required_ | String | Sets the color used as ANSI red. |
|
||||
| `white` | _Required_ | String | Sets the color used as ANSI white. |
|
||||
| `yellow` | _Required_ | String | Sets the color used as ANSI yellow. |
|
||||
|
||||
## Keybindings
|
||||
Properties listed below are specific to each custom key binding.
|
||||
|
||||
| Property | Necessity | Type | Description |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `command` | _Required_ | String | The command executed when the associated key bindings are pressed. |
|
||||
| `keys` | _Required_ | Array[String] | Defines the key combinations used to call the command. |
|
||||
|
||||
### Implemented Keybindings
|
||||
|
||||
Bindings listed below are per the implementation in `src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp`
|
||||
|
||||
- copy
|
||||
- copyTextWithoutNewlines
|
||||
- paste
|
||||
- newTab
|
||||
- openNewTabDropdown
|
||||
- duplicateTab
|
||||
- newTabProfile0
|
||||
- newTabProfile1
|
||||
- newTabProfile2
|
||||
- newTabProfile3
|
||||
- newTabProfile4
|
||||
- newTabProfile5
|
||||
- newTabProfile6
|
||||
- newTabProfile7
|
||||
- newTabProfile8
|
||||
- newWindow
|
||||
- closeWindow
|
||||
- closeTab
|
||||
- closePane
|
||||
- switchToTab
|
||||
- nextTab
|
||||
- prevTab
|
||||
- increaseFontSize
|
||||
- decreaseFontSize
|
||||
- scrollUp
|
||||
- scrollDown
|
||||
- scrollUpPage
|
||||
- scrollDownPage
|
||||
- switchToTab0
|
||||
- switchToTab1
|
||||
- switchToTab2
|
||||
- switchToTab3
|
||||
- switchToTab4
|
||||
- switchToTab5
|
||||
- switchToTab6
|
||||
- switchToTab7
|
||||
- switchToTab8
|
||||
- openSettings
|
||||
- splitHorizontal
|
||||
- splitVertical
|
||||
- resizePaneLeft
|
||||
- resizePaneRight
|
||||
- resizePaneUp
|
||||
- resizePaneDown
|
||||
- moveFocusLeft
|
||||
- moveFocusRight
|
||||
- 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:
|
||||
|
||||

|
||||
|
||||
|
||||
# Profiles.json Documentation
|
||||
|
||||
## Globals
|
||||
Properties listed below affect the entire window, regardless of the profile settings.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `alwaysShowTabs` | _Required_ | Boolean | `true` | When set to `true`, tabs are always displayed. When set to `false` and `showTabsInTitlebar` is set to `false`, tabs only appear after typing <kbd>Ctrl</kbd> + <kbd>T</kbd>. |
|
||||
| `copyOnSelect` | Optional | Boolean | `false` | When set to `true`, a selection is immediately copied to your clipboard upon creation. When set to `false`, the selection persists and awaits further action. |
|
||||
| `defaultProfile` | _Required_ | String | PowerShell guid | Sets the default profile. Opens by typing <kbd>Ctrl</kbd> + <kbd>T</kbd> or by clicking the '+' icon. The guid of the desired default profile is used as the value. |
|
||||
| `initialCols` | _Required_ | Integer | `120` | The number of columns displayed in the window upon first load. |
|
||||
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
|
||||
| `requestedTheme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `showTerminalTitleInTitlebar` | _Required_ | Boolean | `true` | When set to `true`, titlebar displays the title of the selected tab. When set to `false`, titlebar displays "Windows Terminal". |
|
||||
| `showTabsInTitlebar` | Optional | Boolean | `true` | When set to `true`, the tabs are moved into the titlebar and the titlebar disappears. When set to `false`, the titlebar sits above the tabs. |
|
||||
| `wordDelimiters` | Optional | String | <code> /\()"'-:,.;<>~!@#$%^&*|+=[]{}~?│</code><br>_(`│` is `U+2502 BOX DRAWINGS LIGHT VERTICAL`)_ | Determines the delimiters used in a double click selection. |
|
||||
|
||||
## Profiles
|
||||
Properties listed below are specific to each unique profile.
|
||||
|
||||
| Property | Necessity | Type | Default | Description |
|
||||
| -------- | --------- | ---- | ------- | ----------- |
|
||||
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
|
||||
| `name` | _Required_ | String | | Name of the profile. Displays in the dropdown menu. <br>Additionally, this value will be used as the "title" to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. This "title" behavior can be overriden by using `tabTitle`. |
|
||||
| `acrylicOpacity` | Optional | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
|
||||
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `backgroundImage` | Optional | String | | Sets the file location of the Image to draw over the window background. |
|
||||
| `backgroundImageAlignment` | Optional | String | `center` | Sets how the background image aligns to the boundaries of the window. Possible values: `"center"`, `"left"`, `"top"`, `"right"`, `"bottom"`, `"topLeft"`, `"topRight"`, `"bottomLeft"`, `"bottomRight"` |
|
||||
| `backgroundImageOpacity` | Optional | Number | `1.0` | Sets the transparency of the background image. Accepts floating point values from 0-1. |
|
||||
| `backgroundImageStretchMode` | Optional | String | `uniformToFill` | Sets how the background image is resized to fill the window. Possible values: `"none"`, `"fill"`, `"uniform"`, `"uniformToFill"` |
|
||||
| `closeOnExit` | Optional | Boolean | `true` | When set to `true`, the selected tab closes when `exit` is typed. When set to `false`, the tab will remain open when `exit` is typed. |
|
||||
| `colorScheme` | Optional | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Array follows the format defined in `schemes`. |
|
||||
| `commandline` | Optional | String | | Executable used in the profile. |
|
||||
| `cursorColor` | Optional | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. |
|
||||
| `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. |
|
||||
| `cursorShape` | Optional | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) |
|
||||
| `fontFace` | Optional | 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` | Optional | Integer | `12` | Sets the font size. |
|
||||
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `#rgb` or `"#rrggbb"`. |
|
||||
| `hidden` | Optional | Boolean | `false` | If set to true, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamicially generated profiles, while leaving them in your settings file. |
|
||||
| `historySize` | Optional | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
|
||||
| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. |
|
||||
| `padding` | Optional | 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. |
|
||||
| `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` |
|
||||
| `selectionBackground` | Optional | String | Sets the selection background color of the profile. Overrides `selectionBackground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `snapOnInput` | Optional | 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. |
|
||||
| `source` | Optional | String | | Stores the name of the profile generator that originated this profile. _There are no discoverable values for this field._ |
|
||||
| `startingDirectory` | Optional | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
|
||||
| `suppressApplicationTitle` | Optional | Boolean | | When set to `true`, `tabTitle` overrides the default title of the tab and any title change messages from the application will be suppressed. When set to `false`, `tabTitle` behaves as normal. |
|
||||
| `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. |
|
||||
| `useAcrylic` | Optional | 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. |
|
||||
|
||||
## Schemes
|
||||
Properties listed below are specific to each color scheme. [ColorTool](https://github.com/microsoft/terminal/tree/master/src/tools/ColorTool) is a great tool you can use to create and explore new color schemes. All colors use hex color format.
|
||||
|
||||
| Property | Necessity | Type | Description |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `name` | _Required_ | String | Name of the color scheme. |
|
||||
| `foreground` | _Required_ | String | Sets the foreground color of the color scheme. |
|
||||
| `background` | _Required_ | String | Sets the background color of the color scheme. |
|
||||
| `selectionBackground` | Optional | String | Sets the selection background color of the color scheme. |
|
||||
| `black` | _Required_ | String | Sets the color used as ANSI black. |
|
||||
| `blue` | _Required_ | String | Sets the color used as ANSI blue. |
|
||||
| `brightBlack` | _Required_ | String | Sets the color used as ANSI bright black. |
|
||||
| `brightBlue` | _Required_ | String | Sets the color used as ANSI bright blue. |
|
||||
| `brightCyan` | _Required_ | String | Sets the color used as ANSI bright cyan. |
|
||||
| `brightGreen` | _Required_ | String | Sets the color used as ANSI bright green. |
|
||||
| `brightPurple` | _Required_ | String | Sets the color used as ANSI bright purple. |
|
||||
| `brightRed` | _Required_ | String | Sets the color used as ANSI bright red. |
|
||||
| `brightWhite` | _Required_ | String | Sets the color used as ANSI bright white. |
|
||||
| `brightYellow` | _Required_ | String | Sets the color used as ANSI bright yellow. |
|
||||
| `cyan` | _Required_ | String | Sets the color used as ANSI cyan. |
|
||||
| `green` | _Required_ | String | Sets the color used as ANSI green. |
|
||||
| `purple` | _Required_ | String | Sets the color used as ANSI purple. |
|
||||
| `red` | _Required_ | String | Sets the color used as ANSI red. |
|
||||
| `white` | _Required_ | String | Sets the color used as ANSI white. |
|
||||
| `yellow` | _Required_ | String | Sets the color used as ANSI yellow. |
|
||||
|
||||
## Keybindings
|
||||
Properties listed below are specific to each custom key binding.
|
||||
|
||||
| Property | Necessity | Type | Description |
|
||||
| -------- | ---- | ----------- | ----------- |
|
||||
| `command` | _Required_ | String | The command executed when the associated key bindings are pressed. |
|
||||
| `keys` | _Required_ | Array[String] | Defines the key combinations used to call the command. |
|
||||
|
||||
### Implemented Keybindings
|
||||
|
||||
Bindings listed below are per the implementation in `src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp`
|
||||
|
||||
- copy
|
||||
- copyTextWithoutNewlines
|
||||
- paste
|
||||
- newTab
|
||||
- openNewTabDropdown
|
||||
- duplicateTab
|
||||
- newTabProfile0
|
||||
- newTabProfile1
|
||||
- newTabProfile2
|
||||
- newTabProfile3
|
||||
- newTabProfile4
|
||||
- newTabProfile5
|
||||
- newTabProfile6
|
||||
- newTabProfile7
|
||||
- newTabProfile8
|
||||
- closeWindow
|
||||
- closeTab
|
||||
- closePane
|
||||
- switchToTab
|
||||
- nextTab
|
||||
- prevTab
|
||||
- increaseFontSize
|
||||
- decreaseFontSize
|
||||
- resetFontSize
|
||||
- scrollUp
|
||||
- scrollDown
|
||||
- scrollUpPage
|
||||
- scrollDownPage
|
||||
- switchToTab0
|
||||
- switchToTab1
|
||||
- switchToTab2
|
||||
- switchToTab3
|
||||
- switchToTab4
|
||||
- switchToTab5
|
||||
- switchToTab6
|
||||
- switchToTab7
|
||||
- switchToTab8
|
||||
- openSettings
|
||||
- splitHorizontal
|
||||
- splitVertical
|
||||
- resizePaneLeft
|
||||
- resizePaneRight
|
||||
- resizePaneUp
|
||||
- resizePaneDown
|
||||
- moveFocusLeft
|
||||
- moveFocusRight
|
||||
- moveFocusUp
|
||||
- moveFocusDown
|
||||
- toggleFullscreen
|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
|
||||
@@ -33,7 +33,7 @@ This spec will outline how various terminal frontends will be able to interact w
|
||||
5. Visual Studio should be able to persist and edit settings globally, without
|
||||
the need for a globals/profiles structure.
|
||||
6. The Terminal should be able to read information from a settings structure
|
||||
that's independant of how it's persisted / implemented by the Application
|
||||
that's independent of how it's persisted / implemented by the Application
|
||||
7. The Component should be able to have its own settings independent of the
|
||||
application that's embedding it, such as font size and face, scrollbar
|
||||
visibility, etc. These should be settings that are specific to the component,
|
||||
@@ -79,7 +79,7 @@ Shell Commandline |
|
||||
|
||||
### Simple Settings
|
||||
|
||||
An application like VS might not even care about settings profiles. They should be able to persist the settings as just a singular entity, and change those as needed, without the additional overhead. Profiles will be something that's more specifc to Project Cascadia.
|
||||
An application like VS might not even care about settings profiles. They should be able to persist the settings as just a singular entity, and change those as needed, without the additional overhead. Profiles will be something that's more specific to Project Cascadia.
|
||||
|
||||
### Interface Descriptions
|
||||
|
||||
@@ -228,6 +228,6 @@ I don't like that - if we change the font size, we should just recalculate how m
|
||||
## Questions / TODO
|
||||
* How does this interplay with setting properties of the terminal component in XAML?
|
||||
* I would think that the component would load the XAML properties first, and if the controlling application calls `UpdateSettings` on the component, then those in-XAML properties would likely get overwritten.
|
||||
* It's not necessary to create the component with a `IComponentSettings`, nor is it necessary to call `UpdateSettings`. If you wanted to create a trivial settings-less terminal component entriely in XAML, go right ahead.
|
||||
* It's not necessary to create the component with a `IComponentSettings`, nor is it necessary to call `UpdateSettings`. If you wanted to create a trivial settings-less terminal component entirely in XAML, go right ahead.
|
||||
* Any settings that *are* exposed through XAML properties *should* also be exposed in the component's settings implementation as well.
|
||||
* Can that be enforced any way? I doubt it.
|
||||
|
||||
@@ -82,7 +82,7 @@ project from our `TerminalAppLib` project:
|
||||
<ItemGroup>
|
||||
<!-- Manually add references to each of our dependent winmds. Mark them as
|
||||
private=false and CopyLocalSatelliteAssemblies=false, so that we don't
|
||||
propogate them upwards (which can make referencing this project result in
|
||||
propagate them upwards (which can make referencing this project result in
|
||||
duplicate type definitions)-->
|
||||
|
||||
<Reference Include="Microsoft.Terminal.Settings">
|
||||
@@ -106,7 +106,7 @@ in the dll project's directory.
|
||||
|
||||
### Update the dll project
|
||||
|
||||
Now that we havea lib that builds all your code, we can go ahead and tear out
|
||||
Now that we have a lib that builds all your code, we can go ahead and tear out
|
||||
most of the dead code from the old dll project. Remove all the source files from
|
||||
the dll's `.vcxproj` file, save for the `pch.h` and `pch.cpp` files. You _may_
|
||||
need to leave the headers for any C++/WinRT types you've authored in this project
|
||||
@@ -301,7 +301,7 @@ you want to use any XAML types, then you'll have to keep reading.
|
||||
|
||||
### Using Xaml Types (with XAML Islands)
|
||||
|
||||
To be able to instatiate XAML types in your unittest, we'll need to make use of
|
||||
To be able to instantiate XAML types in your unittest, we'll need to make use of
|
||||
the [XAML Hosting
|
||||
API](https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/using-the-xaml-hosting-api)
|
||||
(Xaml Islands). This enables you to use XAML APIs from a Win32 context.
|
||||
|
||||
670
doc/cascadia/profiles.schema.json
Normal file
@@ -0,0 +1,670 @@
|
||||
{
|
||||
"$id": "https://github.com/microsoft/terminal/blob/master/doc/cascadia/profiles.schema.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema'",
|
||||
"definitions": {
|
||||
"Color": {
|
||||
"default": "#",
|
||||
"pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
|
||||
"type": "string",
|
||||
"format": "color"
|
||||
},
|
||||
"ProfileGuid": {
|
||||
"default": "{}",
|
||||
"pattern": "^\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}$",
|
||||
"type": "string"
|
||||
},
|
||||
"ShortcutActionName": {
|
||||
"enum": [
|
||||
"closePane",
|
||||
"closeTab",
|
||||
"closeWindow",
|
||||
"copy",
|
||||
"copyTextWithoutNewlines",
|
||||
"decreaseFontSize",
|
||||
"duplicateTab",
|
||||
"increaseFontSize",
|
||||
"moveFocus",
|
||||
"moveFocusDown",
|
||||
"moveFocusLeft",
|
||||
"moveFocusRight",
|
||||
"moveFocusUp",
|
||||
"newTab",
|
||||
"newTabProfile0",
|
||||
"newTabProfile1",
|
||||
"newTabProfile2",
|
||||
"newTabProfile3",
|
||||
"newTabProfile4",
|
||||
"newTabProfile5",
|
||||
"newTabProfile6",
|
||||
"newTabProfile7",
|
||||
"newTabProfile8",
|
||||
"nextTab",
|
||||
"openNewTabDropdown",
|
||||
"openSettings",
|
||||
"paste",
|
||||
"prevTab",
|
||||
"resetFontSize",
|
||||
"resizePane",
|
||||
"resizePaneDown",
|
||||
"resizePaneLeft",
|
||||
"resizePaneRight",
|
||||
"resizePaneUp",
|
||||
"scrollDown",
|
||||
"scrollDownPage",
|
||||
"scrollUp",
|
||||
"scrollUpPage",
|
||||
"splitHorizontal",
|
||||
"splitVertical",
|
||||
"switchToTab",
|
||||
"switchToTab0",
|
||||
"switchToTab1",
|
||||
"switchToTab2",
|
||||
"switchToTab3",
|
||||
"switchToTab4",
|
||||
"switchToTab5",
|
||||
"switchToTab6",
|
||||
"switchToTab7",
|
||||
"switchToTab8",
|
||||
"toggleFullscreen"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"Direction": {
|
||||
"enum": [
|
||||
"left",
|
||||
"right",
|
||||
"up",
|
||||
"down"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ShortcutAction": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "The action to execute",
|
||||
"$ref": "#/definitions/ShortcutActionName"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"action"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"CopyAction": {
|
||||
"description": "Arguments corresponding to a Copy Text Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "copy" },
|
||||
"trimWhitespace": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "If true, whitespace is removed and newlines are maintained. If false, newlines are removed and whitespace is maintained."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"NewTabAction": {
|
||||
"description": "Arguments corresponding to a New Tab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type":"string", "pattern": "newTab" },
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"description": "The index in the new tab dropdown to open in a new tab"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SwitchToTabAction": {
|
||||
"description": "Arguments corresponding to a Switch To Tab Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "switchToTab" },
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"default": 0,
|
||||
"description": "Which tab to switch to, with the first being 0"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "index" ]
|
||||
},
|
||||
"MoveFocusAction": {
|
||||
"description": "Arguments corresponding to a Move Focus Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "moveFocus" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/Direction",
|
||||
"default": "left",
|
||||
"description": "The direction to move focus in, between panes"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"ResizePaneAction": {
|
||||
"description": "Arguments corresponding to a Resize Pane Action",
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/ShortcutAction" },
|
||||
{
|
||||
"properties": {
|
||||
"action": { "type": "string", "pattern": "resizePane" },
|
||||
"direction": {
|
||||
"$ref": "#/definitions/Direction",
|
||||
"default": "left",
|
||||
"description": "The direction to move the pane separator in"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [ "direction" ]
|
||||
},
|
||||
"Keybinding": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"command": {
|
||||
"description": "The action executed when the associated key bindings are pressed.",
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/CopyAction" },
|
||||
{ "$ref": "#/definitions/ShortcutActionName" },
|
||||
{ "$ref": "#/definitions/NewTabAction" },
|
||||
{ "$ref": "#/definitions/SwitchToTabAction" },
|
||||
{ "$ref": "#/definitions/MoveFocusAction" },
|
||||
{ "$ref": "#/definitions/ResizePaneAction" }
|
||||
]
|
||||
},
|
||||
"keys": {
|
||||
"description": "Defines the key combinations used to call the command.",
|
||||
"items": {
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)\\+?((ctrl|alt|shift)(?<!\\2)\\+?)?((ctrl|alt|shift)(?<!\\2|\\4))?\\+?)?(?<key>[^+\\s]+?)?(?<=[^+\\s])$",
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"command",
|
||||
"keys"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Globals": {
|
||||
"additionalProperties": true,
|
||||
"description": "Properties that affect the entire window, regardless of the profile settings.",
|
||||
"properties": {
|
||||
"alwaysShowTabs": {
|
||||
"default": true,
|
||||
"description": "When set to true, tabs are always displayed. When set to false and showTabsInTitlebar is set to false, tabs only appear after opening a new tab.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"copyOnSelect": {
|
||||
"default": false,
|
||||
"description": "When set to true, a selection is immediately copied to your clipboard upon creation. When set to false, the selection persists and awaits further action.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"defaultProfile": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "Sets the default profile. Opens by clicking the '+' icon or typing the key binding assigned to 'newTab'. The guid of the desired default profile is used as the value."
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
"description": "The number of columns displayed in the window upon first load.",
|
||||
"maximum": 999,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"initialRows": {
|
||||
"default": 30,
|
||||
"description": "The number of rows displayed in the window upon first load.",
|
||||
"maximum": 999,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"keybindings": {
|
||||
"description": "Properties are specific to each custom key binding.",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Keybinding"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"requestedTheme": {
|
||||
"default": "system",
|
||||
"description": "Sets the theme of the application.",
|
||||
"enum": [
|
||||
"light",
|
||||
"dark",
|
||||
"system"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"showTabsInTitlebar": {
|
||||
"default": true,
|
||||
"description": "When set to true, the tabs are moved into the titlebar and the titlebar disappears. When set to false, the titlebar sits above the tabs.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"showTerminalTitleInTitlebar": {
|
||||
"default": true,
|
||||
"description": "When set to true, titlebar displays the title of the selected tab. When set to false, titlebar displays 'Windows Terminal'.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"wordDelimiters": {
|
||||
"default": " ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?│",
|
||||
"description": "Determines the delimiters used in a double click selection.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"defaultProfile"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ProfileList": {
|
||||
"description": "Properties are specific to each unique profile.",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"acrylicOpacity": {
|
||||
"default": 0.5,
|
||||
"description": "When useAcrylic is set to true, it sets the transparency of the window for the profile. Accepts floating point values from 0-1 (default 0.5).",
|
||||
"maximum": 1,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the background color of the profile. Overrides background set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\". Default \"#000000\" (black).",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"backgroundImage": {
|
||||
"description": "Sets the file location of the Image to draw over the window background.",
|
||||
"type": "string"
|
||||
},
|
||||
"backgroundImageAlignment": {
|
||||
"default": "center",
|
||||
"enum": [
|
||||
"bottom",
|
||||
"bottomLeft",
|
||||
"bottomRight",
|
||||
"center",
|
||||
"left",
|
||||
"right",
|
||||
"top",
|
||||
"topLeft",
|
||||
"topRight"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"backgroundImageOpacity": {
|
||||
"description": "(Not in SettingsSchema.md)",
|
||||
"maximum": 1,
|
||||
"minimum": 0,
|
||||
"type": "number"
|
||||
},
|
||||
"backgroundImageStretchMode": {
|
||||
"default": "uniformToFill",
|
||||
"description": "Sets how the background image is resized to fill the window.",
|
||||
"enum": [
|
||||
"fill",
|
||||
"none",
|
||||
"uniform",
|
||||
"uniformToFill"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"closeOnExit": {
|
||||
"default": true,
|
||||
"description": "When set to true (default), the selected tab closes when the connected application exits. When set to false, the tab will remain open when the connected application exits.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"colorScheme": {
|
||||
"default": "Campbell",
|
||||
"description": "Name of the terminal color scheme to use. Color schemes are defined under \"schemes\".",
|
||||
"type": "string"
|
||||
},
|
||||
"colorTable": {
|
||||
"description": "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]",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the background color of the color table."
|
||||
},
|
||||
"black": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI black."
|
||||
},
|
||||
"blue": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI blue."
|
||||
},
|
||||
"brightBlack": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright black."
|
||||
},
|
||||
"brightBlue": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright blue."
|
||||
},
|
||||
"brightCyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright cyan."
|
||||
},
|
||||
"brightGreen": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright green."
|
||||
},
|
||||
"brightPurple": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright purple."
|
||||
},
|
||||
"brightRed": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright red."
|
||||
},
|
||||
"brightWhite": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright white."
|
||||
},
|
||||
"brightYellow": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright yellow."
|
||||
},
|
||||
"cyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI cyan."
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the foreground color of the color table."
|
||||
},
|
||||
"green": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI green."
|
||||
},
|
||||
"purple": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI purple."
|
||||
},
|
||||
"red": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI red."
|
||||
},
|
||||
"white": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI white."
|
||||
},
|
||||
"yellow": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI yellow."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"commandline": {
|
||||
"description": "Executable used in the profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"connectionType": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "A GUID reference to a connection type. Currently undocumented as of 0.3, this is used for Azure Cloud Shell"
|
||||
},
|
||||
"cursorColor": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"default": "#FFFFFF",
|
||||
"description": "Sets the cursor color for the profile. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"cursorHeight": {
|
||||
"description": "Sets the percentage height of the cursor starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 25-100.",
|
||||
"maximum": 100,
|
||||
"minimum": 25,
|
||||
"type": "integer"
|
||||
},
|
||||
"cursorShape": {
|
||||
"default": "bar",
|
||||
"description": "Sets the cursor shape for the profile. Possible values: \"vintage\" ( ▃ ), \"bar\" ( ┃, default ), \"underscore\" ( ▁ ), \"filledBox\" ( █ ), \"emptyBox\" ( ▯ )",
|
||||
"enum": [
|
||||
"bar",
|
||||
"emptyBox",
|
||||
"filledBox",
|
||||
"underscore",
|
||||
"vintage"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"fontFace": {
|
||||
"default": "Consolas",
|
||||
"description": "Name of the font face used in the profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"fontSize": {
|
||||
"default": 12,
|
||||
"description": "Sets the font size.",
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the foreground color of the profile. Overrides foreground set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\". Default \"#ffffff\" (white).",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"guid": {
|
||||
"$ref": "#/definitions/ProfileGuid",
|
||||
"description": "Unique identifier of the profile. Written in registry format: \"{00000000-0000-0000-0000-000000000000}\"."
|
||||
},
|
||||
"hidden": {
|
||||
"default": false,
|
||||
"description": "If set to true, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamicially generated profiles, while leaving them in your settings file.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"historySize": {
|
||||
"default": 9001,
|
||||
"description": "The number of lines above the ones displayed in the window you can scroll back to.",
|
||||
"minimum": -1,
|
||||
"type": "integer"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Image file location of the icon used in the profile. Displays within the tab and the dropdown menu.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the profile. Displays in the dropdown menu.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"padding": {
|
||||
"default": "8, 8, 8, 8",
|
||||
"description": "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.",
|
||||
"pattern": "^-?[0-9]+(\\.[0-9]+)?( *, *-?[0-9]+(\\.[0-9]+)?|( *, *-?[0-9]+(\\.[0-9]+)?){3})?$",
|
||||
"type": "string"
|
||||
},
|
||||
"scrollbarState": {
|
||||
"default": "visible",
|
||||
"description": "Defines the visibility of the scrollbar.",
|
||||
"enum": [
|
||||
"visible",
|
||||
"hidden"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"selectionBackground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the selection background color of the profile. Overrides selection background set in color scheme if colorscheme is set. Uses hex color format: \"#rrggbb\"."
|
||||
},
|
||||
"snapOnInput": {
|
||||
"default": true,
|
||||
"description": "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.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"source": {
|
||||
"description": "Stores the name of the profile generator that originated this profile.",
|
||||
"type": "string"
|
||||
},
|
||||
"startingDirectory": {
|
||||
"description": "The directory the shell starts in when it is loaded.",
|
||||
"type": "string"
|
||||
},
|
||||
"suppressApplicationTitle": {
|
||||
"description": "When set to `true`, `tabTitle` overrides the default title of the tab and any title change messages from the application will be suppressed. When set to `false`, `tabTitle` behaves as normal.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"tabTitle": {
|
||||
"description": "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.",
|
||||
"type": "string"
|
||||
},
|
||||
"useAcrylic": {
|
||||
"default": false,
|
||||
"description": "When set to true, the window will have an acrylic background. When set to false, the window will have a plain, untextured background.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"guid",
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"SchemeList": {
|
||||
"description": "Properties are specific to each color scheme. ColorTool is a great tool you can use to create and explore new color schemes. All colors use hex color format.",
|
||||
"items": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name of the color scheme.",
|
||||
"minLength": 1,
|
||||
"type": "string"
|
||||
},
|
||||
"background": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the background color of the color scheme."
|
||||
},
|
||||
"black": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI black."
|
||||
},
|
||||
"blue": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI blue."
|
||||
},
|
||||
"brightBlack": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright black."
|
||||
},
|
||||
"brightBlue": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright blue."
|
||||
},
|
||||
"brightCyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright cyan."
|
||||
},
|
||||
"brightGreen": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright green."
|
||||
},
|
||||
"brightPurple": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright purple."
|
||||
},
|
||||
"brightRed": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright red."
|
||||
},
|
||||
"brightWhite": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright white."
|
||||
},
|
||||
"brightYellow": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI bright yellow."
|
||||
},
|
||||
"cyan": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI cyan."
|
||||
},
|
||||
"foreground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the foreground color of the color scheme."
|
||||
},
|
||||
"green": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI green."
|
||||
},
|
||||
"purple": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI purple."
|
||||
},
|
||||
"red": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI red."
|
||||
},
|
||||
"selectionBackground": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the selection background color of the color scheme."
|
||||
},
|
||||
"white": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI white."
|
||||
},
|
||||
"yellow": {
|
||||
"$ref": "#/definitions/Color",
|
||||
"description": "Sets the color used as ANSI yellow."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"allOf": [
|
||||
{ "$ref": "#/definitions/Globals" },
|
||||
{
|
||||
"additionalItems": true,
|
||||
"properties": {
|
||||
"profiles": { "$ref": "#/definitions/ProfileList" },
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
},
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"defaultProfile"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"additionalItems": false,
|
||||
"properties": {
|
||||
"globals": { "$ref": "#/definitions/Globals" },
|
||||
"profiles": { "$ref": "#/definitions/ProfileList" },
|
||||
"schemes": { "$ref": "#/definitions/SchemeList" }
|
||||
},
|
||||
"required": [
|
||||
"profiles",
|
||||
"schemes",
|
||||
"globals"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
doc/images/panes.png
Normal file
|
After Width: | Height: | Size: 300 KiB |
BIN
doc/images/solution-platform.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
doc/images/terminal-0.6.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
doc/images/terminal-mockup.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
@@ -0,0 +1,107 @@
|
||||
---
|
||||
author: Dustin Howett @DHowett-MSFT
|
||||
created on: 2019-07-19
|
||||
last updated: 2019-11-05
|
||||
issue id: "#2563"
|
||||
---
|
||||
|
||||
# Improvements to CloseOnExit
|
||||
|
||||
## Abstract
|
||||
|
||||
This specification describes an improvement to the `closeOnExit` profile feature and the `ITerminalConnection` interface that will offer greater flexibility and allow us to provide saner defaults in the face of unreliable software.
|
||||
|
||||
### Conventions and Terminology
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
|
||||
|
||||
## Inspiration
|
||||
|
||||
Other terminal emulators like ConEmu have a similar feature.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### `ITerminalConnection` Changes
|
||||
|
||||
* The `TerminalConnection` interface will be augmented with an enumerator and a set of events regarding connection state transitions.
|
||||
* enum `TerminalConnection::ConnectionState`
|
||||
* This enum attempts to encompass all potential connection states, even those which do not make sense for a local terminal.
|
||||
* The wide variety of values will be useful to indicate state changes in a user interface.
|
||||
* `NotConnected`: All new connections will start out in this state
|
||||
* `Connecting`: The connection has been initated, but has not yet completed connecting.
|
||||
* `Connected`: The connection is active.
|
||||
* `Closing`: The connection is being closed (usually by request).
|
||||
* `Closed`: The connection has been closed, either by request or from the remote end terminating successfully.
|
||||
* `Failed`: The connection was unexpectedly terminated.
|
||||
* event `StateChanged(ITerminalConnection, IInspectable)`
|
||||
* (the `IInspectable` argument is recommended and required for a typed event handler, but it will bear no payload.)
|
||||
* event `TerminalDisconnected` will be removed, as it is replaced by `StateChanged`
|
||||
* **NOTE**: A conforming implementation MUST treat states as a directed acyclic graph. States MUST NOT be transitioned in reverse.
|
||||
* A helper class may be provided for managing state transitions.
|
||||
|
||||
### `TerminalControl` Changes
|
||||
|
||||
* As the decision as to whether to close a terminal control hosting a connection that has transitioned into a terminal state will be made by the application, the unexpressive `Close` event will be removed and replaced with a `ConnectionStateChanged` event.
|
||||
* `event ConnectionStateChanged(TerminalControl, IInspectable)` event will project its connection's `StateChanged` event.
|
||||
* TerminalControl's new `ConnectionState` will project its connection's `State`.
|
||||
* (this is indicated for an eventual data binding; see Future Considerations.)
|
||||
|
||||
### Application and Settings
|
||||
|
||||
1. The existing `closeOnExit` profile key will be replaced with an enumerated string key supporting the following values (behaviours):
|
||||
* `always` - a tab or pane hosting this profile will always be closed when the launched connection reaches a terminal state.
|
||||
* `graceful` - a tab or pane hosting this profile will be closed if and only if the launched connection reaches the `Closed` terminal state.
|
||||
* `never` - a tab or pane hosting this profile will not automatically close.
|
||||
* See the Compatibility section for information on the legacy settings transition.
|
||||
* **The new default value for `closeOnExit` will be `graceful`.**
|
||||
2. `Pane` will remain responsible for making the final determination as to whether it is closed based on the settings of the profile it is hosting.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
* The existing `ITerminalConnection` implementations will be augmented to print out interesting and useful status information when they transition into a `Closed` or `Failed` state.
|
||||
* Example (ConPTY connection)
|
||||
* The pseudoconsole cannot be opened, or the process fails to launch.<br>`[failed to spawn 'thing': 0x80070002]`, transition to `Failed`.
|
||||
* The process exited unexpectedly.<br>`[process exited with code 300]`, transition to `Failed`.
|
||||
* The process exited normally.<br>`[process exited with code 0]`, transition to `Closed`.
|
||||
* _The final message will always be printed_ regardless of user configuration.
|
||||
* If the user's settings specify `closeOnExit: never/false`, the terminal hosting the connection will never be automatically closed. The message will remain on-screen.
|
||||
* If the user's settings specify `closeOnExit: graceful/true`, the terminal hosting the connection _will_ automatically be closed if the connection's state is `Closed`. A connection in the `Failed` state will not be closed, and the message will remain on-screen.
|
||||
* If the user's settings specify `closeOnExit: always`, the terminal hosting the connection will be closed. The message will not be seen.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This will give users of all technologies a way to know when their shell has failed to launch or has exited with an unexpected status code.
|
||||
|
||||
### Security
|
||||
|
||||
There will be no impact to security.
|
||||
|
||||
### Reliability
|
||||
|
||||
Windows Terminal will no longer immediately terminate on startup if the user's shell doesn't exist.
|
||||
|
||||
### Compatibility
|
||||
|
||||
There is an existing `closeOnExit` _boolean_ key that a user may have configured in profiles.json. The boolean values should map as follows:
|
||||
|
||||
* `true` -> `graceful`
|
||||
* `false` -> `never`
|
||||
|
||||
This will make for a clean transition to Windows Terminal's sane new defaults.
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
There will be no impact to Performance, Power or Efficiency.
|
||||
|
||||
## Future considerations
|
||||
|
||||
* Eventually, we may want to implement a feature like "only close on graceful exit if the shell was running for more than X seconds". This puts us in a better position to do that, as we can detect graceful and clumsy exits more readily.
|
||||
* (potential suggestion: `{ "closeOnExit": "10s" }`
|
||||
* The enumerator values for transitioning connection states will be useful for connections that require internet access.
|
||||
* Since the connection states are exposed through `TerminalControl`, they should be able to be data-bound to other Xaml elements. This can be used to provide discrete UI states for terminal controls, panes or tabs _hosting_ terminal controls.
|
||||
* Example: a tab hosting a terminal control whose connection has been broken MAY display a red border.
|
||||
* Example: an inactive tab that reaches the `Connected` state MAY flash to indicate that it is ready.
|
||||
@@ -26,7 +26,7 @@ Windows Terminal.
|
||||
Panes within the context of a single terminal window are not a new idea. The
|
||||
design of the panes for the Windows Terminal was heavily inspired by the
|
||||
application `tmux`, which is a commandline application which acts as a "terminal
|
||||
multiplexer", allowing for the easy managment of many terminal sessions from a
|
||||
multiplexer", allowing for the easy management of many terminal sessions from a
|
||||
single application.
|
||||
|
||||
Other applications that include pane-like functionality include (but are not
|
||||
@@ -115,7 +115,7 @@ We could also split `A` in horizontally, creating a fourth terminal pane `D`.
|
||||
+---------------+
|
||||
```
|
||||
|
||||
While it may appear that there's a single horizonal separator and a single
|
||||
While it may appear that there's a single horizontal separator and a single
|
||||
vertical separator here, that's not actually the case. Due to the tree-like
|
||||
structure of the pane splitting, the horizontal splits exist only between the
|
||||
two panes they're splitting. So, the user could move each of the horizontal
|
||||
@@ -230,5 +230,5 @@ for swapping the positions of tabs, or a shortcut for both "zooming" a tab
|
||||
tab. Additionally, a right-click menu option could be added to do the
|
||||
aformentioned actions. Discoverability of these two actions is not as high as
|
||||
just dragging a tab from one pane to another; however, it's believed that panes
|
||||
are more of a power-user scenario, and power users will not neccessarily be
|
||||
are more of a power-user scenario, and power users will not necessarily be
|
||||
turned off by the feature's discoverability.
|
||||
|
||||
@@ -36,7 +36,7 @@ Largely inspired by the settings model that both VS Code (and Sublime Text) use.
|
||||
### Goal: Minimize Re-Serializing `profiles.json`
|
||||
|
||||
We want to re-serialize the user settings file, `profiles.json`, as little as
|
||||
possible. Each time we serialize the file, there's the possiblity that we've
|
||||
possible. Each time we serialize the file, there's the possibility that we've
|
||||
re-ordered the keys, as `jsoncpp` provides no ordering guarantee of the keys.
|
||||
This isn't great, as each write of the file will randomly re-order the file.
|
||||
|
||||
@@ -241,7 +241,7 @@ Currently, these profiles are only generated when a user first launches the
|
||||
Terminal. If they already have a `profiles.json` file, then we won't run the
|
||||
auto-generation behavior. This is obviously not great - if any new types of
|
||||
dynamic profiles are added, then users that already have the Terminal installed
|
||||
won't get any of these dynamic profiles. Furthemore, if any of the sources of
|
||||
won't get any of these dynamic profiles. Furthermore, if any of the sources of
|
||||
these dynamic profiles are removed, then the app won't auto-remove the
|
||||
associated profile.
|
||||
|
||||
@@ -695,7 +695,7 @@ generators _must_ be enabled to use the dynamic profiles.
|
||||
feature spec.
|
||||
- We'll also want to make sure that when we're serializing default/dynamic
|
||||
profiles, we take into account the state from the global defaults, and we
|
||||
don't duplicate that inormation into the entries for those types of profiles
|
||||
don't duplicate that information into the entries for those types of profiles
|
||||
in the user profiles.
|
||||
* **Re-ordering profiles** - Under "Solution Design", we provide an algorithm
|
||||
for decoding the settings. One of the steps mentioned is parsing the user
|
||||
|
||||
@@ -84,7 +84,7 @@ Key | Sequence
|
||||
|
||||
With <kbd>Num Lock</kbd> disabled, most of the keys on the numeric keypad function the same as cursor keys or editing keys, but with the addition of a center <kbd>5</kbd> key. As a described above, the cursor keys generate a simple ESC prefix instead of CSI or SS3, while the editing keys remain unchanged (with the exception of modifiers).
|
||||
|
||||
In V52 mode, most modifiers are ignored, except for <kbd>Shift</kbd>, which is the equivalent of enabling <kbd>Num Lock</kbd> (i.e. the keys just generate their corresponding digit characters or `.`). With <kbd>Num Lock</kbd> enabled, it's the other way arround - the digits are generated by default, while <kbd>Shift</kbd> enables the cursor/editing functionality.
|
||||
In V52 mode, most modifiers are ignored, except for <kbd>Shift</kbd>, which is the equivalent of enabling <kbd>Num Lock</kbd> (i.e. the keys just generate their corresponding digit characters or `.`). With <kbd>Num Lock</kbd> enabled, it's the other way around - the digits are generated by default, while <kbd>Shift</kbd> enables the cursor/editing functionality.
|
||||
|
||||
Key | Alias | ANSI mode | VT52 mode
|
||||
-------------|-------|-----------|-----------
|
||||
|
||||
95
doc/terminal-v1-roadmap.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Terminal v1.0 Roadmap
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines our roadmap to delivering Windows Terminal v1.0 by spring 2020.
|
||||
|
||||
## Milestones
|
||||
|
||||
The Windows Terminal project is engineered and delivered as a set of 4-week milestones:
|
||||
|
||||
| Duration | Activity | Releases |
|
||||
| --- | --- | --- |
|
||||
| 2 weeks | Dev Work<br/> <ul><li>Fixes / Features for future Windows Releases</li><li>Fixes / Features for Windows Terminal</li></ul> | Release to Internal Selfhosters at end of week 2 |
|
||||
| 1 week | Quality & Stability<br/> <ul><li>Bug Fixes</li><li>Perf & Stability</li><li>UI Polish</li><li>Tests</li><li>etc.</li></ul>| Push to Microsoft Store at end of week 3 |
|
||||
| 1 week | Release <br/> <ul><li>Available from [Microsoft Store](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701) & [GitHub Releases](https://github.com/microsoft/terminal/releases) (Tues of 4th week)</li><li>Release Notes & Announcement Blog published</li><li>Engineering System Maintenance</li><li>Community Engagement</li><li>Docs</li><li>Future Milestone Planning</li></ul> | Release available from Microsoft Store & GitHub Releases |
|
||||
|
||||
## Terminal Roadmap / Timeline
|
||||
|
||||
Ultimately, we're aiming for Terminal v1.0 to be feature-complete by Dec 2019, and to declare v1.0 by April 2020:
|
||||
|
||||
> ⚠ Note: Terminal v1.0 will be a quality-oriented release driven in large part by the community. So, ___if you see bugs, find/file them___!
|
||||
|
||||
| Milestone end date | Milestone Name | Key Deliverables |
|
||||
| --- | --- | --- |
|
||||
| 2019-05-07 | [Announcement](https://devblogs.microsoft.com/commandline/introducing-windows-terminal/) | Terminal announced & open-sourced ([Build 2019 Terminal session](https://www.youtube.com/watch?v=KMudkRcwjCw), ["Sizzle" video](https://www.youtube.com/watch?v=8gw0rXPMMPE&list=PLEHMQNlPj-Jzh9DkNpqipDGCZZuOwrQwR&index=2&t=0s)) |
|
||||
| 2019-07-09 | [v0.2 (update)](https://github.com/microsoft/terminal/releases/tag/v0.2.1831.0) | First version of the Terminal released via the Microsoft Store, fundamental features in place, basic tab control, basic UI layout, config & settings via JSON file |
|
||||
| 2019-08-02 | [v0.3](https://github.com/microsoft/terminal/releases/tag/v0.3.2142.0) | Major UI improvements, improved tab bar layout & color, basic a11y support, Azure Cloud Shell connection |
|
||||
| 2019-08-27 | [v0.4](https://github.com/microsoft/terminal/releases/tag/v0.4.2382.0) | HTML Copy, Tab Titles, Double/Triple Click Selection, Local Settings, JSON settings validation, A11y improvements |
|
||||
| 2019-09-24 | [1909]( http://devblogs.microsoft.com/commandline/windows-terminal-preview-1909) | Stability & Quality improvements, installs [Cascadia Code](https://github.com/microsoft/cascadia-code) font, adds JSON schema to `profiles.json` settings file enabling Intellisense in VSCode, etc. |
|
||||
| 2019-10-22 | [1910](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1910-release/) | Cascading Settings, Dynamic Profiles |
|
||||
| 2019-11-19 | 1911 | Final v1.0 feature work |
|
||||
| 2019-12-17 | 1912 | "Feature Complete" - All v1.0 Features in-place |
|
||||
| Winter Vacation | N/A | None planned |
|
||||
| 2020-01-28 | Beta 1 | Pri 0/1/2 Bug fixes & polish |
|
||||
| 2020-02-25 | Beta 2 | Pri 0/1 Bug fixes & polish |
|
||||
| 2020-03-24 | RC | Pri 0 bug fixes |
|
||||
| 2020-04-01 ? | v1.0 | Terminal v1.0 Release |
|
||||
|
||||
## GitHub Milestones
|
||||
|
||||
Each milestone above is/will be reflected in our [GitHub milestones](https://github.com/microsoft/terminal/milestones):
|
||||
|
||||
| Milestone | Description |
|
||||
| --- | --- |
|
||||
| [Terminal-1909](https://github.com/microsoft/terminal/milestone/12) | Work planned for 1909 |
|
||||
| [Terminal-1910](https://github.com/microsoft/terminal/milestone/15) | Work planned for 1910 |
|
||||
| [Terminal-1911](https://github.com/microsoft/terminal/milestone/16) | Work planned for 1911 |
|
||||
| [Terminal-1912](https://github.com/microsoft/terminal/milestone/17) | Work planned for 1912 |
|
||||
| <Future Milestones> | <Coming soon> |
|
||||
| [Terminal v1.0](https://github.com/microsoft/terminal/milestone/6) | Work planned for v1.0, but not yet assigned to a milestone |
|
||||
| [Terminal Backlog](https://github.com/microsoft/terminal/milestone/7) | Work not yet assigned to a milestone or release |
|
||||
|
||||
## Issue Triage & Prioritization
|
||||
|
||||
Incoming issues/asks/etc. are triaged several times a week, labelled appropriately, and assigned to a milestone in priority order:
|
||||
|
||||
* P0 (serious crashes, data loss, etc.) issues are scheduled to be dealt with ASAP
|
||||
* P1/2 issues/features/asks assigned to the current or future milestone, or to the [Terminal v1.0 milestone](https://github.com/microsoft/terminal/milestone/6) for future assignment, if required to deliver a v1.0 feature
|
||||
* Issues/features/asks not on our list of v1.0 features is assigned to the [Terminal Backlog](https://github.com/microsoft/terminal/milestone/7) for subsequent triage, prioritization & scheduling.
|
||||
|
||||
## v1.0 Scenarios
|
||||
|
||||
The following are a list of the key scenarios we're aiming to deliver for Terminal v1.0.
|
||||
|
||||
> 👉 Note: There are many other features that don't fit within v1.0, but will be re-assessed and prioritized for v2.0, the plan for which will be published in early in 2020.
|
||||
|
||||
| Release | Priority\* | Scenario | Description/Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| V1 | 0 | Performance & Efficiency | Terminal shall be fast and efficient. Input latency should be eliminated wherever possible. Terminal will be very memory-efficient, and will avoid utilizing unnecessary dependencies to minimize memory consumption and disk footprint |
|
||||
| V1 | 0 | Reliability | Every reasonable step should be taken to ensure that Terminal will not crash unexpectedly. Crashing is considered harmful to the user's well-being & state of mind. Crashing issues are prioritized Pri-0 by default |
|
||||
| V1 | 0 | Code Reuse | Terminal's core engine will reuse & share componentry from within Windows Console wherever feasible to minimize support & maintenance costs for both|
|
||||
| V1 | 0 | Terminal Reuse | Terminal's core will be hostable as a UWP (and perhaps WPF) Control so that apps can host/embed a high quality Terminal. This will satisfy a long-standing ask from many customers and partners for a hostable/embeddable Terminal Control. |
|
||||
| V1 | 0 | Rich, modern text renderer | Terminal must be able to render glyphs from East Asian and Middle Asian languages, inc. Chinese, Hebrew, Arabic, etc. Terminal will also be able to render Emoji - an increasingly important feature considering that several programming languages now support Emoji in method and variable names! To render such glyphs, the Terminal needs a DirectWrite-based layout & rendering system which supports font fallback, customizable text layout, GPU accelerated rendering, and many other features not currently supported by the built-in Windows Console |
|
||||
| V1 | 0 | Solid Unicode & UTF-8 support | Terminal must be able to store data encoded as Unicode UTF-16/UCS-2 and UTF-8, including surrogate pairs. Note: Terminal v1.0 won't be able to support composing characters or grapheme clusters that are not representable with a single unicode codepoint - this will be addressed in a subsequent release |
|
||||
| V1 | 0 | International text rendering | The Terminal will support rendering text for almost every language for which there is a fixed-width font including East Asian languages. Bonus points for RTL languages/scripts. |
|
||||
| V1 | 0 | Multiple instances | Users must be able to launch multiple independent instances of the Terminal in order to run tools side-by-side / independently |
|
||||
| V1 | 0 | Elevation | Terminal can be launched "elevated" with Admin rights if required so that the user can perform operations that affect machine-wide state |
|
||||
| V1 | 0 | Multiple Tabs per instance | Each Terminal instance must support one or more independent tabs. This is the #1 ask from the community! |
|
||||
| V1 | 0 | Configurability & Customization | The new Terminal will have a modern, flexible settings mechanism that persists settings to/from a JSON file stored in the user's app data folders, and/or in files synchronized between machines via OneDrive, etc. There will be no settings UI in Terminal v1 - this is a feature for a future Terminal release. |
|
||||
| V1 | 0 | Accessibility (A11y) | The Terminal will be highly accessible and inclusive. It will expose its contents via [UIA](https://docs.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview) to support tools such as [Windows Narrator](https://support.microsoft.com/en-us/help/22798/windows-10-complete-guide-to-narrator), and UI automation tools including [WinAppDriver](https://github.com/Microsoft/WinAppDriver) |
|
||||
| V1 | 1 | Color Theming & Styling | The Terminal will honor the user's Windows dark/light theme settings, and/or color accent settings. Also, the Terminal background & text colors will be highly configurable, and importable/exportable via settings files.|
|
||||
| V1 | 1 | Background transparency | Background transparency is a valuable feature for many command-line users. Terminal will (optionally) support transparent backgrounds, but without making the Terminal's text content itself transparent (like the Windows Console currently does due to GDI limitations)|
|
||||
| V1 | 1 | Fluent "Acrylic" blurred backgrounds | While full transparency is valuable to some, clear/full-transparency can be distracting. Some would like blurred transparency similar to Fluent Acrylic |
|
||||
| V1 | 1 | Customizable Key Bindings | Terminal will provide a way for users to customize key bindings, enabling them to configure specific key chords to particular Terminal actions |
|
||||
| V1 | 1 | Mouse Support | Terminal will support mouse input, passing mouse movements and actions to command-line apps |
|
||||
| V1 | 2 | Azure Cloud Shell | Enable users to register their Azure account/subscription, and allow the Terminal to enumerate and automatically configure a connection to the user's Cloud Shell |
|
||||
| V1 | 2 | Multiple panes | Multiple tabs are useful to some, but developers often need to see several files/logs on the same screen at the same time. Windows Terminal should allow a "page" to be split into "panes", each running independent commands/shells/etc. similar to [tmux](https://www.ocf.berkeley.edu/~ckuehl/tmux/) on *NIX/macOS |
|
||||
|
||||
Feature Notes:
|
||||
|
||||
\* Feature Priorities:
|
||||
|
||||
0. Mandatory <br/>
|
||||
1. Optimal <br/>
|
||||
2. Optional / Stretch-goal <br/>
|
||||
67
doc/user-docs/ThirdPartyToolProfiles.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Adding profiles for third-party tools
|
||||
|
||||
This doc will hopefully provide a useful guide for adding profiles for common
|
||||
third-party tools to your
|
||||
[profiles.json](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md)
|
||||
file.
|
||||
|
||||
All of these profiles are provided _without_ their `guid` set. If you'd like to
|
||||
set any of these profiles as your _default_ profile, you'll need to make sure to
|
||||
[generate a unique guid](https://www.guidgenerator.com/) for them manually.
|
||||
|
||||
## Anaconda
|
||||
|
||||
Assuming that you've installed Anaconda into `%USERPROFILE%\Anaconda3`:
|
||||
|
||||
```json
|
||||
{
|
||||
"commandline" : "cmd.exe /K %USERPROFILE%\\Anaconda3\\Scripts\\activate.bat %USERPROFILE%\\Anaconda3",
|
||||
"icon" : "%USERPROFILE%/Anaconda3/Menu/anaconda-navigator.ico",
|
||||
"name" : "Anaconda3",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
|
||||
## cmder
|
||||
|
||||
Assuming that you've installed cmder into `%CMDER_ROOT%`:
|
||||
|
||||
```json
|
||||
{
|
||||
"commandline" : "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
|
||||
"name" : "cmder",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
|
||||
## Cygwin
|
||||
|
||||
Assuming that you've installed Cygwin into `C:/Cygwin`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name" : "Cygwin",
|
||||
"commandline" : "C:/Cygwin/bin/bash --login -i",
|
||||
"icon" : "C:/Cygwin/Cygwin.ico",
|
||||
"startingDirectory" : "C:/Cygwin/bin"
|
||||
}
|
||||
```
|
||||
|
||||
Note that the starting directory of Cygwin is set as it is to make the path
|
||||
work. The default directory opened when starting Cygwin will be `$HOME` because
|
||||
of the `--login` flag.
|
||||
|
||||
## Git Bash
|
||||
|
||||
Assuming that you've installed Git Bash into `C:/Program Files/Git`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name" : "Git Bash",
|
||||
"commandline" : "C:/Program Files/Git/bin/bash.exe",
|
||||
"icon" : "C:/Program Files/Git/mingw64/share/git/git-for-windows.ico",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
````
|
||||
|
||||
<!-- Adding a tool here? Make sure to add it in alphabetical order! -->
|
||||
@@ -4,7 +4,7 @@ One way (currently the only way) to configure Windows Terminal is by editing the
|
||||
`profiles.json` settings file. At the time of writing you can open the settings
|
||||
file in your default editor by selecting `Settings` from the WT pull down menu.
|
||||
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\RoamingState\profiles.json`.
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\profiles.json`.
|
||||
|
||||
As of [#2515](https://github.com/microsoft/terminal/pull/2515), the settings are
|
||||
split into _two_ files: a hardcoded `defaults.json`, and `profiles.json`, which
|
||||
@@ -183,6 +183,11 @@ When dynamic profiles are created at runtime, they'll be added to the
|
||||
a linux distro, then the profile will remain in your `profiles.json` file, but
|
||||
the profile will be hidden.
|
||||
|
||||
The Windows Terminal uses the `guid` property of these dynamically-generated
|
||||
profiles to uniquely identify them. If you try to change the `guid` of a
|
||||
dynamically-generated profile, the Terminal will automatically recreate a new
|
||||
entry for that profile.
|
||||
|
||||
If you'd like to disable a particular dynamic profile source, you can add that
|
||||
`source` to the global `"disabledProfileSources"` array. For example, if you'd
|
||||
like to hide all the WSL profiles, you could add the following setting:
|
||||
@@ -200,14 +205,14 @@ like to hide all the WSL profiles, you could add the following setting:
|
||||
|
||||
1. Download the Debian JPG logo https://www.debian.org/logos/openlogo-100.jpg
|
||||
2. Put the image in the
|
||||
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\RoamingState\`
|
||||
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\LocalState\`
|
||||
directory (same directory as your `profiles.json` file).
|
||||
|
||||
__NOTE__: You can put the image anywhere you like, the above suggestion happens to be convenient.
|
||||
3. Open your WT json properties file.
|
||||
4. Under the Debian Linux profile, add the following fields:
|
||||
```json
|
||||
"backgroundImage": "ms-appdata:///Roaming/openlogo-100.jpg",
|
||||
"backgroundImage": "ms-appdata:///Local/openlogo-100.jpg",
|
||||
"backgroundImageOpacity": 1,
|
||||
"backgroundImageStretchMode" : "none",
|
||||
"backgroundImageAlignment" : "topRight",
|
||||
@@ -241,6 +246,8 @@ following objects into your `globals.keybindings` array:
|
||||
{ "command": "paste", "keys": ["ctrl+shift+v"] }
|
||||
```
|
||||
|
||||
> 👉 **Note**: you can also add a keybinding for the `copyTextWithoutNewlines` command. This removes newlines as the text is copied to your clipboard.
|
||||
|
||||
This will add copy and paste on <kbd>ctrl+shift+c</kbd>
|
||||
and <kbd>ctrl+shift+v</kbd> respectively.
|
||||
|
||||
@@ -270,8 +277,33 @@ 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'll only be able to send
|
||||
> 👉 **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"`
|
||||
|
||||
|
||||
### Setting the `startingDirectory` of WSL Profiles to `~`
|
||||
|
||||
By default, the `startingDirectory` of a profile is `%USERPROFILE%`
|
||||
(`C:\Users\<YourUsername>`). This is a Windows path. However, for WSL, you might
|
||||
want to use the WSL home path instead. At the time of writing (26decf1 / Nov.
|
||||
1st, 2019), `startingDirectory` only accepts a Windows-style path, so setting it
|
||||
to start within the WSL distro can be a little tricky.
|
||||
|
||||
Fortunately, with Windows 1903, the filesystems of WSL distros can easily be
|
||||
addressed using the `\\wsl$\` prefix. For any WSL distro whose name is
|
||||
`DistroName`, you can use `\\wsl$\DistroName` as a Windows path that points to
|
||||
the root of that distro's filesystem.
|
||||
|
||||
For example, the following works as a profile to launch the "Ubuntu-18.04"
|
||||
distro in it's home path:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Ubuntu-18.04",
|
||||
"commandline" : "wsl -d Ubuntu-18.04",
|
||||
"startingDirectory" : "//wsl$/Ubuntu-18.04/home/<Your Ubuntu Username>",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
NOTE: At the time of writing Windows Terminal is still under active development and many things will
|
||||
change. If you notice an error in the docs, please raise an issue. Or better yet, please file a PR with an appropriate update!
|
||||
|
||||
## Installing Windows Terminal
|
||||
## Installing Windows Terminal
|
||||
|
||||
### From Source Code
|
||||
|
||||
@@ -18,12 +18,10 @@ To compile Windows Terminal yourself using the source code, follow the instructi
|
||||
|
||||
## Starting Windows Terminal
|
||||
|
||||
From the Windows Start menu, select Windows Terminal and run the application.
|
||||
|
||||
Note: You can right click on the application item and run with Windows Administrator privilege if required.
|
||||
|
||||
The default shell is PowerShell.
|
||||
1. Locate the _Windows Terminal_ app in your Start menu.
|
||||
2. Click _Windows Terminal_ to launch the app. If you need administrative privileges, right-click the entry and click `Run as administrator`. Alternatively, you can highlight the app and press `Ctrl`+`Shift`+`Enter`.
|
||||
|
||||
NOTE: The default shell is PowerShell; you can change this using the _Running a Different Shell_ procedure.
|
||||
|
||||
### Command line options
|
||||
|
||||
@@ -32,7 +30,7 @@ None at this time. See issue [#607](https://github.com/microsoft/terminal/issues
|
||||
## Multiple Tabs
|
||||
|
||||
Additional shells can be started by hitting the `+` button from the tab bar -- a new instance of the
|
||||
default shell is displayed (default shortcut `Ctrl+Shift+1`).
|
||||
default shell is displayed (default shortcut: <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>1</kbd>).
|
||||
|
||||
## Running a Different Shell
|
||||
|
||||
@@ -49,17 +47,17 @@ To customize the shell list, see the _Configuring Windows Terminal_ section belo
|
||||
|
||||
There is no current plan to support this feature for security reasons. See issue [#623](https://github.com/microsoft/terminal/issues/632)
|
||||
|
||||
## Using cut and paste in the Terminal window
|
||||
## Selecting and Copying Text in Windows Terminal
|
||||
|
||||
### With PowerShell
|
||||
As in ConHost, a selection can be made by left-clicking and dragging the mouse across the terminal. This is a line selection by default, meaning that the selection will wrap to the end of the line and the beginning of the next one. You can select in block mode by holding down the <kbd>Alt</kbd> key when starting a selection.
|
||||
|
||||
* Copy - Select the text with mouse (default left button), then right click with mouse
|
||||
* Paste - by default use `<ctrl>+v`>, or right click with mouse
|
||||
To copy the text to your clipboard, you can right-click the terminal when a selection is active. As of [#1224](https://github.com/microsoft/terminal/pull/1224) (first available in Windows Terminal v0.4), the Windows Terminal now supports HTML copy. The HTML is automatically copied to your clipboard along with the regular text in any copy operation.
|
||||
|
||||
### With Bash
|
||||
If there is not an active selection, a right-click will paste the text content from your clipboard to the terminal.
|
||||
|
||||
* Copy - Select the text with mouse (default left button), then right click with mouse
|
||||
* Paste - Right click with mouse
|
||||
Copy and paste operations can also be keybound. For more information on how to bind keys, see [Using Json Settings](UsingJsonSettings.md#adding-copy-and-paste-keybindings).
|
||||
|
||||
> 👉 **Note**: If you have the `copyOnSelect` global setting enabled, a selection will persist and immediately copy the selected text to your clipboard. Right-clicking will always paste your clipboard data.
|
||||
|
||||
## Add a "Open Windows Terminal Here" to File Explorer
|
||||
|
||||
@@ -67,12 +65,12 @@ Not currently supported "out of the box". See issue [#1060](https://github.com/m
|
||||
|
||||
## Configuring Windows Terminal
|
||||
|
||||
All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/RoamingState`.
|
||||
All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState`.
|
||||
|
||||
To open the settings file from Windows Terminal:
|
||||
|
||||
1. Click the `⌵` button in the top bar.
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: `Ctrl+,`.
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: <kbd>Ctrl</kbd>+<kbd>,</kbd>.
|
||||
3. Your default `json` editor will open the settings file.
|
||||
|
||||
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.
|
||||
@@ -86,6 +84,6 @@ For an introduction to the various settings, see [Using Json Settings](UsingJson
|
||||
|
||||
(ref https://twitter.com/r_keith_hill/status/1142871145852440576)
|
||||
|
||||
2. Terminal zoom can be changed by holding `Ctrl` and scrolling with mouse.
|
||||
3. If `useAcrylic` is enabled in profiles.json, background opacity can be changed by holding `Ctrl+Shift` and scrolling with mouse.
|
||||
2. Terminal zoom can be changed by holding <kbd>Ctrl</kbd> and scrolling with mouse.
|
||||
3. If `useAcrylic` is enabled in profiles.json, background opacity can be changed by holding <kbd>Ctrl</kbd>+<kbd>Shift</kbd> and scrolling with mouse.
|
||||
4. Please add more Tips and Tricks
|
||||
|
||||
@@ -27,7 +27,7 @@ You may ask yourself, why is the destructor deleted, then later defined to the
|
||||
strangeness that can occur as well, the details of which escape my memory from
|
||||
when @austdi and I first investigaved this early 2018.
|
||||
|
||||
The end result of not defining your interfaces exacly like this will be that
|
||||
The end result of not defining your interfaces exactly like this will be that
|
||||
occasionally, when destructing objects, you'll get a segfault.
|
||||
|
||||
To check that this behavior works, I direct your attention to the VtIoTests.
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
|
||||
|
||||
<PropertyGroup Label="Configuration">
|
||||
<TargetPlatformVersion>10.0.17763.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
|
||||
<!--
|
||||
These two properties are very important!
|
||||
Without them, msbuild will stomp MinVersion and MaxVersionTested in the
|
||||
Package.appxmanifest and replace them with whatever our values for
|
||||
TargetPlatformMinVersion and TargetPlatformVersion are.
|
||||
-->
|
||||
<AppxOSMinVersionReplaceManifestVersion>false</AppxOSMinVersionReplaceManifestVersion>
|
||||
<AppxOSMaxVersionTestedReplaceManifestVersion>false</AppxOSMaxVersionTestedReplaceManifestVersion>
|
||||
|
||||
<!-- In a WAP project, this suppresses a spurious dependency on the non-Desktop VCLibs. -->
|
||||
<IncludeGetResolvedSDKReferences>false</IncludeGetResolvedSDKReferences>
|
||||
|
||||
<!-- Suppress the inclusion of Windows.winmd in the appx. -->
|
||||
<SkipUnionWinmd>true</SkipUnionWinmd>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>2D310963-F3E0-4EE5-8AC6-FBC94DCC3310</ProjectGuid>
|
||||
|
||||
<EntryPointExe>OpenConsole.exe</EntryPointExe>
|
||||
<EntryPointProjectUniqueName>..\..\src\host\exe\Host.EXE.vcxproj</EntryPointProjectUniqueName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="!Exists('OpenConsolePackage_TemporaryKey.pfx')">
|
||||
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
|
||||
<AppxBundle>Never</AppxBundle>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="Exists('OpenConsolePackage_TemporaryKey.pfx')">
|
||||
<AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>
|
||||
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||
<PackageCertificateKeyFile>OpenConsolePackage_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="Exists('OpenConsolePackage_TemporaryKey.pfx')">
|
||||
<None Include="OpenConsolePackage_TemporaryKey.pfx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="images\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="images\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="images\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="images\Square44x44Logo.targetsize-16_altform-unplated.png" />
|
||||
<Content Include="images\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="images\Square44x44Logo.targetsize-32_altform-unplated.png" />
|
||||
<Content Include="images\Square44x44Logo.targetsize-48_altform-unplated.png" />
|
||||
<Content Include="images\Square44x44Logo.targetsize-256_altform-unplated.png" />
|
||||
<Content Include="images\StoreLogo.png" />
|
||||
<Content Include="images\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.post.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\host\exe\Host.EXE.vcxproj" />
|
||||
<ProjectReference Include="..\..\src\propsheet\propsheet.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="OpenConsoleStompSourceProjectForWapProject" BeforeTargets="_ConvertItems">
|
||||
<ItemGroup>
|
||||
<_TemporaryFilteredWapProjOutput Include="@(_FilteredNonWapProjProjectOutput)" />
|
||||
<_FilteredNonWapProjProjectOutput Remove="@(_FilteredNonWapProjProjectOutput)" />
|
||||
<_FilteredNonWapProjProjectOutput Include="@(_TemporaryFilteredWapProjOutput)">
|
||||
<SourceProject></SourceProject> <!-- Blank SourceProject, which WapProj uses to name subdirectories. -->
|
||||
</_FilteredNonWapProjProjectOutput>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4" IgnorableNamespaces="uap mp rescap">
|
||||
<Identity Name="Microsoft.WindowsConsoleHost" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="0.9.0.0" />
|
||||
<Properties>
|
||||
<DisplayName>Windows Console (Preview)</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>images\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0" />
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="OpenConsole.exe" EntryPoint="$targetentrypoint$">
|
||||
<Extensions>
|
||||
<uap3:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication" Executable="OpenConsole.exe">
|
||||
<uap3:AppExecutionAlias>
|
||||
<desktop:ExecutionAlias Alias="OpenConsole.exe" />
|
||||
<desktop:ExecutionAlias Alias="confans.exe" />
|
||||
</uap3:AppExecutionAlias>
|
||||
</uap3:Extension>
|
||||
</Extensions>
|
||||
<uap:VisualElements DisplayName="Windows Console (Preview)" Description="The Windows Console, but actually fun!" BackgroundColor="transparent" Square150x150Logo="images\Square150x150Logo.png" Square44x44Logo="images\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="images\Wide310x150Logo.png">
|
||||
</uap:DefaultTile>
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
|
Before Width: | Height: | Size: 716 B |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 551 B |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 697 B |
|
Before Width: | Height: | Size: 778 B |
|
Before Width: | Height: | Size: 719 B |
|
Before Width: | Height: | Size: 6.2 KiB |
BIN
res/Cascadia.ttf
Normal file
@@ -1,6 +1,21 @@
|
||||
# Windows Terminal and Console Assets
|
||||
|
||||
The assets in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/master/LICENSE) as the rest
|
||||
## Images
|
||||
|
||||
The images in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/master/LICENSE) as the rest
|
||||
of the Windows Terminal code.
|
||||
|
||||
Please consult the [license](./LICENSE) in this directory for applicable terms.
|
||||
Please consult the [license](./LICENSE) in this directory for terms applicable to the image assets in this directory.
|
||||
|
||||
## Fonts
|
||||
|
||||
The fonts in this directory do not fall under the same [license](https://raw.githubusercontent.com/microsoft/terminal/master/LICENSE) as the rest
|
||||
of the Windows Terminal code.
|
||||
|
||||
Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadia-code/master/LICENSE) in the
|
||||
[microsoft/cascadia-code](https://github.com/microsoft/cascadia-code) repository for terms applicable to the fonts in this directory.
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code
|
||||
* from microsoft/cascadia-code@d733599504811e8f3969de20368817d20e162dba
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
<!-- 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" />
|
||||
<!-- This one has caught us off guard as it suddenly showed up. Re-enablement is going to be in #2941 -->
|
||||
<Rule Id="C26814" Action="None" />
|
||||
</Rules>
|
||||
|
||||
|
||||
|
||||
@@ -278,31 +278,71 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
// .. otherwise if we internally have a list of 2 and we're about to insert a single color
|
||||
// it's probable that we're just walking left-to-right through the row and changing each
|
||||
// cell one at a time.
|
||||
// e.g.
|
||||
// AAAAABBBBBBB
|
||||
// AAAAAABBBBBB
|
||||
// AAAAAAABBBBB
|
||||
// Check for that circumstance by seeing if we're inserting a single run of the
|
||||
// left side color right at the boundary and just adjust the counts in the existing
|
||||
// two elements in our internal list.
|
||||
else if (_list.size() == 2 && newAttrs.at(0).GetLength() == 1)
|
||||
// .. otherwise if we internally have a list of 2 or more and we're about to insert a single color
|
||||
// it's possible that we just walk left-to-right through the row and find a quick exit.
|
||||
else if (iStart > 0 && iStart == iEnd)
|
||||
{
|
||||
const auto left = _list.begin();
|
||||
if (iStart == left->GetLength() && NewAttr == left->GetAttributes())
|
||||
// First we try to find the run where the insertion happens, using lowerBound and upperBound to track
|
||||
// where we are curretly at.
|
||||
size_t lowerBound = 0;
|
||||
size_t upperBound = 0;
|
||||
for (size_t i = 0; i < _list.size(); i++)
|
||||
{
|
||||
const auto right = left + 1;
|
||||
left->IncrementLength();
|
||||
right->DecrementLength();
|
||||
|
||||
// If we just reduced the right half to zero, just erase it out of the list.
|
||||
if (right->GetLength() == 0)
|
||||
upperBound += _list.at(i).GetLength();
|
||||
if (iStart >= lowerBound && iStart < upperBound)
|
||||
{
|
||||
_list.erase(right);
|
||||
const auto curr = std::next(_list.begin(), i);
|
||||
|
||||
// The run that we try to insert into has the same color as the new one.
|
||||
// e.g.
|
||||
// AAAAABBBBBBBCCC
|
||||
// ^
|
||||
// AAAAABBBBBBBCCC
|
||||
//
|
||||
// 'B' is the new color and '^' represents where iStart is. We don't have to
|
||||
// do anything.
|
||||
if (curr->GetAttributes() == NewAttr)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// If the insertion happens at current run's lower boundary...
|
||||
if (iStart == lowerBound)
|
||||
{
|
||||
const auto prev = std::prev(curr, 1);
|
||||
// ... and the previous run has the same color as the new one, we can
|
||||
// just adjust the counts in the existing two elements in our internal list.
|
||||
// e.g.
|
||||
// AAAAABBBBBBBCCC
|
||||
// ^
|
||||
// AAAAAABBBBBBCCC
|
||||
//
|
||||
// Here 'A' is the new color.
|
||||
if (NewAttr == prev->GetAttributes())
|
||||
{
|
||||
prev->IncrementLength();
|
||||
curr->DecrementLength();
|
||||
|
||||
// If we just reduced the right half to zero, just erase it out of the list.
|
||||
if (curr->GetLength() == 0)
|
||||
{
|
||||
_list.erase(curr);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advance one run in the _list.
|
||||
lowerBound = upperBound;
|
||||
|
||||
// The lowerBound is larger than iStart, which means we fail to find an early exit at the run
|
||||
// where the insertion happens. We can just break out.
|
||||
if (lowerBound > iStart)
|
||||
{
|
||||
break;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,20 +16,22 @@ AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow
|
||||
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) noexcept :
|
||||
_pAttrRow{ attrRow },
|
||||
_run{ attrRow->_list.cbegin() },
|
||||
_currentAttributeIndex{ 0 }
|
||||
_currentAttributeIndex{ 0 },
|
||||
_exceeded{ false }
|
||||
{
|
||||
}
|
||||
|
||||
AttrRowIterator::operator bool() const
|
||||
{
|
||||
return _run < _pAttrRow->_list.cend();
|
||||
return !_exceeded && _run < _pAttrRow->_list.cend();
|
||||
}
|
||||
|
||||
bool AttrRowIterator::operator==(const AttrRowIterator& it) const
|
||||
{
|
||||
return (_pAttrRow == it._pAttrRow &&
|
||||
_run == it._run &&
|
||||
_currentAttributeIndex == it._currentAttributeIndex);
|
||||
_currentAttributeIndex == it._currentAttributeIndex &&
|
||||
_exceeded == it._exceeded);
|
||||
}
|
||||
|
||||
bool AttrRowIterator::operator!=(const AttrRowIterator& it) const
|
||||
@@ -52,13 +54,16 @@ AttrRowIterator AttrRowIterator::operator++(int)
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
if (movement >= 0)
|
||||
if (!_exceeded)
|
||||
{
|
||||
_increment(gsl::narrow<size_t>(movement));
|
||||
}
|
||||
else
|
||||
{
|
||||
_decrement(gsl::narrow<size_t>(-movement));
|
||||
if (movement >= 0)
|
||||
{
|
||||
_increment(gsl::narrow<size_t>(movement));
|
||||
}
|
||||
else
|
||||
{
|
||||
_decrement(gsl::narrow<size_t>(-movement));
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -84,11 +89,13 @@ AttrRowIterator AttrRowIterator::operator--(int)
|
||||
|
||||
const TextAttribute* AttrRowIterator::operator->() const
|
||||
{
|
||||
THROW_HR_IF(E_BOUNDS, _exceeded);
|
||||
return &_run->GetAttributes();
|
||||
}
|
||||
|
||||
const TextAttribute& AttrRowIterator::operator*() const
|
||||
{
|
||||
THROW_HR_IF(E_BOUNDS, _exceeded);
|
||||
return _run->GetAttributes();
|
||||
}
|
||||
|
||||
@@ -123,14 +130,23 @@ void AttrRowIterator::_decrement(size_t count)
|
||||
{
|
||||
while (count > 0)
|
||||
{
|
||||
// If there's still space within this color attribute to move left, do so.
|
||||
if (count <= _currentAttributeIndex)
|
||||
{
|
||||
_currentAttributeIndex -= count;
|
||||
return;
|
||||
}
|
||||
// If there's not space, move to the previous attribute run
|
||||
// We'll walk through above on the if branch to move left further (if necessary)
|
||||
else
|
||||
{
|
||||
count -= _currentAttributeIndex;
|
||||
// make sure we don't go out of bounds
|
||||
if (_run == _pAttrRow->_list.cbegin())
|
||||
{
|
||||
_exceeded = true;
|
||||
return;
|
||||
}
|
||||
count -= _currentAttributeIndex + 1;
|
||||
--_run;
|
||||
_currentAttributeIndex = _run->GetLength() - 1;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ private:
|
||||
std::vector<TextAttributeRun>::const_iterator _run;
|
||||
const ATTR_ROW* _pAttrRow;
|
||||
size_t _currentAttributeIndex; // index of TextAttribute within the current TextAttributeRun
|
||||
bool _exceeded;
|
||||
|
||||
void _increment(size_t count);
|
||||
void _decrement(size_t count);
|
||||
|
||||
@@ -147,11 +147,11 @@ const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept
|
||||
// Arguments:
|
||||
// - it - custom console iterator to use for seeking input data. bool() false when it becomes invalid while seeking.
|
||||
// - index - column in row to start writing at
|
||||
// - setWrap - set the wrap flags if we hit the end of the row while writing and there's still more data in the iterator.
|
||||
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data in the iterator.
|
||||
// - limitRight - right inclusive column ID for the last write in this row. (optional, will just write to the end of row if nullopt)
|
||||
// Return Value:
|
||||
// - iterator to first cell that was not written to this row.
|
||||
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const bool setWrap, std::optional<size_t> limitRight)
|
||||
OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap, std::optional<size_t> limitRight)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, index >= _charRow.size());
|
||||
THROW_HR_IF(E_INVALIDARG, limitRight.value_or(0) >= _charRow.size());
|
||||
@@ -202,10 +202,15 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
|
||||
++it;
|
||||
}
|
||||
|
||||
// If we're asked to set the wrap status and we just filled the last column with some text, set wrap status on the row.
|
||||
if (setWrap && fillingLastColumn)
|
||||
// If we're asked to (un)set the wrap status and we just filled the last column with some text...
|
||||
// NOTE:
|
||||
// - wrap = std::nullopt --> don't change the wrap value
|
||||
// - wrap = true --> we're filling cells as a steam, consider this a wrap
|
||||
// - wrap = false --> we're filling cells as a block, unwrap
|
||||
if (wrap.has_value() && fillingLastColumn)
|
||||
{
|
||||
_charRow.SetWrapForced(true);
|
||||
// set wrap status on the row to parameter's value.
|
||||
_charRow.SetWrapForced(wrap.value());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
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);
|
||||
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap = std::nullopt, std::optional<size_t> limitRight = std::nullopt);
|
||||
|
||||
friend bool operator==(const ROW& a, const ROW& b) noexcept;
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view<COLORREF>
|
||||
COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view<COLORREF> colorTable,
|
||||
COLORREF defaultColor) const noexcept
|
||||
{
|
||||
return _foreground.GetColor(colorTable, defaultColor, _isBold);
|
||||
return _foreground.GetColor(colorTable, defaultColor, IsBold());
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -155,11 +155,6 @@ void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground)
|
||||
}
|
||||
}
|
||||
|
||||
bool TextAttribute::IsBold() const noexcept
|
||||
{
|
||||
return _isBold;
|
||||
}
|
||||
|
||||
bool TextAttribute::_IsReverseVideo() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO);
|
||||
@@ -215,6 +210,11 @@ void TextAttribute::Debolden() noexcept
|
||||
_SetBoldness(false);
|
||||
}
|
||||
|
||||
void TextAttribute::SetExtendedAttributes(const ExtendedAttributes attrs) noexcept
|
||||
{
|
||||
_extendedAttrs = attrs;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - swaps foreground and background color
|
||||
void TextAttribute::Invert() noexcept
|
||||
@@ -224,7 +224,7 @@ void TextAttribute::Invert() noexcept
|
||||
|
||||
void TextAttribute::_SetBoldness(const bool isBold) noexcept
|
||||
{
|
||||
_isBold = isBold;
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Bold, isBold);
|
||||
}
|
||||
|
||||
void TextAttribute::SetDefaultForeground() noexcept
|
||||
|
||||
@@ -27,6 +27,8 @@ Revision History:
|
||||
#include "WexTestClass.h"
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
class TextAttribute final
|
||||
{
|
||||
public:
|
||||
@@ -34,7 +36,7 @@ public:
|
||||
_wAttrLegacy{ 0 },
|
||||
_foreground{},
|
||||
_background{},
|
||||
_isBold{ false }
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ public:
|
||||
_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 }
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
{
|
||||
// If we're given lead/trailing byte information with the legacy color, strip it.
|
||||
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
|
||||
@@ -53,7 +55,7 @@ public:
|
||||
_wAttrLegacy{ 0 },
|
||||
_foreground{ rgbForeground },
|
||||
_background{ rgbBackground },
|
||||
_isBold{ false }
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -62,7 +64,7 @@ public:
|
||||
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);
|
||||
return (fg | bg | meta) | (IsBold() ? FOREGROUND_INTENSITY : 0);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -85,7 +87,7 @@ public:
|
||||
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);
|
||||
return (fg | bg | meta) | (IsBold() ? FOREGROUND_INTENSITY : 0);
|
||||
}
|
||||
|
||||
COLORREF CalculateRgbForeground(std::basic_string_view<COLORREF> colorTable,
|
||||
@@ -131,7 +133,18 @@ public:
|
||||
friend constexpr bool operator!=(const WORD& legacyAttr, const TextAttribute& attr) noexcept;
|
||||
|
||||
bool IsLegacy() const noexcept;
|
||||
bool IsBold() const noexcept;
|
||||
|
||||
constexpr bool IsBold() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Bold);
|
||||
}
|
||||
|
||||
constexpr ExtendedAttributes GetExtendedAttributes() const noexcept
|
||||
{
|
||||
return _extendedAttrs;
|
||||
}
|
||||
|
||||
void SetExtendedAttributes(const ExtendedAttributes attrs) noexcept;
|
||||
|
||||
void SetForeground(const COLORREF rgbForeground) noexcept;
|
||||
void SetBackground(const COLORREF rgbBackground) noexcept;
|
||||
@@ -159,7 +172,7 @@ private:
|
||||
WORD _wAttrLegacy;
|
||||
TextColor _foreground;
|
||||
TextColor _background;
|
||||
bool _isBold;
|
||||
ExtendedAttributes _extendedAttrs;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
@@ -169,6 +182,13 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
// 2 for _wAttrLegacy
|
||||
// 4 for _foreground
|
||||
// 4 for _background
|
||||
// 1 for _extendedAttrs
|
||||
static_assert(sizeof(TextAttribute) <= 11 * sizeof(BYTE), "We should only need 11B for an entire TextColor. Any more than that is just waste");
|
||||
|
||||
enum class TextAttributeBehavior
|
||||
{
|
||||
Stored, // use contained text attribute
|
||||
@@ -181,7 +201,7 @@ constexpr bool operator==(const TextAttribute& a, const TextAttribute& b) noexce
|
||||
return a._wAttrLegacy == b._wAttrLegacy &&
|
||||
a._foreground == b._foreground &&
|
||||
a._background == b._background &&
|
||||
a._isBold == b._isBold;
|
||||
a._extendedAttrs == b._extendedAttrs;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const TextAttribute& a, const TextAttribute& b) noexcept
|
||||
|
||||
@@ -19,14 +19,14 @@ Revision History:
|
||||
|
||||
#include "../inc/conattrs.hpp"
|
||||
|
||||
// the following values are used to create the textmode cursor.
|
||||
#define CURSOR_SMALL_SIZE 25 // large enough to be one pixel on a six pixel font
|
||||
class TextBuffer;
|
||||
|
||||
class Cursor final
|
||||
{
|
||||
public:
|
||||
static const unsigned int s_InvertCursorColor = INVALID_COLOR;
|
||||
// the following values are used to create the textmode cursor.
|
||||
static constexpr unsigned int CURSOR_SMALL_SIZE = 25; // large enough to be one pixel on a six pixel font
|
||||
|
||||
Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept;
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{0CF235BD-2DA0-407E-90EE-C467E8BBC714}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>bufferout</RootNamespace>
|
||||
<ProjectName>BufferOut</ProjectName>
|
||||
<TargetName>ConBufferOut</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AttrRow.cpp" />
|
||||
@@ -11,6 +19,7 @@
|
||||
<ClCompile Include="..\OutputCellView.cpp" />
|
||||
<ClCompile Include="..\Row.cpp" />
|
||||
<ClCompile Include="..\RowCellIterator.cpp" />
|
||||
<ClCompile Include="..\search.cpp" />
|
||||
<ClCompile Include="..\TextColor.cpp" />
|
||||
<ClCompile Include="..\TextAttribute.cpp" />
|
||||
<ClCompile Include="..\TextAttributeRun.cpp" />
|
||||
@@ -37,6 +46,7 @@
|
||||
<ClInclude Include="..\OutputCellView.hpp" />
|
||||
<ClInclude Include="..\Row.hpp" />
|
||||
<ClInclude Include="..\RowCellIterator.hpp" />
|
||||
<ClInclude Include="..\search.h" />
|
||||
<ClInclude Include="..\TextColor.h" />
|
||||
<ClInclude Include="..\TextAttribute.h" />
|
||||
<ClInclude Include="..\TextAttributeRun.h" />
|
||||
@@ -49,19 +59,6 @@
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
<ClInclude Include="..\UnicodeStorage.hpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{0CF235BD-2DA0-407E-90EE-C467E8BBC714}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>bufferout</RootNamespace>
|
||||
<ProjectName>BufferOut</ProjectName>
|
||||
<TargetName>ConBufferOut</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\AppModel;$(SolutionDir)\dep\MinCore;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.lib.props" />
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -5,29 +5,32 @@
|
||||
|
||||
#include "search.h"
|
||||
|
||||
#include "dbcs.h"
|
||||
#include "../buffer/out/CharRow.hpp"
|
||||
#include "CharRow.hpp"
|
||||
#include "textBuffer.hpp"
|
||||
#include "../types/inc/Utf16Parser.hpp"
|
||||
#include "../types/inc/GlyphWidth.hpp"
|
||||
|
||||
using namespace Microsoft::Console::Types;
|
||||
|
||||
// Routine Description:
|
||||
// - Constructs a Search object.
|
||||
// - Make a Search object then call .FindNext() to locate items.
|
||||
// - Once you've found something, you can perfom actions like .Select() or .Color()
|
||||
// Arguments:
|
||||
// - screenInfo - The screen buffer to search through (the "haystack")
|
||||
// - textBuffer - The screen text buffer to search through (the "haystack")
|
||||
// - uiaData - The IUiaData type reference, it is for providing selection methods
|
||||
// - str - The search term you want to find (the "needle")
|
||||
// - direction - The direction to search (upward or downward)
|
||||
// - sensitivity - Whether or not you care about case
|
||||
Search::Search(const SCREEN_INFORMATION& screenInfo,
|
||||
Search::Search(IUiaData& uiaData,
|
||||
const std::wstring& str,
|
||||
const Direction direction,
|
||||
const Sensitivity sensitivity) :
|
||||
_direction(direction),
|
||||
_sensitivity(sensitivity),
|
||||
_screenInfo(screenInfo),
|
||||
_needle(s_CreateNeedleFromString(str)),
|
||||
_coordAnchor(s_GetInitialAnchor(screenInfo, direction))
|
||||
_uiaData(uiaData),
|
||||
_coordAnchor(s_GetInitialAnchor(uiaData, direction))
|
||||
{
|
||||
_coordNext = _coordAnchor;
|
||||
}
|
||||
@@ -37,21 +40,22 @@ Search::Search(const SCREEN_INFORMATION& screenInfo,
|
||||
// - Make a Search object then call .FindNext() to locate items.
|
||||
// - Once you've found something, you can perfom actions like .Select() or .Color()
|
||||
// Arguments:
|
||||
// - screenInfo - The screen buffer to search through (the "haystack")
|
||||
// - textBuffer - The screen text buffer to search through (the "haystack")
|
||||
// - uiaData - The IUiaData type reference, it is for providing selection methods
|
||||
// - str - The search term you want to find (the "needle")
|
||||
// - direction - The direction to search (upward or downward)
|
||||
// - sensitivity - Whether or not you care about case
|
||||
// - anchor - starting search location in screenInfo
|
||||
Search::Search(const SCREEN_INFORMATION& screenInfo,
|
||||
Search::Search(IUiaData& uiaData,
|
||||
const std::wstring& str,
|
||||
const Direction direction,
|
||||
const Sensitivity sensitivity,
|
||||
const COORD anchor) :
|
||||
_direction(direction),
|
||||
_sensitivity(sensitivity),
|
||||
_screenInfo(screenInfo),
|
||||
_needle(s_CreateNeedleFromString(str)),
|
||||
_coordAnchor(anchor)
|
||||
_coordAnchor(anchor),
|
||||
_uiaData(uiaData)
|
||||
{
|
||||
_coordNext = _coordAnchor;
|
||||
}
|
||||
@@ -96,12 +100,13 @@ void Search::Select() const
|
||||
// Only select if we've found something.
|
||||
if (_coordSelStart != _coordSelEnd)
|
||||
{
|
||||
Selection::Instance().SelectNewRegion(_coordSelStart, _coordSelEnd);
|
||||
_uiaData.SelectNewRegion(_coordSelStart, _coordSelEnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Takes the found word and applies the given color to it in the screen buffer
|
||||
// - In console host, we take the found word and apply the given color to it in the screen buffer
|
||||
// - In Windows Terminal, we just select the found word, but we do not modify the buffer
|
||||
// Arguments:
|
||||
// - ulAttr - The legacy color attribute to apply to the word
|
||||
void Search::Color(const TextAttribute attr) const
|
||||
@@ -109,7 +114,7 @@ void Search::Color(const TextAttribute attr) const
|
||||
// Only select if we've found something.
|
||||
if (_coordSelStart != _coordSelEnd)
|
||||
{
|
||||
Selection::Instance().ColorSelection(_coordSelStart, _coordSelEnd, attr);
|
||||
_uiaData.ColorSelection(_coordSelStart, _coordSelEnd, attr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,22 +135,23 @@ std::pair<COORD, COORD> Search::GetFoundLocation() const noexcept
|
||||
// - If the screen buffer given already has a selection in it, it will be used to determine the anchor.
|
||||
// - Otherwise, we will choose one of the ends of the screen buffer depending on direction.
|
||||
// Arguments:
|
||||
// - screenInfo - The screen buffer for determining the anchor
|
||||
// - uiaData - The reference to the IUiaData interface type object
|
||||
// - direction - The intended direction of the search
|
||||
// Return Value:
|
||||
// - Coordinate to start the search from.
|
||||
COORD Search::s_GetInitialAnchor(const SCREEN_INFORMATION& screenInfo, const Direction direction)
|
||||
COORD Search::s_GetInitialAnchor(IUiaData& uiaData, const Direction direction)
|
||||
{
|
||||
if (Selection::Instance().IsInSelectingState())
|
||||
const auto& textBuffer = uiaData.GetTextBuffer();
|
||||
if (uiaData.IsSelectionActive())
|
||||
{
|
||||
auto anchor = Selection::Instance().GetSelectionAnchor();
|
||||
auto anchor = uiaData.GetSelectionAnchor();
|
||||
if (direction == Direction::Forward)
|
||||
{
|
||||
screenInfo.GetBufferSize().IncrementInBoundsCircular(anchor);
|
||||
textBuffer.GetSize().IncrementInBoundsCircular(anchor);
|
||||
}
|
||||
else
|
||||
{
|
||||
screenInfo.GetBufferSize().DecrementInBoundsCircular(anchor);
|
||||
textBuffer.GetSize().DecrementInBoundsCircular(anchor);
|
||||
}
|
||||
return anchor;
|
||||
}
|
||||
@@ -157,7 +163,7 @@ COORD Search::s_GetInitialAnchor(const SCREEN_INFORMATION& screenInfo, const Dir
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto bufferSize = screenInfo.GetBufferSize().Dimensions();
|
||||
const auto bufferSize = textBuffer.GetSize().Dimensions();
|
||||
return { bufferSize.X - 1, bufferSize.Y - 1 };
|
||||
}
|
||||
}
|
||||
@@ -183,7 +189,7 @@ bool Search::_FindNeedleInHaystackAt(const COORD pos, COORD& start, COORD& end)
|
||||
for (const auto& needleCell : _needle)
|
||||
{
|
||||
// Haystack is the buffer. Needle is the string we were given.
|
||||
const auto hayIter = _screenInfo.GetTextDataAt(bufferPos);
|
||||
const auto hayIter = _uiaData.GetTextBuffer().GetTextDataAt(bufferPos);
|
||||
const auto hayChars = *hayIter;
|
||||
const auto needleChars = std::wstring_view(needleCell.data(), needleCell.size());
|
||||
|
||||
@@ -214,7 +220,7 @@ bool Search::_FindNeedleInHaystackAt(const COORD pos, COORD& start, COORD& end)
|
||||
// - two - String view representing the second string of text
|
||||
// Return Value:
|
||||
// - True if they are the same. False otherwise.
|
||||
bool Search::_CompareChars(const std::wstring_view one, const std::wstring_view two) const
|
||||
bool Search::_CompareChars(const std::wstring_view one, const std::wstring_view two) const noexcept
|
||||
{
|
||||
if (one.size() != two.size())
|
||||
{
|
||||
@@ -223,7 +229,7 @@ bool Search::_CompareChars(const std::wstring_view one, const std::wstring_view
|
||||
|
||||
for (size_t i = 0; i < one.size(); i++)
|
||||
{
|
||||
if (_ApplySensitivity(one[i]) != _ApplySensitivity(two[i]))
|
||||
if (_ApplySensitivity(one.at(i)) != _ApplySensitivity(two.at(i)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -239,7 +245,7 @@ bool Search::_CompareChars(const std::wstring_view one, const std::wstring_view
|
||||
// - wch - Character to adjust if necessary
|
||||
// Return Value:
|
||||
// - Adjusted value (or not).
|
||||
wchar_t Search::_ApplySensitivity(const wchar_t wch) const
|
||||
wchar_t Search::_ApplySensitivity(const wchar_t wch) const noexcept
|
||||
{
|
||||
if (_sensitivity == Sensitivity::CaseInsensitive)
|
||||
{
|
||||
@@ -257,7 +263,7 @@ wchar_t Search::_ApplySensitivity(const wchar_t wch) const
|
||||
// - coord - Updated by function to increment one position (will wrap X and Y direction)
|
||||
void Search::_IncrementCoord(COORD& coord) const
|
||||
{
|
||||
_screenInfo.GetBufferSize().IncrementInBoundsCircular(coord);
|
||||
_uiaData.GetTextBuffer().GetSize().IncrementInBoundsCircular(coord);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -266,7 +272,7 @@ void Search::_IncrementCoord(COORD& coord) const
|
||||
// - coord - Updated by function to decrement one position (will wrap X and Y direction)
|
||||
void Search::_DecrementCoord(COORD& coord) const
|
||||
{
|
||||
_screenInfo.GetBufferSize().DecrementInBoundsCircular(coord);
|
||||
_uiaData.GetTextBuffer().GetSize().DecrementInBoundsCircular(coord);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -17,6 +17,11 @@ Revision History:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <WinConTypes.h>
|
||||
#include "TextAttribute.hpp"
|
||||
#include "textBuffer.hpp"
|
||||
#include "../types/IUiaData.h"
|
||||
|
||||
// This used to be in find.h.
|
||||
#define SEARCH_STRING_LENGTH (80)
|
||||
|
||||
@@ -35,12 +40,12 @@ public:
|
||||
CaseSensitive
|
||||
};
|
||||
|
||||
Search(const SCREEN_INFORMATION& ScreenInfo,
|
||||
Search(Microsoft::Console::Types::IUiaData& uiaData,
|
||||
const std::wstring& str,
|
||||
const Direction dir,
|
||||
const Sensitivity sensitivity);
|
||||
|
||||
Search(const SCREEN_INFORMATION& ScreenInfo,
|
||||
Search(Microsoft::Console::Types::IUiaData& uiaData,
|
||||
const std::wstring& str,
|
||||
const Direction dir,
|
||||
const Sensitivity sensitivity,
|
||||
@@ -53,15 +58,16 @@ public:
|
||||
std::pair<COORD, COORD> GetFoundLocation() const noexcept;
|
||||
|
||||
private:
|
||||
wchar_t _ApplySensitivity(const wchar_t wch) const;
|
||||
wchar_t _ApplySensitivity(const wchar_t wch) const noexcept;
|
||||
bool Search::_FindNeedleInHaystackAt(const COORD pos, COORD& start, COORD& end) const;
|
||||
bool _CompareChars(const std::wstring_view one, const std::wstring_view two) const;
|
||||
bool _CompareChars(const std::wstring_view one, const std::wstring_view two) const noexcept;
|
||||
void _UpdateNextPosition();
|
||||
|
||||
void _IncrementCoord(COORD& coord) const;
|
||||
void _DecrementCoord(COORD& coord) const;
|
||||
|
||||
static COORD s_GetInitialAnchor(const SCREEN_INFORMATION& screenInfo, const Direction dir);
|
||||
static COORD s_GetInitialAnchor(Microsoft::Console::Types::IUiaData& uiaData, const Direction dir);
|
||||
|
||||
static std::vector<std::vector<wchar_t>> s_CreateNeedleFromString(const std::wstring& wstr);
|
||||
|
||||
bool _reachedEnd = false;
|
||||
@@ -73,7 +79,7 @@ private:
|
||||
const std::vector<std::vector<wchar_t>> _needle;
|
||||
const Direction _direction;
|
||||
const Sensitivity _sensitivity;
|
||||
const SCREEN_INFORMATION& _screenInfo;
|
||||
Microsoft::Console::Types::IUiaData& _uiaData;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class SearchTests;
|
||||
@@ -48,6 +48,7 @@ SOURCES= \
|
||||
..\CharRowCell.cpp \
|
||||
..\CharRowCellReference.cpp \
|
||||
..\UnicodeStorage.cpp \
|
||||
..\search.cpp \
|
||||
|
||||
INCLUDES= \
|
||||
$(INCLUDES); \
|
||||
|
||||
@@ -318,10 +318,12 @@ OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt)
|
||||
// Arguments:
|
||||
// - givenIt - Iterator representing output cell data to write
|
||||
// - target - the row/column to start writing the text to
|
||||
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data
|
||||
// Return Value:
|
||||
// - The final position of the iterator
|
||||
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
|
||||
const COORD target)
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap)
|
||||
{
|
||||
// Make mutable copy so we can walk.
|
||||
auto it = givenIt;
|
||||
@@ -336,7 +338,8 @@ OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
|
||||
while (it && size.IsInBounds(lineTarget))
|
||||
{
|
||||
// Attempt to write as much data as possible onto this line.
|
||||
it = WriteLine(it, lineTarget, true);
|
||||
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
|
||||
it = WriteLine(it, lineTarget, wrap);
|
||||
|
||||
// Move to the next line down.
|
||||
lineTarget.X = 0;
|
||||
@@ -351,13 +354,13 @@ OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
|
||||
// Arguments:
|
||||
// - givenIt - The iterator that will dereference into cell data to insert
|
||||
// - target - Coordinate targeted within output buffer
|
||||
// - setWrap - Whether we should try to set the wrap flag if we write up to the end of the line and have more data
|
||||
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data in the iterator.
|
||||
// - limitRight - Optionally restrict the right boundary for writing (e.g. stop writing earlier than the end of line)
|
||||
// Return Value:
|
||||
// - The iterator, but advanced to where we stopped writing. Use to find input consumed length or cells written length.
|
||||
OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
|
||||
const COORD target,
|
||||
const bool setWrap,
|
||||
const std::optional<bool> wrap,
|
||||
std::optional<size_t> limitRight)
|
||||
{
|
||||
// If we're not in bounds, exit early.
|
||||
@@ -368,7 +371,7 @@ OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
|
||||
|
||||
// Get the row and write the cells
|
||||
ROW& row = GetRowByOffset(target.Y);
|
||||
const auto newIt = row.WriteCells(givenIt, target.X, setWrap, limitRight);
|
||||
const auto newIt = row.WriteCells(givenIt, target.X, wrap, limitRight);
|
||||
|
||||
// Take the cell distance written and notify that it needs to be repainted.
|
||||
const auto written = newIt.GetCellDistance(givenIt);
|
||||
@@ -1062,14 +1065,10 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe
|
||||
// - htmlTitle - value used in title tag of html header. Used to name the application
|
||||
// Return Value:
|
||||
// - string containing the generated HTML
|
||||
std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPoints, const PCWCHAR fontFaceName, const COLORREF backgroundColor, const std::string& htmlTitle)
|
||||
std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPoints, const std::wstring_view fontFaceName, const COLORREF backgroundColor, const std::string& htmlTitle)
|
||||
{
|
||||
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
|
||||
@@ -1093,7 +1092,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
|
||||
htmlBuilder << "font-family:";
|
||||
htmlBuilder << "'";
|
||||
htmlBuilder << ConvertToA(CP_UTF8, faceNameView);
|
||||
htmlBuilder << ConvertToA(CP_UTF8, fontFaceName);
|
||||
htmlBuilder << "',";
|
||||
// even with different font, add monospace as fallback
|
||||
htmlBuilder << "monospace;";
|
||||
@@ -1146,7 +1145,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
}
|
||||
|
||||
const auto writeAccumulatedChars = [&](bool includeCurrent) {
|
||||
if (col > startOffset)
|
||||
if (col >= startOffset)
|
||||
{
|
||||
const auto unescapedText = ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent));
|
||||
for (const auto c : unescapedText)
|
||||
@@ -1241,3 +1240,185 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Generates an RTF document based on the passed in text and color data
|
||||
// RTF 1.5 Spec: https://www.biblioscape.com/rtf15_spec.htm
|
||||
// Arguments:
|
||||
// - rows - the text and color data we will format & encapsulate
|
||||
// - backgroundColor - default background color for characters, also used in padding
|
||||
// - fontHeightPoints - the unscaled font height
|
||||
// - fontFaceName - the name of the font used
|
||||
// - htmlTitle - value used in title tag of html header. Used to name the application
|
||||
// Return Value:
|
||||
// - string containing the generated RTF
|
||||
std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoints, const std::wstring_view fontFaceName, const COLORREF backgroundColor)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ostringstream rtfBuilder;
|
||||
|
||||
// start rtf
|
||||
rtfBuilder << "{";
|
||||
|
||||
// Standard RTF header.
|
||||
// This is similar to the header gnerated by WordPad.
|
||||
// \ansi - specifies that the ANSI char set is used in the current doc
|
||||
// \ansicpg1252 - represents the ANSI code page which is used to perform the Unicode to ANSI conversion when writing RTF text
|
||||
// \deff0 - specifes that the default font for the document is the one at index 0 in the font table
|
||||
// \nouicompat - ?
|
||||
rtfBuilder << "\\rtf1\\ansi\\ansicpg1252\\deff0\\nouicompat";
|
||||
|
||||
// font table
|
||||
rtfBuilder << "{\\fonttbl{\\f0\\fmodern\\fcharset0 " << ConvertToA(CP_UTF8, fontFaceName) << ";}}";
|
||||
|
||||
// map to keep track of colors:
|
||||
// keys are colors represented by COLORREF
|
||||
// values are indices of the corresponding colors in the color table
|
||||
std::unordered_map<COLORREF, int> colorMap;
|
||||
int nextColorIndex = 1; // leave 0 for the default color and start from 1.
|
||||
|
||||
// RTF color table
|
||||
std::ostringstream colorTableBuilder;
|
||||
colorTableBuilder << "{\\colortbl ;";
|
||||
colorTableBuilder << "\\red" << static_cast<int>(GetRValue(backgroundColor))
|
||||
<< "\\green" << static_cast<int>(GetGValue(backgroundColor))
|
||||
<< "\\blue" << static_cast<int>(GetBValue(backgroundColor))
|
||||
<< ";";
|
||||
colorMap[backgroundColor] = nextColorIndex++;
|
||||
|
||||
// content
|
||||
std::ostringstream contentBuilder;
|
||||
contentBuilder << "\\viewkind4\\uc4";
|
||||
|
||||
// paragraph styles
|
||||
// \fs specificies font size in half-points i.e. \fs20 results in a font size
|
||||
// of 10 pts. That's why, font size is multiplied by 2 here.
|
||||
contentBuilder << "\\pard\\slmult1\\f0\\fs" << std::to_string(2 * fontHeightPoints)
|
||||
<< "\\highlight1"
|
||||
<< " ";
|
||||
|
||||
std::optional<COLORREF> fgColor = std::nullopt;
|
||||
std::optional<COLORREF> bkColor = std::nullopt;
|
||||
for (size_t row = 0; row < rows.text.size(); ++row)
|
||||
{
|
||||
size_t startOffset = 0;
|
||||
|
||||
if (row != 0)
|
||||
{
|
||||
contentBuilder << "\\line "; // new line
|
||||
}
|
||||
|
||||
for (size_t col = 0; col < rows.text.at(row).length(); ++col)
|
||||
{
|
||||
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.at(row).at(col) != fgColor.value())
|
||||
{
|
||||
fgColor = rows.FgAttr.at(row).at(col);
|
||||
colorChanged = true;
|
||||
}
|
||||
|
||||
if (!bkColor.has_value() || rows.BkAttr.at(row).at(col) != bkColor.value())
|
||||
{
|
||||
bkColor = rows.BkAttr.at(row).at(col);
|
||||
colorChanged = true;
|
||||
}
|
||||
|
||||
const auto writeAccumulatedChars = [&](bool includeCurrent) {
|
||||
if (col >= startOffset)
|
||||
{
|
||||
const auto unescapedText = ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent));
|
||||
for (const auto c : unescapedText)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\\':
|
||||
case '{':
|
||||
case '}':
|
||||
contentBuilder << "\\" << c;
|
||||
break;
|
||||
default:
|
||||
contentBuilder << c;
|
||||
}
|
||||
}
|
||||
|
||||
startOffset = col;
|
||||
}
|
||||
};
|
||||
|
||||
if (colorChanged)
|
||||
{
|
||||
writeAccumulatedChars(false);
|
||||
|
||||
int bkColorIndex = 0;
|
||||
if (colorMap.find(bkColor.value()) != colorMap.end())
|
||||
{
|
||||
// color already exists in the map, just retrieve the index
|
||||
bkColorIndex = colorMap[bkColor.value()];
|
||||
}
|
||||
else
|
||||
{
|
||||
// color not present in the map, so add it
|
||||
colorTableBuilder << "\\red" << static_cast<int>(GetRValue(bkColor.value()))
|
||||
<< "\\green" << static_cast<int>(GetGValue(bkColor.value()))
|
||||
<< "\\blue" << static_cast<int>(GetBValue(bkColor.value()))
|
||||
<< ";";
|
||||
colorMap[bkColor.value()] = nextColorIndex;
|
||||
bkColorIndex = nextColorIndex++;
|
||||
}
|
||||
|
||||
int fgColorIndex = 0;
|
||||
if (colorMap.find(fgColor.value()) != colorMap.end())
|
||||
{
|
||||
// color already exists in the map, just retrieve the index
|
||||
fgColorIndex = colorMap[fgColor.value()];
|
||||
}
|
||||
else
|
||||
{
|
||||
// color not present in the map, so add it
|
||||
colorTableBuilder << "\\red" << static_cast<int>(GetRValue(fgColor.value()))
|
||||
<< "\\green" << static_cast<int>(GetGValue(fgColor.value()))
|
||||
<< "\\blue" << static_cast<int>(GetBValue(fgColor.value()))
|
||||
<< ";";
|
||||
colorMap[fgColor.value()] = nextColorIndex;
|
||||
fgColorIndex = nextColorIndex++;
|
||||
}
|
||||
|
||||
contentBuilder << "\\highglight" << bkColorIndex
|
||||
<< "\\cf" << fgColorIndex
|
||||
<< " ";
|
||||
}
|
||||
|
||||
if (isLastCharInRow)
|
||||
{
|
||||
writeAccumulatedChars(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// end colortbl
|
||||
colorTableBuilder << "}";
|
||||
|
||||
// add color table to the final RTF
|
||||
rtfBuilder << colorTableBuilder.str();
|
||||
|
||||
// add the text content to the final RTF
|
||||
rtfBuilder << contentBuilder.str();
|
||||
|
||||
// end rtf
|
||||
rtfBuilder << "}";
|
||||
|
||||
return rtfBuilder.str();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_HR(wil::ResultFromCaughtException());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,6 @@ public:
|
||||
Microsoft::Console::Render::IRenderTarget& renderTarget);
|
||||
TextBuffer(const TextBuffer& a) = delete;
|
||||
|
||||
~TextBuffer() = default;
|
||||
|
||||
// Used for duplicating properties to another text buffer
|
||||
void CopyProperties(const TextBuffer& OtherBuffer) noexcept;
|
||||
|
||||
@@ -89,11 +87,12 @@ public:
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt,
|
||||
const COORD target);
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap = true);
|
||||
|
||||
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
|
||||
const COORD target,
|
||||
const bool setWrap = false,
|
||||
const std::optional<bool> setWrap = std::nullopt,
|
||||
const std::optional<size_t> limitRight = std::nullopt);
|
||||
|
||||
bool InsertCharacter(const wchar_t wch, const DbcsAttribute dbcsAttribute, const TextAttribute attr);
|
||||
@@ -147,10 +146,15 @@ public:
|
||||
|
||||
static std::string GenHTML(const TextAndColor& rows,
|
||||
const int fontHeightPoints,
|
||||
const PCWCHAR fontFaceName,
|
||||
const std::wstring_view fontFaceName,
|
||||
const COLORREF backgroundColor,
|
||||
const std::string& htmlTitle);
|
||||
|
||||
static std::string GenRTF(const TextAndColor& rows,
|
||||
const int fontHeightPoints,
|
||||
const std::wstring_view fontFaceName,
|
||||
const COLORREF backgroundColor);
|
||||
|
||||
private:
|
||||
std::deque<ROW> _storage;
|
||||
Cursor _cursor;
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{531C23E7-4B76-4C08-8BBD-04164CB628C9}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>TextBufferUnitTests</RootNamespace>
|
||||
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
|
||||
<TargetName>TextBuffer.Unit.Tests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="TextColorTests.cpp" />
|
||||
@@ -17,20 +25,12 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{531C23E7-4B76-4C08-8BBD-04164CB628C9}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>TextBufferUnitTests</RootNamespace>
|
||||
<ProjectName>TextBuffer.Unit.Tests</ProjectName>
|
||||
<TargetName>TextBuffer.Unit.Tests</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..;$(SolutionDir)src\inc;$(SolutionDir)src\inc\test;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<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>
|
||||
</Project>
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
|
||||
<PropertyGroup Label="Version">
|
||||
<!-- These fields are picked up by PackageES -->
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>4</VersionMinor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||
@@ -45,247 +40,27 @@
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-100.png">
|
||||
<Link>Images\LargeTile.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-125.png">
|
||||
<Link>Images\LargeTile.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-150.png">
|
||||
<Link>Images\LargeTile.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-200.png">
|
||||
<Link>Images\LargeTile.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-400.png">
|
||||
<Link>Images\LargeTile.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-100.png">
|
||||
<Link>Images\LockScreenLogo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-125.png">
|
||||
<Link>Images\LockScreenLogo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-150.png">
|
||||
<Link>Images\LockScreenLogo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-200.png">
|
||||
<Link>Images\LockScreenLogo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-400.png">
|
||||
<Link>Images\LockScreenLogo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-100.png">
|
||||
<Link>Images\SmallTile.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-125.png">
|
||||
<Link>Images\SmallTile.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-150.png">
|
||||
<Link>Images\SmallTile.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-200.png">
|
||||
<Link>Images\SmallTile.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-400.png">
|
||||
<Link>Images\SmallTile.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-100.png">
|
||||
<Link>Images\SplashScreen.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-125.png">
|
||||
<Link>Images\SplashScreen.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-150.png">
|
||||
<Link>Images\SplashScreen.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-200.png">
|
||||
<Link>Images\SplashScreen.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-400.png">
|
||||
<Link>Images\SplashScreen.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-100.png">
|
||||
<Link>Images\Square150x150Logo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-125.png">
|
||||
<Link>Images\Square150x150Logo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-150.png">
|
||||
<Link>Images\Square150x150Logo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-200.png">
|
||||
<Link>Images\Square150x150Logo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-400.png">
|
||||
<Link>Images\Square150x150Logo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-100.png">
|
||||
<Link>Images\Square44x44Logo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-125.png">
|
||||
<Link>Images\Square44x44Logo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-150.png">
|
||||
<Link>Images\Square44x44Logo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-200.png">
|
||||
<Link>Images\Square44x44Logo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-400.png">
|
||||
<Link>Images\Square44x44Logo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-16.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-16.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-16_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-16_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-20.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-20.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-20_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-20_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-24.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-24.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-24_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-24_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-256.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-256.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-256_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-256_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-30.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-30.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-30_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-30_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-32.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-32.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-32_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-32_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-36.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-36.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-36_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-36_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-40.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-40.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-40_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-40_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-48.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-48.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-48_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-48_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-60.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-60.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-60_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-60_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-64.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-64.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-64_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-64_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-72.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-72.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-72_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-72_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-80.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-80.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-80_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-80_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-96.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-96.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-96_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-96_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-100.png">
|
||||
<Link>Images\StoreLogo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-125.png">
|
||||
<Link>Images\StoreLogo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-150.png">
|
||||
<Link>Images\StoreLogo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-200.png">
|
||||
<Link>Images\StoreLogo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-400.png">
|
||||
<Link>Images\StoreLogo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-100.png">
|
||||
<Link>Images\Wide310x150Logo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-125.png">
|
||||
<Link>Images\Wide310x150Logo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-150.png">
|
||||
<Link>Images\Wide310x150Logo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-200.png">
|
||||
<Link>Images\Wide310x150Logo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-400.png">
|
||||
<Link>Images\Wide310x150Logo.scale-400.png</Link>
|
||||
</Content>
|
||||
<!-- Profile Icons -->
|
||||
<Content Include="ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-200.png" />
|
||||
<!-- Default Settings -->
|
||||
<Content Include="$(OpenConsoleDir)src\cascadia\TerminalApp\defaults.json">
|
||||
<Link>defaults.json</Link>
|
||||
</Content>
|
||||
<!-- Resources -->
|
||||
<!-- This resw only defines things that are used in this package's AppxManifest,
|
||||
so it's not in the common resource items. -->
|
||||
<PRIResource Include="Resources\en-US\Resources.resw" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\CascadiaResources.build.items" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.post.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
|
||||
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
|
||||
</ItemGroup>
|
||||
<!-- Stomp the path to conhost.exe.
|
||||
This task will copy OpenConsole.exe to the appx as conhost.exe, and place it
|
||||
adjacent to WindowsTerminal.exe.
|
||||
-->
|
||||
|
||||
<Target Name="OpenConsoleStompSourceProjectForWapProject" BeforeTargets="_ConvertItems">
|
||||
<ItemGroup>
|
||||
<!-- Stomp all "SourceProject" values for all incoming dependencies to flatten the package. -->
|
||||
<_TemporaryFilteredWapProjOutput Include="@(_FilteredNonWapProjProjectOutput)" />
|
||||
<_FilteredNonWapProjProjectOutput Remove="@(_TemporaryFilteredWapProjOutput)" />
|
||||
<_FilteredNonWapProjProjectOutput Include="@(_TemporaryFilteredWapProjOutput)">
|
||||
<!-- Override the filename for OpenConsole.exe (only) -->
|
||||
<TargetPath Condition="'%(Filename)' == 'OpenConsole' and '%(Extension)' == '.exe'">conhost.exe</TargetPath>
|
||||
<!-- Blank the SourceProject here to vend all files into the root of the package. -->
|
||||
<SourceProject>
|
||||
</SourceProject>
|
||||
@@ -335,4 +110,13 @@
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!-- This is required to get the package dependency in the AppXManifest. -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
|
||||
xmlns:uap7="http://schemas.microsoft.com/appx/manifest/uap/windows10/7"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap mp rescap">
|
||||
@@ -67,4 +69,12 @@
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
|
||||
<Extensions>
|
||||
<uap7:Extension Category="windows.sharedFonts">
|
||||
<uap7:SharedFonts>
|
||||
<uap4:Font File="Cascadia.ttf" />
|
||||
</uap7:SharedFonts>
|
||||
</uap7:Extension>
|
||||
</Extensions>
|
||||
</Package>
|
||||
|
||||
248
src/cascadia/CascadiaResources.build.items
Normal file
@@ -0,0 +1,248 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-100.png">
|
||||
<Link>Images\LargeTile.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-125.png">
|
||||
<Link>Images\LargeTile.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-150.png">
|
||||
<Link>Images\LargeTile.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-200.png">
|
||||
<Link>Images\LargeTile.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LargeTile.scale-400.png">
|
||||
<Link>Images\LargeTile.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-100.png">
|
||||
<Link>Images\LockScreenLogo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-125.png">
|
||||
<Link>Images\LockScreenLogo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-150.png">
|
||||
<Link>Images\LockScreenLogo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-200.png">
|
||||
<Link>Images\LockScreenLogo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\LockScreenLogo.scale-400.png">
|
||||
<Link>Images\LockScreenLogo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-100.png">
|
||||
<Link>Images\SmallTile.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-125.png">
|
||||
<Link>Images\SmallTile.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-150.png">
|
||||
<Link>Images\SmallTile.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-200.png">
|
||||
<Link>Images\SmallTile.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SmallTile.scale-400.png">
|
||||
<Link>Images\SmallTile.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-100.png">
|
||||
<Link>Images\SplashScreen.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-125.png">
|
||||
<Link>Images\SplashScreen.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-150.png">
|
||||
<Link>Images\SplashScreen.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-200.png">
|
||||
<Link>Images\SplashScreen.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\SplashScreen.scale-400.png">
|
||||
<Link>Images\SplashScreen.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-100.png">
|
||||
<Link>Images\Square150x150Logo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-125.png">
|
||||
<Link>Images\Square150x150Logo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-150.png">
|
||||
<Link>Images\Square150x150Logo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-200.png">
|
||||
<Link>Images\Square150x150Logo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square150x150Logo.scale-400.png">
|
||||
<Link>Images\Square150x150Logo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-100.png">
|
||||
<Link>Images\Square44x44Logo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-125.png">
|
||||
<Link>Images\Square44x44Logo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-150.png">
|
||||
<Link>Images\Square44x44Logo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-200.png">
|
||||
<Link>Images\Square44x44Logo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.scale-400.png">
|
||||
<Link>Images\Square44x44Logo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-16.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-16.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-16_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-16_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-20.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-20.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-20_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-20_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-24.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-24.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-24_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-24_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-256.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-256.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-256_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-256_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-30.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-30.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-30_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-30_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-32.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-32.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-32_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-32_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-36.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-36.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-36_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-36_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-40.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-40.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-40_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-40_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-48.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-48.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-48_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-48_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-60.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-60.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-60_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-60_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-64.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-64.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-64_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-64_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-72.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-72.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-72_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-72_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-80.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-80.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-80_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-80_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-96.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-96.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Square44x44Logo.targetsize-96_altform-unplated.png">
|
||||
<Link>Images\Square44x44Logo.targetsize-96_altform-unplated.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-100.png">
|
||||
<Link>Images\StoreLogo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-125.png">
|
||||
<Link>Images\StoreLogo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-150.png">
|
||||
<Link>Images\StoreLogo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-200.png">
|
||||
<Link>Images\StoreLogo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\StoreLogo.scale-400.png">
|
||||
<Link>Images\StoreLogo.scale-400.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-100.png">
|
||||
<Link>Images\Wide310x150Logo.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-125.png">
|
||||
<Link>Images\Wide310x150Logo.scale-125.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-150.png">
|
||||
<Link>Images\Wide310x150Logo.scale-150.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-200.png">
|
||||
<Link>Images\Wide310x150Logo.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(OpenConsoleDir)res\terminal\Wide310x150Logo.scale-400.png">
|
||||
<Link>Images\Wide310x150Logo.scale-400.png</Link>
|
||||
</Content>
|
||||
<!-- Fonts -->
|
||||
<Content Include="$(OpenConsoleDir)res\Cascadia.ttf" Condition="'$(WindowsTerminalReleaseBuild)'=='true'">
|
||||
<Link>Cascadia.ttf</Link>
|
||||
</Content>
|
||||
<!-- Profile Icons -->
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-100.png">
|
||||
<Link>ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-200.png">
|
||||
<Link>ProfileIcons\{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-100.png">
|
||||
<Link>ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-200.png">
|
||||
<Link>ProfileIcons\{574e775e-4f2a-5b96-ac1e-a2962a402336}.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-100.png">
|
||||
<Link>ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-200.png">
|
||||
<Link>ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-100.png">
|
||||
<Link>ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-200.png">
|
||||
<Link>ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-200.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-100.png">
|
||||
<Link>ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-100.png</Link>
|
||||
</Content>
|
||||
<Content Include="$(MSBuildThisFileDirectory)CascadiaPackage\ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-200.png">
|
||||
<Link>ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-200.png</Link>
|
||||
</Content>
|
||||
<!-- Default Settings -->
|
||||
<Content Include="$(MSBuildThisFileDirectory)\TerminalApp\defaults.json">
|
||||
<Link>defaults.json</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -98,6 +98,7 @@ namespace TerminalAppLocalTests
|
||||
"name": "scheme0",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010100",
|
||||
"red": "#010000",
|
||||
"green": "#000100",
|
||||
"blue": "#000001"
|
||||
@@ -106,6 +107,7 @@ namespace TerminalAppLocalTests
|
||||
"name": "scheme1",
|
||||
"foreground": "#020202",
|
||||
"background": "#030303",
|
||||
"selectionBackground": "#020200",
|
||||
"red": "#020000",
|
||||
|
||||
"blue": "#000002"
|
||||
@@ -114,6 +116,7 @@ namespace TerminalAppLocalTests
|
||||
"name": "scheme0",
|
||||
"foreground": "#040404",
|
||||
"background": "#050505",
|
||||
"selectionBackground": "#030300",
|
||||
"red": "#030000",
|
||||
"green": "#000300"
|
||||
})" };
|
||||
@@ -126,6 +129,8 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"scheme0", scheme0._schemeName);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 0), scheme0._selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 0), scheme0._table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0._table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 1), scheme0._table[XTERM_BLUE_ATTR]);
|
||||
@@ -136,6 +141,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 0), scheme0._selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 0, 0), scheme0._table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0._table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0._table[XTERM_BLUE_ATTR]);
|
||||
@@ -146,6 +152,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 0), scheme0._selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 0, 0), scheme0._table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 3, 0), scheme0._table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0._table[XTERM_BLUE_ATTR]);
|
||||
@@ -169,7 +176,7 @@ namespace TerminalAppLocalTests
|
||||
"background": "#050505"
|
||||
})" };
|
||||
const std::string scheme3String{ R"({
|
||||
// "name": "scheme3",
|
||||
// by not providing a name, the scheme will have the name ""
|
||||
"foreground": "#060606",
|
||||
"background": "#070707"
|
||||
})" };
|
||||
@@ -188,47 +195,85 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
|
||||
settings._LayerOrCreateColorScheme(scheme0Json);
|
||||
VERIFY_ARE_EQUAL(1u, settings._globals.GetColorSchemes().size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), settings._globals.GetColorSchemes().at(0)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), settings._globals.GetColorSchemes().at(0)._defaultBackground);
|
||||
{
|
||||
for (auto& kv : settings._globals._colorSchemes)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"kv:%s->%s", kv.first.data(), kv.second.GetName().data()));
|
||||
}
|
||||
VERIFY_ARE_EQUAL(1u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0 = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0._defaultBackground);
|
||||
}
|
||||
|
||||
settings._LayerOrCreateColorScheme(scheme1Json);
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals.GetColorSchemes().size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), settings._globals.GetColorSchemes().at(0)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), settings._globals.GetColorSchemes().at(0)._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), settings._globals.GetColorSchemes().at(1)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), settings._globals.GetColorSchemes().at(1)._defaultBackground);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0 = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme1") != settings._globals._colorSchemes.end());
|
||||
auto scheme1 = settings._globals._colorSchemes.find(L"scheme1")->second;
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1._defaultBackground);
|
||||
}
|
||||
settings._LayerOrCreateColorScheme(scheme2Json);
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals.GetColorSchemes().size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), settings._globals.GetColorSchemes().at(0)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), settings._globals.GetColorSchemes().at(0)._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), settings._globals.GetColorSchemes().at(1)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), settings._globals.GetColorSchemes().at(1)._defaultBackground);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0 = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme1") != settings._globals._colorSchemes.end());
|
||||
auto scheme1 = settings._globals._colorSchemes.find(L"scheme1")->second;
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1._defaultBackground);
|
||||
}
|
||||
settings._LayerOrCreateColorScheme(scheme3Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings._globals.GetColorSchemes().size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), settings._globals.GetColorSchemes().at(0)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), settings._globals.GetColorSchemes().at(0)._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), settings._globals.GetColorSchemes().at(1)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), settings._globals.GetColorSchemes().at(1)._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 6, 6), settings._globals.GetColorSchemes().at(2)._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 7, 7, 7), settings._globals.GetColorSchemes().at(2)._defaultBackground);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(3u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0 = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme1") != settings._globals._colorSchemes.end());
|
||||
auto scheme1 = settings._globals._colorSchemes.find(L"scheme1")->second;
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"") != settings._globals._colorSchemes.end());
|
||||
auto scheme2 = settings._globals._colorSchemes.find(L"")->second;
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1._defaultBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 6, 6), scheme2._defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 7, 7, 7), scheme2._defaultBackground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
@@ -35,11 +37,33 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(LayerKeybindings);
|
||||
TEST_METHOD(UnbindKeybindings);
|
||||
|
||||
TEST_METHOD(TestArbitraryArgs);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - This is a helper to retrieve the ActionAndArgs from the keybindings
|
||||
// for a given chord.
|
||||
// Arguments:
|
||||
// - bindings: The AppKeyBindings to lookup the ActionAndArgs from.
|
||||
// - kc: The key chord to look up the bound ActionAndArgs for.
|
||||
// Return Value:
|
||||
// - The ActionAndArgs bound to the given key, or nullptr if nothing is bound to it.
|
||||
static const ActionAndArgs KeyBindingsTests::GetActionAndArgs(const implementation::AppKeyBindings& bindings,
|
||||
const KeyChord& kc)
|
||||
{
|
||||
const auto keyIter = bindings._keyShortcuts.find(kc);
|
||||
VERIFY_IS_TRUE(keyIter != bindings._keyShortcuts.end(), L"Expected to find an action bound to the given KeyChord");
|
||||
if (keyIter != bindings._keyShortcuts.end())
|
||||
{
|
||||
return keyIter->second;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
};
|
||||
|
||||
void KeyBindingsTests::ManyKeysSameAction()
|
||||
@@ -156,4 +180,189 @@ namespace TerminalAppLocalTests
|
||||
appKeyBindings->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestArbitraryArgs()
|
||||
{
|
||||
const std::string bindings0String{ R"([
|
||||
{ "command": "copy", "keys": ["ctrl+c"] },
|
||||
{ "command": "copyTextWithoutNewlines", "keys": ["alt+c"] },
|
||||
{ "command": { "action": "copy", "trimWhitespace": false }, "keys": ["ctrl+shift+c"] },
|
||||
{ "command": { "action": "copy", "trimWhitespace": true }, "keys": ["alt+shift+c"] },
|
||||
|
||||
{ "command": "newTab", "keys": ["ctrl+t"] },
|
||||
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+t"] },
|
||||
{ "command": "newTabProfile0", "keys": ["alt+shift+t"] },
|
||||
{ "command": { "action": "newTab", "index": 11 }, "keys": ["ctrl+shift+y"] },
|
||||
{ "command": "newTabProfile8", "keys": ["alt+shift+y"] },
|
||||
|
||||
{ "command": { "action": "copy", "madeUpBool": true }, "keys": ["ctrl+b"] },
|
||||
{ "command": { "action": "copy" }, "keys": ["ctrl+shift+b"] },
|
||||
|
||||
{ "command": "decreaseFontSize", "keys": ["ctrl+-"] },
|
||||
{ "command": "increaseFontSize", "keys": ["ctrl+="] }
|
||||
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto appKeyBindings = winrt::make_self<implementation::AppKeyBindings>();
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(13u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` without args parses as Copy(TrimWhitespace=true)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copyTextWithoutNewlines` parses as Copy(TrimWhitespace=false)"));
|
||||
KeyChord kc{ false, true, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` with args parses them correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_FALSE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` with args parses them correctly"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` without args parses as NewTab(Index=null)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.ProfileIndex());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` parses args correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(0, realArgs.ProfileIndex().Value());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTabProfile0` parses as NewTab(Index=0)"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTabProfile0, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(0, realArgs.ProfileIndex().Value());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` with an index greater than the legacy "
|
||||
L"args afforded parses correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('Y') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(11, realArgs.ProfileIndex().Value());
|
||||
}
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTabProfile8` parses as NewTab(Index=8)"));
|
||||
KeyChord kc{ false, true, true, static_cast<int32_t>('Y') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTabProfile8, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NOT_NULL(realArgs.ProfileIndex());
|
||||
VERIFY_ARE_EQUAL(8, realArgs.ProfileIndex().Value());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` ignores args it doesn't understand"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` null as it's `args` parses as the default option"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('B') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `increaseFontSize` without args parses as AdjustFontSize(Delta=1)"));
|
||||
KeyChord kc{ false, true, false, static_cast<int32_t>('=') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::IncreaseFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(1, realArgs.Delta());
|
||||
}
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `decreaseFontSize` without args parses as AdjustFontSize(Delta=-1)"));
|
||||
KeyChord kc{ false, true, false, static_cast<int32_t>('-') };
|
||||
auto actionAndArgs = GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::DecreaseFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(-1, realArgs.Delta());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -95,7 +95,8 @@ namespace TerminalAppLocalTests
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101"
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010101"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name": "profile1",
|
||||
@@ -106,7 +107,8 @@ namespace TerminalAppLocalTests
|
||||
const std::string profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#030303"
|
||||
"foreground": "#030303",
|
||||
"selectionBackground": "#020202"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
@@ -120,6 +122,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(profile0._defaultBackground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), profile0._defaultBackground.value());
|
||||
|
||||
VERIFY_IS_TRUE(profile0._selectionBackground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), profile0._selectionBackground.value());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", profile0._name);
|
||||
|
||||
VERIFY_IS_FALSE(profile0._startingDirectory.has_value());
|
||||
@@ -134,6 +139,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(profile0._defaultBackground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), profile0._defaultBackground.value());
|
||||
|
||||
VERIFY_IS_TRUE(profile0._selectionBackground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), profile0._selectionBackground.value());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile1", profile0._name);
|
||||
|
||||
VERIFY_IS_TRUE(profile0._startingDirectory.has_value());
|
||||
@@ -149,6 +157,9 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(profile0._defaultBackground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), profile0._defaultBackground.value());
|
||||
|
||||
VERIFY_IS_TRUE(profile0._selectionBackground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), profile0._selectionBackground.value());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile2", profile0._name);
|
||||
|
||||
VERIFY_IS_TRUE(profile0._startingDirectory.has_value());
|
||||
|
||||
@@ -47,6 +47,13 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(TestReorderingWithoutGuid);
|
||||
TEST_METHOD(TestLayeringNameOnlyProfiles);
|
||||
TEST_METHOD(TestExplodingNameOnlyProfiles);
|
||||
TEST_METHOD(TestHideAllProfiles);
|
||||
TEST_METHOD(TestInvalidColorSchemeName);
|
||||
|
||||
TEST_METHOD(TestLayerGlobalsOnRoot);
|
||||
|
||||
TEST_METHOD(TestProfileIconWithEnvVar);
|
||||
TEST_METHOD(TestProfileBackgroundImageWithEnvVar);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
@@ -392,9 +399,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
settings->_ValidateSettings();
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, settings->_warnings.size());
|
||||
VERIFY_ARE_EQUAL(3u, settings->_warnings.size());
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0));
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(1));
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme, settings->_warnings.at(2));
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings->_profiles.size());
|
||||
VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid());
|
||||
@@ -792,6 +800,9 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{ "name": "Campbell" }
|
||||
]
|
||||
})" };
|
||||
|
||||
@@ -1125,4 +1136,282 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(L"Windows PowerShell", settings._profiles.at(3)._name);
|
||||
VERIFY_ARE_EQUAL(L"cmd", settings._profiles.at(4)._name);
|
||||
}
|
||||
|
||||
void SettingsTests::TestHideAllProfiles()
|
||||
{
|
||||
const std::string settingsWithProfiles{ R"(
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const std::string settingsWithoutProfiles{ R"(
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"hidden": true
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settingsWithProfiles);
|
||||
VerifyParseSucceeded(settingsWithoutProfiles);
|
||||
|
||||
{
|
||||
// Case 1: Good settings
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settingsWithProfiles, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
settings._RemoveHiddenProfiles();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"settingsWithProfiles successfully parsed and validated"));
|
||||
VERIFY_ARE_EQUAL(1u, settings._profiles.size());
|
||||
}
|
||||
{
|
||||
// Case 2: Bad settings
|
||||
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settingsWithoutProfiles, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
bool caughtExpectedException = false;
|
||||
try
|
||||
{
|
||||
settings._RemoveHiddenProfiles();
|
||||
}
|
||||
catch (const ::TerminalApp::SettingsException& ex)
|
||||
{
|
||||
VERIFY_IS_TRUE(ex.Error() == ::TerminalApp::SettingsLoadErrors::AllProfilesHidden);
|
||||
caughtExpectedException = true;
|
||||
}
|
||||
VERIFY_IS_TRUE(caughtExpectedException);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsTests::TestInvalidColorSchemeName()
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Ensure that setting a profile's scheme to a non-existent scheme causes a warning."));
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"colorScheme": "schemeOne"
|
||||
},
|
||||
{
|
||||
"name" : "profile1",
|
||||
"colorScheme": "InvalidSchemeName"
|
||||
},
|
||||
{
|
||||
"name" : "profile2"
|
||||
// Will use the Profile default value, "Campbell"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{
|
||||
"name": "schemeOne",
|
||||
"foreground": "#111111"
|
||||
},
|
||||
{
|
||||
"name": "schemeTwo",
|
||||
"foreground": "#222222"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settings0String);
|
||||
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings._profiles.size());
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals._colorSchemes.size());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"schemeOne", settings._profiles.at(0)._schemeName.value());
|
||||
VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings._profiles.at(1)._schemeName.value());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings._profiles.at(2)._schemeName.value());
|
||||
|
||||
settings._ValidateAllSchemesExist();
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings._warnings.size());
|
||||
VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme, settings._warnings.at(0));
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings._profiles.size());
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals._colorSchemes.size());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"schemeOne", settings._profiles.at(0)._schemeName.value());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings._profiles.at(1)._schemeName.value());
|
||||
VERIFY_ARE_EQUAL(L"Campbell", settings._profiles.at(2)._schemeName.value());
|
||||
}
|
||||
|
||||
void SettingsTests::TestLayerGlobalsOnRoot()
|
||||
{
|
||||
// Test for microsoft/terminal#2906. We added the ability for the root
|
||||
// to be used as the globals object in #2515. However, if you have a
|
||||
// globals object, then the settings in the root would get ignored.
|
||||
// This test ensures that settings from a child "globals" element
|
||||
// _layer_ on top of root properties, and they don't cause the root
|
||||
// properties to be totally ignored.
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 123
|
||||
}
|
||||
})" };
|
||||
const std::string settings1String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 234
|
||||
})" };
|
||||
const std::string settings2String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"initialRows": 345,
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
// initialRows should not be cleared here
|
||||
}
|
||||
})" };
|
||||
const std::string settings3String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"globals": {
|
||||
"initialRows": 456
|
||||
// defaultProfile should not be cleared here
|
||||
}
|
||||
})" };
|
||||
const std::string settings4String{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string settings5String{ R"(
|
||||
{
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
},
|
||||
"defaultProfile": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
|
||||
"globals": {
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settings0String);
|
||||
VerifyParseSucceeded(settings1String);
|
||||
VerifyParseSucceeded(settings2String);
|
||||
VerifyParseSucceeded(settings3String);
|
||||
VerifyParseSucceeded(settings4String);
|
||||
VerifyParseSucceeded(settings5String);
|
||||
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}");
|
||||
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(123, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings1String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(234, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings2String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(345, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings3String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid2, settings._globals._defaultProfile);
|
||||
VERIFY_ARE_EQUAL(456, settings._globals._initialRows);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings4String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid1, settings._globals._defaultProfile);
|
||||
}
|
||||
{
|
||||
CascadiaSettings settings;
|
||||
settings._ParseJsonString(settings5String, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_ARE_EQUAL(guid3, settings._globals._defaultProfile);
|
||||
}
|
||||
}
|
||||
void SettingsTests::TestProfileIconWithEnvVar()
|
||||
{
|
||||
const auto expectedPath = wil::ExpandEnvironmentStringsW<std::wstring>(L"%WINDIR%\\System32\\x_80.png");
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"icon": "%WINDIR%\\System32\\x_80.png"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settingsJson);
|
||||
CascadiaSettings settings{};
|
||||
settings._ParseJsonString(settingsJson, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_IS_FALSE(settings._profiles.empty(), 0);
|
||||
VERIFY_ARE_EQUAL(expectedPath, settings._profiles[0].GetExpandedIconPath());
|
||||
}
|
||||
void SettingsTests::TestProfileBackgroundImageWithEnvVar()
|
||||
{
|
||||
const auto expectedPath = wil::ExpandEnvironmentStringsW<std::wstring>(L"%WINDIR%\\System32\\x_80.png");
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"backgroundImage": "%WINDIR%\\System32\\x_80.png"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
VerifyParseSucceeded(settingsJson);
|
||||
CascadiaSettings settings{};
|
||||
settings._ParseJsonString(settingsJson, false);
|
||||
settings.LayerJson(settings._userSettings);
|
||||
VERIFY_IS_FALSE(settings._profiles.empty(), 0);
|
||||
|
||||
GlobalAppSettings globalSettings{};
|
||||
auto terminalSettings = settings._profiles[0].CreateTerminalSettings(globalSettings.GetColorSchemes());
|
||||
VERIFY_ARE_EQUAL(expectedPath, terminalSettings.BackgroundImage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{CA5CAD1A-b11c-4ddb-a4fe-c3afae9b5506}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>TerminalAppLocalTests</RootNamespace>
|
||||
<ProjectName>LocalTests_TerminalApp</ProjectName>
|
||||
<TargetName>TerminalApp.LocalTests</TargetName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<!-- We'll manage our own OutDir/IntDir -->
|
||||
<NoOutputRedirection>true</NoOutputRedirection>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Manually change our outdir to be in a subdirectory. We don't really want
|
||||
to put our output in the bin root, because if we do, we'll copy
|
||||
TerminalApp.winmd to the bin root, and then every subsequent mdmerge step
|
||||
(in _any_ cppwinrt project) will automatically try to pick up
|
||||
TerminalApp.winmd as a dependency (which is just wrong). This MUST be done
|
||||
before importing common.build.pre.props -->
|
||||
<OutDir>$(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
|
||||
<IntDir>$(SolutionDir)obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)\src\common.build.pre.props" />
|
||||
|
||||
@@ -40,15 +64,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Globals ======================== -->
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{CA5CAD1A-b11c-4ddb-a4fe-c3afae9b5506}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>TerminalAppLocalTests</RootNamespace>
|
||||
<ProjectName>LocalTests_TerminalApp</ProjectName>
|
||||
<TargetName>TerminalApp.LocalTests</TargetName>
|
||||
<WindowsTargetPlatformMinVersion>10.0.18362.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- ====================== Compiler & Linker Flags ===================== -->
|
||||
<ItemDefinitionGroup>
|
||||
@@ -70,31 +85,19 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.dll.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Manually change our outdir to be in a subdirectory. We don't really want
|
||||
to put our output in the bin root, because if we do, we'll copy
|
||||
TerminalApp.winmd to the bin root, and then every subsequent mdmerge step
|
||||
(in _any_ cppwinrt project) will automatically try to pick up
|
||||
TerminalApp.winmd as a dependency (which is just wrong). This MUST be done
|
||||
after importing common.build.post.props-->
|
||||
<OutDir>$(OpenConsoleDir)\bin\$(Platform)\$(Configuration)\$(ProjectName)\</OutDir>
|
||||
<IntDir>$(OpenConsoleDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<_CppWinrtBinRoot>"$(OpenConsoleDir)$(Platform)\$(Configuration)\"</_CppWinrtBinRoot>
|
||||
<!-- From Microsoft.UI.Xaml.targets -->
|
||||
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
|
||||
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- We actually can just straight up reference MUX here, it's fine -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.191203001-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
|
||||
<!-- This project will generate individual sxs manifests for each of our winrt libraries -->
|
||||
|
||||
415
src/cascadia/PublicTerminalCore/HwndTerminal.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "HwndTerminal.hpp"
|
||||
#include <DefaultSettings.h>
|
||||
#include "../../renderer/base/Renderer.hpp"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
#include "../../types/viewport.cpp"
|
||||
#include "../../types/inc/GlyphWidth.hpp"
|
||||
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
|
||||
static LPCWSTR term_window_class = L"HwndTerminalClass";
|
||||
|
||||
static LRESULT CALLBACK HwndTerminalWndProc(
|
||||
HWND hwnd,
|
||||
UINT uMsg,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam) noexcept
|
||||
{
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
static bool RegisterTermClass(HINSTANCE hInstance) noexcept
|
||||
{
|
||||
WNDCLASSW wc;
|
||||
if (GetClassInfoW(hInstance, term_window_class, &wc))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = HwndTerminalWndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = nullptr;
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hbrBackground = nullptr;
|
||||
wc.lpszMenuName = nullptr;
|
||||
wc.lpszClassName = term_window_class;
|
||||
|
||||
return RegisterClassW(&wc) != 0;
|
||||
}
|
||||
|
||||
HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_desiredFont{ DEFAULT_FONT_FACE.c_str(), 0, 10, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ DEFAULT_FONT_FACE.c_str(), 0, 10, { 0, 14 }, CP_UTF8, false }
|
||||
{
|
||||
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
|
||||
|
||||
if (RegisterTermClass(hInstance))
|
||||
{
|
||||
_hwnd = wil::unique_hwnd(CreateWindowExW(
|
||||
0,
|
||||
term_window_class,
|
||||
nullptr,
|
||||
WS_CHILD |
|
||||
WS_CLIPCHILDREN |
|
||||
WS_CLIPSIBLINGS |
|
||||
WS_VISIBLE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
parentHwnd,
|
||||
nullptr,
|
||||
hInstance,
|
||||
nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT HwndTerminal::Initialize()
|
||||
{
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
||||
auto* const localPointerToThread = renderThread.get();
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
|
||||
RETURN_HR_IF_NULL(E_POINTER, localPointerToThread);
|
||||
RETURN_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
RETURN_IF_FAILED(dxEngine->SetHwnd(_hwnd.get()));
|
||||
RETURN_IF_FAILED(dxEngine->Enable());
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
|
||||
const auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1);
|
||||
SetGlyphWidthFallback(pfn);
|
||||
|
||||
_UpdateFont(USER_DEFAULT_SCREEN_DPI);
|
||||
RECT windowRect;
|
||||
GetWindowRect(_hwnd.get(), &windowRect);
|
||||
|
||||
const COORD windowSize{ gsl::narrow<short>(windowRect.right - windowRect.left), gsl::narrow<short>(windowRect.bottom - windowRect.top) };
|
||||
|
||||
// Fist set up the dx engine with the window size in pixels.
|
||||
// Then, using the font, get the number of characters that can fit.
|
||||
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
|
||||
RETURN_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
|
||||
_renderEngine = std::move(dxEngine);
|
||||
|
||||
_terminal->SetBackgroundCallback([](auto) {});
|
||||
|
||||
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetDefaultBackground(RGB(5, 27, 80));
|
||||
_terminal->SetDefaultForeground(RGB(255, 255, 255));
|
||||
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> callback)
|
||||
{
|
||||
_terminal->SetScrollPositionChangedCallback(callback);
|
||||
}
|
||||
|
||||
void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
|
||||
{
|
||||
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept {
|
||||
const wchar_t* text = input.c_str();
|
||||
const size_t textChars = wcslen(text) + 1;
|
||||
const size_t textBytes = textChars * sizeof(wchar_t);
|
||||
wchar_t* callingText = nullptr;
|
||||
|
||||
callingText = static_cast<wchar_t*>(::CoTaskMemAlloc(textBytes));
|
||||
|
||||
if (callingText == nullptr)
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
wcscpy_s(callingText, textChars, text);
|
||||
|
||||
callback(callingText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void HwndTerminal::_UpdateFont(int newDpi)
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
|
||||
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
|
||||
// actually fail. We need a way to gracefully fallback.
|
||||
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
|
||||
}
|
||||
|
||||
HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, dimensions);
|
||||
|
||||
auto lock = _terminal->LockForWriting();
|
||||
|
||||
RETURN_IF_FAILED(_renderEngine->SetWindowSize(windowSize));
|
||||
|
||||
// Invalidate everything
|
||||
_renderer->TriggerRedrawAll();
|
||||
|
||||
// Convert our new dimensions to characters
|
||||
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
|
||||
{ gsl::narrow<short>(windowSize.cx), gsl::narrow<short>(windowSize.cy) });
|
||||
const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
|
||||
|
||||
// If this function succeeds with S_FALSE, then the terminal didn't
|
||||
// actually change size. No need to notify the connection of this
|
||||
// no-op.
|
||||
// TODO: MSFT:20642295 Resizing the buffer will corrupt it
|
||||
// I believe we'll need support for CSI 2J, and additionally I think
|
||||
// we're resetting the viewport to the top
|
||||
RETURN_IF_FAILED(_terminal->UserResize({ vp.Width(), vp.Height() }));
|
||||
dimensions->X = vp.Width();
|
||||
dimensions->Y = vp.Height();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void HwndTerminal::SendOutput(std::wstring_view data)
|
||||
{
|
||||
_terminal->Write(data);
|
||||
}
|
||||
|
||||
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
|
||||
{
|
||||
auto _terminal = std::make_unique<HwndTerminal>(parentHwnd);
|
||||
RETURN_IF_FAILED(_terminal->Initialize());
|
||||
|
||||
*hwnd = _terminal->_hwnd.get();
|
||||
*terminal = _terminal.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void _stdcall TerminalRegisterScrollCallback(void* terminal, void __stdcall callback(int, int, int))
|
||||
{
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->RegisterScrollCallback(callback);
|
||||
}
|
||||
|
||||
void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*))
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->RegisterWriteCallback(callback);
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->SendOutput(data);
|
||||
}
|
||||
|
||||
HRESULT _stdcall TerminalTriggerResize(void* terminal, double width, double height, _Out_ COORD* dimensions)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
|
||||
const SIZE windowSize{ static_cast<short>(width), static_cast<short>(height) };
|
||||
return publicTerminal->Refresh(windowSize, dimensions);
|
||||
}
|
||||
|
||||
void _stdcall TerminalDpiChanged(void* terminal, int newDpi)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
}
|
||||
|
||||
void _stdcall TerminalUserScroll(void* terminal, int viewTop)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->UserScrollViewport(viewTop);
|
||||
}
|
||||
|
||||
HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed)
|
||||
{
|
||||
COORD terminalPosition = { cursorPosition };
|
||||
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
const auto fontSize = publicTerminal->_actualFont.GetSize();
|
||||
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);
|
||||
|
||||
terminalPosition.X /= fontSize.X;
|
||||
terminalPosition.Y /= fontSize.Y;
|
||||
|
||||
publicTerminal->_terminal->SetSelectionAnchor(terminalPosition);
|
||||
publicTerminal->_terminal->SetBoxSelection(altPressed);
|
||||
|
||||
publicTerminal->_renderer->TriggerSelection();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition)
|
||||
{
|
||||
COORD terminalPosition = { cursorPosition };
|
||||
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
const auto fontSize = publicTerminal->_actualFont.GetSize();
|
||||
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
|
||||
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);
|
||||
|
||||
terminalPosition.X /= fontSize.X;
|
||||
terminalPosition.Y /= fontSize.Y;
|
||||
|
||||
publicTerminal->_terminal->SetEndSelectionPosition(terminalPosition);
|
||||
publicTerminal->_renderer->TriggerSelection();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void _stdcall TerminalClearSelection(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->ClearSelection();
|
||||
}
|
||||
bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
const bool selectionActive = publicTerminal->_terminal->IsSelectionActive();
|
||||
return selectionActive;
|
||||
}
|
||||
|
||||
// Returns the selected text in the terminal.
|
||||
const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
|
||||
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
|
||||
// convert text: vector<string> --> string
|
||||
std::wstring selectedText;
|
||||
for (const auto& text : bufferData.text)
|
||||
{
|
||||
selectedText += text;
|
||||
}
|
||||
|
||||
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
|
||||
TerminalClearSelection(terminal);
|
||||
|
||||
return returnText.release();
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
const auto scanCode = MapVirtualKeyW((UINT)wParam, MAPVK_VK_TO_VSC);
|
||||
struct KeyModifier
|
||||
{
|
||||
int vkey;
|
||||
ControlKeyStates flags;
|
||||
};
|
||||
|
||||
constexpr std::array<KeyModifier, 5> modifiers{ {
|
||||
{ VK_RMENU, ControlKeyStates::RightAltPressed },
|
||||
{ VK_LMENU, ControlKeyStates::LeftAltPressed },
|
||||
{ VK_RCONTROL, ControlKeyStates::RightCtrlPressed },
|
||||
{ VK_LCONTROL, ControlKeyStates::LeftCtrlPressed },
|
||||
{ VK_SHIFT, ControlKeyStates::ShiftPressed },
|
||||
} };
|
||||
|
||||
ControlKeyStates flags;
|
||||
|
||||
for (const auto& mod : modifiers)
|
||||
{
|
||||
const auto state = GetKeyState(mod.vkey);
|
||||
const auto isDown = state < 0;
|
||||
|
||||
if (isDown)
|
||||
{
|
||||
flags |= mod.flags;
|
||||
}
|
||||
}
|
||||
|
||||
publicTerminal->_terminal->SendKeyEvent((WORD)wParam, (WORD)scanCode, flags);
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch)
|
||||
{
|
||||
if (ch == '\t')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->SendCharEvent(ch);
|
||||
}
|
||||
|
||||
void _stdcall DestroyTerminal(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
delete publicTerminal;
|
||||
}
|
||||
|
||||
// Updates the terminal font type, size, color, as well as the background/foreground colors to a specified theme.
|
||||
void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
{
|
||||
auto lock = publicTerminal->_terminal->LockForWriting();
|
||||
|
||||
publicTerminal->_terminal->SetDefaultForeground(theme.DefaultForeground);
|
||||
publicTerminal->_terminal->SetDefaultBackground(theme.DefaultBackground);
|
||||
|
||||
// Set the font colors
|
||||
for (size_t tableIndex = 0; tableIndex < 16; tableIndex++)
|
||||
{
|
||||
// It's using gsl::at to check the index is in bounds, but the analyzer still calls this array-to-pointer-decay
|
||||
[[gsl::suppress(bounds .3)]] publicTerminal->_terminal->SetColorTableEntry(tableIndex, gsl::at(theme.ColorTable, tableIndex));
|
||||
}
|
||||
}
|
||||
|
||||
publicTerminal->_terminal->SetCursorStyle(theme.CursorStyle);
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, 10, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
|
||||
// When the font changes the terminal dimensions need to be recalculated since the available row and column
|
||||
// space will have changed.
|
||||
RECT windowRect;
|
||||
GetWindowRect(publicTerminal->_hwnd.get(), &windowRect);
|
||||
|
||||
COORD dimensions = {};
|
||||
const SIZE windowSize{ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top };
|
||||
publicTerminal->Refresh(windowSize, &dimensions);
|
||||
}
|
||||
|
||||
// Resizes the terminal to the specified rows and columns.
|
||||
HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
|
||||
return publicTerminal->_terminal->UserResize(dimensions);
|
||||
}
|
||||
|
||||
void _stdcall TerminalBlinkCursor(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
if (!publicTerminal->_terminal->IsCursorBlinkingAllowed() && publicTerminal->_terminal->IsCursorVisible())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
publicTerminal->_terminal->SetCursorVisible(!publicTerminal->_terminal->IsCursorVisible());
|
||||
}
|
||||
|
||||
void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->SetCursorVisible(visible);
|
||||
}
|
||||
77
src/cascadia/PublicTerminalCore/HwndTerminal.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../renderer/base/Renderer.hpp"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
|
||||
typedef struct _TerminalTheme
|
||||
{
|
||||
COLORREF DefaultBackground;
|
||||
COLORREF DefaultForeground;
|
||||
DispatchTypes::CursorStyle CursorStyle;
|
||||
COLORREF ColorTable[16];
|
||||
} TerminalTheme, *LPTerminalTheme;
|
||||
|
||||
extern "C" {
|
||||
__declspec(dllexport) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSendOutput(void* terminal, LPCWSTR data);
|
||||
__declspec(dllexport) void _stdcall TerminalRegisterScrollCallback(void* terminal, void __stdcall callback(int, int, int));
|
||||
__declspec(dllexport) HRESULT _stdcall TerminalTriggerResize(void* terminal, double width, double height, _Out_ COORD* dimensions);
|
||||
__declspec(dllexport) HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
|
||||
__declspec(dllexport) void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
|
||||
__declspec(dllexport) void _stdcall TerminalUserScroll(void* terminal, int viewTop);
|
||||
__declspec(dllexport) HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
|
||||
__declspec(dllexport) HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
|
||||
__declspec(dllexport) void _stdcall TerminalClearSelection(void* terminal);
|
||||
__declspec(dllexport) const wchar_t* _stdcall TerminalGetSelection(void* terminal);
|
||||
__declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch);
|
||||
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
};
|
||||
|
||||
struct HwndTerminal
|
||||
{
|
||||
public:
|
||||
HwndTerminal(HWND hwnd);
|
||||
HRESULT Initialize();
|
||||
void SendOutput(std::wstring_view data);
|
||||
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
|
||||
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
|
||||
void RegisterWriteCallback(const void _stdcall callback(wchar_t*));
|
||||
|
||||
private:
|
||||
wil::unique_hwnd _hwnd;
|
||||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
|
||||
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
|
||||
|
||||
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer;
|
||||
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;
|
||||
|
||||
friend HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
|
||||
friend HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
|
||||
friend void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
|
||||
friend void _stdcall TerminalUserScroll(void* terminal, int viewTop);
|
||||
friend HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
|
||||
friend HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
|
||||
friend void _stdcall TerminalClearSelection(void* terminal);
|
||||
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
|
||||
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WPARAM wParam);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch);
|
||||
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
friend void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
void _UpdateFont(int newDpi);
|
||||
};
|
||||
57
src/cascadia/PublicTerminalCore/PublicTerminalCore.vcxproj
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{84848BFA-931D-42CE-9ADF-01EE54DE7890}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>PublicTerminalCore</RootNamespace>
|
||||
<ProjectName>PublicTerminalCore</ProjectName>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HwndTerminal.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="HwndTerminal.hpp" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\terminal\input\lib\terminalinput.vcxproj">
|
||||
<Project>{1cf55140-ef6a-4736-a403-957e4f7430bb}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\TerminalCore\lib\TerminalCore-lib.vcxproj">
|
||||
<Project>{ca5cad1a-abcd-429c-b551-8562ec954746}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj">
|
||||
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\buffer\out\lib\bufferout.vcxproj">
|
||||
<Project>{0cf235bd-2da0-407e-90ee-c467e8bbc714}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\terminal\parser\lib\parser.vcxproj">
|
||||
<Project>{3ae13314-1939-4dfa-9c14-38ca0834050c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="HwndTerminal.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="HwndTerminal.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
4
src/cascadia/PublicTerminalCore/pch.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
4
src/cascadia/PublicTerminalCore/pch.h
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include <LibraryIncludes.h>
|
||||
11
src/cascadia/TerminalApp/ActionAndArgs.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "pch.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "ActionAndArgs.g.cpp"
|
||||
|
||||
// We define everything necessary for the ActionAndArgs class in the header, but
|
||||
// we still need this file to compile the ActionAndArgs.g.cpp file, and we can't
|
||||
// just include that file in the header.
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
}
|
||||
18
src/cascadia/TerminalApp/ActionAndArgs.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "ActionAndArgs.g.h"
|
||||
#include "..\inc\cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct ActionAndArgs : public ActionAndArgsT<ActionAndArgs>
|
||||
{
|
||||
ActionAndArgs() = default;
|
||||
GETSET_PROPERTY(TerminalApp::ShortcutAction, Action, TerminalApp::ShortcutAction::Invalid);
|
||||
GETSET_PROPERTY(IActionArgs, Args, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ActionAndArgs);
|
||||
}
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
#include "ActionEventArgs.g.cpp"
|
||||
#include "CopyTextArgs.g.cpp"
|
||||
#include "NewTabWithProfileArgs.g.cpp"
|
||||
#include "NewTabArgs.g.cpp"
|
||||
#include "SwitchToTabArgs.g.cpp"
|
||||
#include "ResizePaneArgs.g.cpp"
|
||||
#include "MoveFocusArgs.g.cpp"
|
||||
#include "AdjustFontSizeArgs.g.cpp"
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
// *.g.cpp to ActionArgs.cpp!
|
||||
#include "ActionEventArgs.g.h"
|
||||
#include "CopyTextArgs.g.h"
|
||||
#include "NewTabWithProfileArgs.g.h"
|
||||
#include "NewTabArgs.g.h"
|
||||
#include "SwitchToTabArgs.g.h"
|
||||
#include "ResizePaneArgs.g.h"
|
||||
#include "MoveFocusArgs.g.h"
|
||||
#include "AdjustFontSizeArgs.g.h"
|
||||
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// Notes on defining ActionArgs and ActionEventArgs:
|
||||
// * All properties specific to an action should be defined as an ActionArgs
|
||||
@@ -34,33 +36,211 @@ namespace winrt::TerminalApp::implementation
|
||||
struct CopyTextArgs : public CopyTextArgsT<CopyTextArgs>
|
||||
{
|
||||
CopyTextArgs() = default;
|
||||
GETSET_PROPERTY(bool, TrimWhitespace, false);
|
||||
GETSET_PROPERTY(bool, TrimWhitespace, true);
|
||||
|
||||
static constexpr std::string_view TrimWhitespaceKey{ "trimWhitespace" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<CopyTextArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_TrimWhitespace == _TrimWhitespace;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
if (auto trimWhitespace{ json[JsonKey(TrimWhitespaceKey)] })
|
||||
{
|
||||
args->_TrimWhitespace = trimWhitespace.asBool();
|
||||
}
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
|
||||
struct NewTabWithProfileArgs : public NewTabWithProfileArgsT<NewTabWithProfileArgs>
|
||||
struct NewTabArgs : public NewTabArgsT<NewTabArgs>
|
||||
{
|
||||
NewTabWithProfileArgs() = default;
|
||||
GETSET_PROPERTY(int32_t, ProfileIndex, 0);
|
||||
NewTabArgs() = default;
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<int32_t>, ProfileIndex, nullptr);
|
||||
|
||||
static constexpr std::string_view ProfileIndexKey{ "index" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<NewTabArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_ProfileIndex == _ProfileIndex;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<NewTabArgs>();
|
||||
if (auto profileIndex{ json[JsonKey(ProfileIndexKey)] })
|
||||
{
|
||||
args->_ProfileIndex = profileIndex.asInt();
|
||||
}
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
|
||||
struct SwitchToTabArgs : public SwitchToTabArgsT<SwitchToTabArgs>
|
||||
{
|
||||
SwitchToTabArgs() = default;
|
||||
GETSET_PROPERTY(int32_t, TabIndex, 0);
|
||||
|
||||
static constexpr std::string_view TabIndexKey{ "index" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<SwitchToTabArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_TabIndex == _TabIndex;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
if (auto tabIndex{ json[JsonKey(TabIndexKey)] })
|
||||
{
|
||||
args->_TabIndex = tabIndex.asInt();
|
||||
}
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
|
||||
// Possible Direction values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view LeftString{ "left" };
|
||||
static constexpr std::string_view RightString{ "right" };
|
||||
static constexpr std::string_view UpString{ "up" };
|
||||
static constexpr std::string_view DownString{ "down" };
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for parsing a Direction from a string
|
||||
// Arguments:
|
||||
// - directionString: the string to attempt to parse
|
||||
// Return Value:
|
||||
// - The encoded Direction value, or Direction::None if it was an invalid string
|
||||
static TerminalApp::Direction ParseDirection(const std::string& directionString)
|
||||
{
|
||||
if (directionString == LeftString)
|
||||
{
|
||||
return TerminalApp::Direction::Left;
|
||||
}
|
||||
else if (directionString == RightString)
|
||||
{
|
||||
return TerminalApp::Direction::Right;
|
||||
}
|
||||
else if (directionString == UpString)
|
||||
{
|
||||
return TerminalApp::Direction::Up;
|
||||
}
|
||||
else if (directionString == DownString)
|
||||
{
|
||||
return TerminalApp::Direction::Down;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::Direction::None;
|
||||
};
|
||||
|
||||
struct ResizePaneArgs : public ResizePaneArgsT<ResizePaneArgs>
|
||||
{
|
||||
ResizePaneArgs() = default;
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::Left);
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::None);
|
||||
|
||||
static constexpr std::string_view DirectionKey{ "direction" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<ResizePaneArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_Direction == _Direction;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
if (auto directionString{ json[JsonKey(DirectionKey)] })
|
||||
{
|
||||
args->_Direction = ParseDirection(directionString.asString());
|
||||
}
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
|
||||
struct MoveFocusArgs : public MoveFocusArgsT<MoveFocusArgs>
|
||||
{
|
||||
MoveFocusArgs() = default;
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::Left);
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::None);
|
||||
|
||||
static constexpr std::string_view DirectionKey{ "direction" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<MoveFocusArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_Direction == _Direction;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
if (auto directionString{ json[JsonKey(DirectionKey)] })
|
||||
{
|
||||
args->_Direction = ParseDirection(directionString.asString());
|
||||
}
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
|
||||
struct AdjustFontSizeArgs : public AdjustFontSizeArgsT<AdjustFontSizeArgs>
|
||||
{
|
||||
AdjustFontSizeArgs() = default;
|
||||
GETSET_PROPERTY(int32_t, Delta, 0);
|
||||
|
||||
static constexpr std::string_view AdjustFontSizeDelta{ "delta" };
|
||||
|
||||
public:
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<AdjustFontSizeArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_Delta == _Delta;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static winrt::TerminalApp::IActionArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<AdjustFontSizeArgs>();
|
||||
if (auto jsonDelta{ json[JsonKey(AdjustFontSizeDelta)] })
|
||||
{
|
||||
args->_Delta = jsonDelta.asInt();
|
||||
}
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
// An empty interface must specify an explicit [uuid] to ensure uniqueness.
|
||||
// We also manually have to specify a "version" attribute to make the compiler happy.
|
||||
[uuid("191C2BDE-1A60-4BAB-9765-D850F0EF2CAC")][version(1)] interface IActionArgs{};
|
||||
interface IActionArgs
|
||||
{
|
||||
Boolean Equals(IActionArgs other);
|
||||
};
|
||||
|
||||
interface IActionEventArgs
|
||||
{
|
||||
@@ -15,7 +16,8 @@ namespace TerminalApp
|
||||
|
||||
enum Direction
|
||||
{
|
||||
Left = 0,
|
||||
None = 0,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down
|
||||
@@ -31,9 +33,11 @@ namespace TerminalApp
|
||||
Boolean TrimWhitespace { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass NewTabWithProfileArgs : IActionArgs
|
||||
[default_interface] runtimeclass NewTabArgs : IActionArgs
|
||||
{
|
||||
Int32 ProfileIndex { get; };
|
||||
// ProfileIndex can be null (for "use the default"), so this needs to be
|
||||
// a IReference, so it's nullable
|
||||
Windows.Foundation.IReference<Int32> ProfileIndex { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SwitchToTabArgs : IActionArgs
|
||||
@@ -51,4 +55,9 @@ namespace TerminalApp
|
||||
Direction Direction { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AdjustFontSizeArgs : IActionArgs
|
||||
{
|
||||
Int32 Delta { get; };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,614 +3,60 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "App.h"
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
#include "App.g.cpp"
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::ApplicationModel::Activation;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
using IInspectable = Windows::Foundation::IInspectable;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// !!! IMPORTANT !!!
|
||||
// Make sure that these keys are in the same order as the
|
||||
// SettingsLoadWarnings/Errors enum is!
|
||||
static const std::array<std::wstring_view, 2> settingsLoadWarningsLabels {
|
||||
L"MissingDefaultProfileText",
|
||||
L"DuplicateProfileText"
|
||||
};
|
||||
static const std::array<std::wstring_view, 1> settingsLoadErrorsLabels {
|
||||
L"NoProfilesText"
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Function Description:
|
||||
// - General-purpose helper for looking up a localized string for a
|
||||
// warning/error. First will look for the given key in the provided map of
|
||||
// keys->strings, where the values in the map are ResourceKeys. If it finds
|
||||
// one, it will lookup the localized string from that ResourceKey.
|
||||
// - If it does not find a key, it'll return an empty string
|
||||
// Arguments:
|
||||
// - key: the value to use to look for a resource key in the given map
|
||||
// - map: A map of keys->Resource keys.
|
||||
// - loader: the ScopedResourceLoader to use to look up the localized string.
|
||||
// Return Value:
|
||||
// - the localized string for the given type, if it exists.
|
||||
template<std::size_t N>
|
||||
static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_view, N> keys, ScopedResourceLoader& loader)
|
||||
{
|
||||
if (index < keys.size())
|
||||
{
|
||||
return loader.GetLocalizedString(keys.at(index));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Gets the text from our ResourceDictionary for the given
|
||||
// SettingsLoadWarning. If there is no such text, we'll return nullptr.
|
||||
// - The warning should have an entry in settingsLoadWarningsLabels.
|
||||
// Arguments:
|
||||
// - warning: the SettingsLoadWarnings value to get the localized text for.
|
||||
// - loader: the ScopedResourceLoader to use to look up the localized string.
|
||||
// Return Value:
|
||||
// - localized text for the given warning
|
||||
static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning, ScopedResourceLoader& loader)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(warning), settingsLoadWarningsLabels, loader);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Gets the text from our ResourceDictionary for the given
|
||||
// SettingsLoadError. If there is no such text, we'll return nullptr.
|
||||
// - The warning should have an entry in settingsLoadErrorsLabels.
|
||||
// Arguments:
|
||||
// - error: the SettingsLoadErrors value to get the localized text for.
|
||||
// - loader: the ScopedResourceLoader to use to look up the localized string.
|
||||
// Return Value:
|
||||
// - localized text for the given error
|
||||
static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error, ScopedResourceLoader& loader)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(error), settingsLoadErrorsLabels, loader);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a Run of text to display an error message. The text is yellow or
|
||||
// red for dark/light theme, respectively.
|
||||
// Arguments:
|
||||
// - text: The text of the error message.
|
||||
// - resources: The application's resource loader.
|
||||
// Return Value:
|
||||
// - The fully styled text run.
|
||||
static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceDictionary& resources)
|
||||
{
|
||||
Documents::Run textRun;
|
||||
textRun.Text(text);
|
||||
|
||||
// Color the text red (light theme) or yellow (dark theme) based on the system theme
|
||||
winrt::IInspectable key = winrt::box_value(L"ErrorTextBrush");
|
||||
if (resources.HasKey(key))
|
||||
{
|
||||
winrt::IInspectable g = resources.Lookup(key);
|
||||
auto brush = g.try_as<winrt::Windows::UI::Xaml::Media::Brush>();
|
||||
textRun.Foreground(brush);
|
||||
}
|
||||
|
||||
return textRun;
|
||||
}
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
App::App() :
|
||||
_dialogLock{},
|
||||
_loadedInitialSettings{ false },
|
||||
_settingsLoadedResult{ S_OK }
|
||||
App::App()
|
||||
{
|
||||
// For your own sanity, it's better to do setup outside the ctor.
|
||||
// If you do any setup in the ctor that ends up throwing an exception,
|
||||
// then it might look like App just failed to activate, which will
|
||||
// cause you to chase down the rabbit hole of "why is App not
|
||||
// registered?" when it definitely is.
|
||||
// This is the same trick that Initialize() is about to use to figure out whether we're coming
|
||||
// from a UWP context or from a Win32 context
|
||||
// See https://github.com/windows-toolkit/Microsoft.Toolkit.Win32/blob/52611c57d89554f357f281d0c79036426a7d9257/Microsoft.Toolkit.Win32.UI.XamlApplication/XamlApplication.cpp#L42
|
||||
const auto dispatcherQueue = ::winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
|
||||
if (dispatcherQueue)
|
||||
{
|
||||
_isUwp = true;
|
||||
}
|
||||
|
||||
// Initialize will become protected or be deleted when GH#1339 (workaround for MSFT:22116519) are fixed.
|
||||
Initialize();
|
||||
|
||||
_resourceLoader = std::make_shared<ScopedResourceLoader>(L"TerminalApp/Resources");
|
||||
|
||||
// The TerminalPage has to be constructed during our construction, to
|
||||
// make sure that there's a terminal page for callers of
|
||||
// SetTitleBarContent
|
||||
_root = winrt::make_self<TerminalPage>(_resourceLoader);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Build the UI for the terminal app. Before this method is called, it
|
||||
// should not be assumed that the TerminalApp is usable. The Settings
|
||||
// should be loaded before this is called, either with LoadSettings or
|
||||
// GetLaunchDimensions (which will call LoadSettings)
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void App::Create()
|
||||
AppLogic App::Logic()
|
||||
{
|
||||
// Assert that we've already loaded our settings. We have to do
|
||||
// this as a MTA, before the app is Create()'d
|
||||
WINRT_ASSERT(_loadedInitialSettings);
|
||||
|
||||
_root->ShowDialog({ this, &App::_ShowDialog });
|
||||
|
||||
_root->SetSettings(_settings, false);
|
||||
_root->Loaded({ this, &App::_OnLoaded });
|
||||
_root->Create();
|
||||
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"AppCreated",
|
||||
TraceLoggingDescription("Event emitted when the application is started"),
|
||||
TraceLoggingBool(_settings->GlobalSettings().GetShowTabsInTitlebar(), "TabsInTitlebar"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
static AppLogic logic;
|
||||
return logic;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Show a ContentDialog with buttons to take further action. Uses the
|
||||
// FrameworkElements provided as the title and content of this dialog, and
|
||||
// displays buttons (or a single button). Two buttons (primary and secondary)
|
||||
// will be displayed if this is an warning dialog for closing the termimal,
|
||||
// this allows the users to abondon the closing action. Otherwise, a single
|
||||
// close button will be displayed.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens.
|
||||
// Arguments:
|
||||
// sender: unused
|
||||
// dialog: the dialog object that is going to show up
|
||||
fire_and_forget App::_ShowDialog(const winrt::Windows::Foundation::IInspectable& sender, winrt::Windows::UI::Xaml::Controls::ContentDialog dialog)
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
void App::OnLaunched(LaunchActivatedEventArgs const& /*e*/)
|
||||
{
|
||||
// DON'T release this lock in a wil::scope_exit. The scope_exit will get
|
||||
// called when we await, which is not what we want.
|
||||
std::unique_lock lock{ _dialogLock, std::try_to_lock };
|
||||
if (!lock)
|
||||
// if this is a UWP... it means its our problem to hook up the content to the window here.
|
||||
if (_isUwp)
|
||||
{
|
||||
// Another dialog is visible.
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs.
|
||||
// Since we're hosting the dialog in a Xaml island, we need to connect it to the
|
||||
// xaml tree somehow.
|
||||
dialog.XamlRoot(_root->XamlRoot());
|
||||
|
||||
// IMPORTANT: Set the requested theme of the dialog, because the
|
||||
// PopupRoot isn't directly in the Xaml tree of our root. So the dialog
|
||||
// won't inherit our RequestedTheme automagically.
|
||||
dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
|
||||
// Display the dialog.
|
||||
Controls::ContentDialogResult result = co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
|
||||
|
||||
// After the dialog is dismissed, the dialog lock (held by `lock`) will
|
||||
// be released so another can be shown
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Displays a dialog for errors found while loading or validating the
|
||||
// settings. Uses the resources under the provided title and content keys
|
||||
// as the title and first content of the dialog, then also displays a
|
||||
// message for whatever exception was found while validating the settings.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See _ShowDialog for details
|
||||
// Arguments:
|
||||
// - titleKey: The key to use to lookup the title text from our resources.
|
||||
// - contentKey: The key to use to lookup the content text from our resources.
|
||||
void App::_ShowLoadErrorsDialog(const winrt::hstring& titleKey,
|
||||
const winrt::hstring& contentKey,
|
||||
HRESULT settingsLoadedResult)
|
||||
{
|
||||
auto title = _resourceLoader->GetLocalizedString(titleKey);
|
||||
auto buttonText = _resourceLoader->GetLocalizedString(L"Ok");
|
||||
|
||||
Controls::TextBlock warningsTextBlock;
|
||||
// Make sure you can copy-paste
|
||||
warningsTextBlock.IsTextSelectionEnabled(true);
|
||||
// Make sure the lines of text wrap
|
||||
warningsTextBlock.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
winrt::Windows::UI::Xaml::Documents::Run errorRun;
|
||||
const auto errorLabel = _resourceLoader->GetLocalizedString(contentKey);
|
||||
errorRun.Text(errorLabel);
|
||||
warningsTextBlock.Inlines().Append(errorRun);
|
||||
|
||||
if (FAILED(settingsLoadedResult))
|
||||
{
|
||||
if (!_settingsLoadExceptionText.empty())
|
||||
auto content = Window::Current().Content();
|
||||
if (content == nullptr)
|
||||
{
|
||||
warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, Resources()));
|
||||
auto logic = Logic();
|
||||
logic.LoadSettings();
|
||||
logic.Create();
|
||||
|
||||
auto page = logic.GetRoot().as<TerminalPage>();
|
||||
|
||||
Window::Current().Content(page);
|
||||
Window::Current().Activate();
|
||||
}
|
||||
}
|
||||
|
||||
// Add a note that we're using the default settings in this case.
|
||||
winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun;
|
||||
const auto usingDefaultsText = _resourceLoader->GetLocalizedString(L"UsingDefaultSettingsText");
|
||||
usingDefaultsRun.Text(usingDefaultsText);
|
||||
warningsTextBlock.Inlines().Append(usingDefaultsRun);
|
||||
|
||||
Controls::ContentDialog dialog;
|
||||
dialog.Title(winrt::box_value(title));
|
||||
dialog.Content(winrt::box_value(warningsTextBlock));
|
||||
dialog.CloseButtonText(buttonText);
|
||||
|
||||
_ShowDialog(nullptr, dialog);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Displays a dialog for warnings found while loading or validating the
|
||||
// settings. Displays messages for whatever warnings were found while
|
||||
// validating the settings.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See _ShowDialog for details
|
||||
void App::_ShowLoadWarningsDialog()
|
||||
{
|
||||
auto title = _resourceLoader->GetLocalizedString(L"SettingsValidateErrorTitle");
|
||||
auto buttonText = _resourceLoader->GetLocalizedString(L"Ok");
|
||||
|
||||
Controls::TextBlock warningsTextBlock;
|
||||
// Make sure you can copy-paste
|
||||
warningsTextBlock.IsTextSelectionEnabled(true);
|
||||
// Make sure the lines of text wrap
|
||||
warningsTextBlock.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
const auto& warnings = _settings->GetWarnings();
|
||||
for (const auto& warning : warnings)
|
||||
{
|
||||
// Try looking up the warning message key for each warning.
|
||||
const auto warningText = _GetWarningText(warning, *_resourceLoader);
|
||||
if (!warningText.empty())
|
||||
{
|
||||
warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, Resources()));
|
||||
}
|
||||
}
|
||||
|
||||
Controls::ContentDialog dialog;
|
||||
dialog.Title(winrt::box_value(title));
|
||||
dialog.Content(winrt::box_value(warningsTextBlock));
|
||||
dialog.CloseButtonText(buttonText);
|
||||
|
||||
_ShowDialog(nullptr, dialog);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Triggered when the application is fiished loading. If we failed to load
|
||||
// the settings, then this will display the error dialog. This is done
|
||||
// here instead of when loading the settings, because we need our UI to be
|
||||
// visible to display the dialog, and when we're loading the settings,
|
||||
// the UI might not be visible yet.
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
void App::_OnLoaded(const IInspectable& /*sender*/,
|
||||
const RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
const winrt::hstring titleKey = L"InitialJsonParseErrorTitle";
|
||||
const winrt::hstring textKey = L"InitialJsonParseErrorText";
|
||||
_ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult);
|
||||
}
|
||||
else if (_settingsLoadedResult == S_FALSE)
|
||||
{
|
||||
_ShowLoadWarningsDialog();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the size in pixels of the client area we'll need to launch this
|
||||
// terminal app. This method will use the default profile's settings to do
|
||||
// this calculation, as well as the _system_ dpi scaling. See also
|
||||
// TermControl::GetProposedDimensions.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a point containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Point App::GetLaunchDimensions(uint32_t dpi)
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
// Use the default profile to determine how big of a window we need.
|
||||
TerminalSettings settings = _settings->MakeSettings(std::nullopt);
|
||||
|
||||
// TODO MSFT:21150597 - If the global setting "Always show tab bar" is
|
||||
// set, then we'll need to add the height of the tab bar here.
|
||||
|
||||
return TermControl::GetProposedDimensions(settings, dpi);
|
||||
}
|
||||
|
||||
bool App::GetShowTabsInTitlebar()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings->GlobalSettings().GetShowTabsInTitlebar();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to load the settings. If we fail for any reason, returns an error.
|
||||
// Return Value:
|
||||
// - S_OK if we successfully parsed the settings, otherwise an appropriate HRESULT.
|
||||
[[nodiscard]] HRESULT App::_TryLoadSettings() noexcept
|
||||
{
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
try
|
||||
{
|
||||
auto newSettings = CascadiaSettings::LoadAll();
|
||||
_settings = std::move(newSettings);
|
||||
const auto& warnings = _settings->GetWarnings();
|
||||
hr = warnings.size() == 0 ? S_OK : S_FALSE;
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
hr = e.code();
|
||||
_settingsLoadExceptionText = e.message();
|
||||
LOG_HR(hr);
|
||||
}
|
||||
catch (const ::TerminalApp::SettingsException& ex)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
_settingsLoadExceptionText = _GetErrorText(ex.Error(), *_resourceLoader);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
hr = wil::ResultFromCaughtException();
|
||||
LOG_HR(hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initialized our settings. See CascadiaSettings for more details.
|
||||
// Additionally hooks up our callbacks for keybinding events to the
|
||||
// keybindings object.
|
||||
// NOTE: This must be called from a MTA if we're running as a packaged
|
||||
// application. The Windows.Storage APIs require a MTA. If this isn't
|
||||
// happening during startup, it'll need to happen on a background thread.
|
||||
void App::LoadSettings()
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"SettingsLoadStarted",
|
||||
TraceLoggingDescription("Event emitted before loading the settings"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
// Attempt to load the settings.
|
||||
// If it fails,
|
||||
// - use Default settings,
|
||||
// - don't persist them (LoadAll won't save them in this case).
|
||||
// - _settingsLoadedResult will be set to an error, indicating that
|
||||
// we should display the loading error.
|
||||
// * We can't display the error now, because we might not have a
|
||||
// UI yet. We'll display the error in _OnLoaded.
|
||||
_settingsLoadedResult = _TryLoadSettings();
|
||||
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
_settings = CascadiaSettings::LoadDefaults();
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> delta = end - start;
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"SettingsLoadComplete",
|
||||
TraceLoggingDescription("Event emitted when loading the settings is finished"),
|
||||
TraceLoggingFloat64(delta.count(), "Duration"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
_loadedInitialSettings = true;
|
||||
|
||||
// Register for directory change notification.
|
||||
_RegisterSettingsChange();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Registers for changes to the settings folder and upon a updated settings
|
||||
// profile calls _ReloadSettings().
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void App::_RegisterSettingsChange()
|
||||
{
|
||||
// Get the containing folder.
|
||||
std::filesystem::path settingsPath{ CascadiaSettings::GetSettingsPath() };
|
||||
const auto folder = settingsPath.parent_path();
|
||||
|
||||
_reader.create(folder.c_str(),
|
||||
false,
|
||||
wil::FolderChangeEvents::All,
|
||||
[this, settingsPath](wil::FolderChangeEvent event, PCWSTR fileModified) {
|
||||
// We want file modifications, AND when files are renamed to be
|
||||
// profiles.json. This second case will oftentimes happen with text
|
||||
// editors, who will write a temp file, then rename it to be the
|
||||
// actual file you wrote. So listen for that too.
|
||||
if (!(event == wil::FolderChangeEvent::Modified ||
|
||||
event == wil::FolderChangeEvent::RenameNewName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path modifiedFilePath = fileModified;
|
||||
|
||||
// Getting basename (filename.ext)
|
||||
const auto settingsBasename = settingsPath.filename();
|
||||
const auto modifiedBasename = modifiedFilePath.filename();
|
||||
|
||||
if (settingsBasename == modifiedBasename)
|
||||
{
|
||||
this->_DispatchReloadSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Dispatches a settings reload with debounce.
|
||||
// Text editors implement Save in a bunch of different ways, so
|
||||
// this stops us from reloading too many times or too quickly.
|
||||
fire_and_forget App::_DispatchReloadSettings()
|
||||
{
|
||||
static constexpr auto FileActivityQuiesceTime{ std::chrono::milliseconds(50) };
|
||||
if (!_settingsReloadQueued.exchange(true))
|
||||
{
|
||||
co_await winrt::resume_after(FileActivityQuiesceTime);
|
||||
_ReloadSettings();
|
||||
_settingsReloadQueued.store(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Reloads the settings from the profile.json.
|
||||
void App::_ReloadSettings()
|
||||
{
|
||||
// Attempt to load our settings.
|
||||
// If it fails,
|
||||
// - don't change the settings (and don't actually apply the new settings)
|
||||
// - don't persist them.
|
||||
// - display a loading error
|
||||
_settingsLoadedResult = _TryLoadSettings();
|
||||
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
_root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
const winrt::hstring titleKey = L"ReloadJsonParseErrorTitle";
|
||||
const winrt::hstring textKey = L"ReloadJsonParseErrorText";
|
||||
_ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
else if (_settingsLoadedResult == S_FALSE)
|
||||
{
|
||||
_root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
_ShowLoadWarningsDialog();
|
||||
});
|
||||
}
|
||||
|
||||
// Here, we successfully reloaded the settings, and created a new
|
||||
// TerminalSettings object.
|
||||
|
||||
// Update the settings in TerminalPage
|
||||
_root->SetSettings(_settings, true);
|
||||
|
||||
_root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
// Refresh the UI theme
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void App::_ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme)
|
||||
{
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_requestedThemeChangedHandlers(*this, newTheme);
|
||||
}
|
||||
|
||||
UIElement App::GetRoot() noexcept
|
||||
{
|
||||
return _root.as<winrt::Windows::UI::Xaml::Controls::Control>();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the title of the currently focused terminal control. If there
|
||||
// isn't a control selected for any reason, returns "Windows Terminal"
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the title of the focused control if there is one, else "Windows Terminal"
|
||||
hstring App::Title()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->Title();
|
||||
}
|
||||
return { L"Windows Terminal" };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Used to tell the app that the titlebar has been clicked. The App won't
|
||||
// actually recieve any clicks in the titlebar area, so this is a helper
|
||||
// to clue the app in that a click has happened. The App will use this as
|
||||
// a indicator that it needs to dismiss any open flyouts.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void App::TitlebarClicked()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->TitlebarClicked();
|
||||
}
|
||||
}
|
||||
|
||||
// Methods that proxy typed event handlers through TerminalPage
|
||||
winrt::event_token App::SetTitleBarContent(Windows::Foundation::TypedEventHandler<winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement> const& handler)
|
||||
{
|
||||
return _root->SetTitleBarContent(handler);
|
||||
}
|
||||
void App::SetTitleBarContent(winrt::event_token const& token) noexcept
|
||||
{
|
||||
return _root->SetTitleBarContent(token);
|
||||
}
|
||||
|
||||
winrt::event_token App::TitleChanged(Windows::Foundation::TypedEventHandler<winrt::Windows::Foundation::IInspectable, winrt::hstring> const& handler)
|
||||
{
|
||||
return _root->TitleChanged(handler);
|
||||
}
|
||||
void App::TitleChanged(winrt::event_token const& token) noexcept
|
||||
{
|
||||
return _root->TitleChanged(token);
|
||||
}
|
||||
|
||||
winrt::event_token App::LastTabClosed(Windows::Foundation::TypedEventHandler<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs> const& handler)
|
||||
{
|
||||
return _root->LastTabClosed(handler);
|
||||
}
|
||||
void App::LastTabClosed(winrt::event_token const& token) noexcept
|
||||
{
|
||||
return _root->LastTabClosed(token);
|
||||
}
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// Winrt events need a method for adding a callback to the event and removing the callback.
|
||||
// These macros will define them both for you.
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
}
|
||||
|
||||
@@ -3,21 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Tab.h"
|
||||
#include "CascadiaSettings.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "App.g.h"
|
||||
#include "App.base.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
@@ -25,70 +12,12 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
public:
|
||||
App();
|
||||
~App() = default;
|
||||
void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs const&);
|
||||
|
||||
void Create();
|
||||
void LoadSettings();
|
||||
|
||||
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
|
||||
bool GetShowTabsInTitlebar();
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
|
||||
hstring Title();
|
||||
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(RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
TerminalApp::AppLogic Logic();
|
||||
|
||||
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 _ApplyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
|
||||
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
|
||||
|
||||
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
|
||||
|
||||
HRESULT _settingsLoadedResult;
|
||||
winrt::hstring _settingsLoadExceptionText{};
|
||||
|
||||
bool _loadedInitialSettings;
|
||||
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
std::shared_mutex _dialogLock;
|
||||
|
||||
std::atomic<bool> _settingsReloadQueued{ false };
|
||||
|
||||
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 _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
|
||||
void _LoadSettings();
|
||||
void _RegisterSettingsChange();
|
||||
fire_and_forget _DispatchReloadSettings();
|
||||
void _ReloadSettings();
|
||||
|
||||
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
|
||||
|
||||
static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile);
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl();
|
||||
|
||||
void _CopyToClipboardHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData);
|
||||
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);
|
||||
bool _isUwp = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "../AppLogic.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
delegate void LastTabClosedEventArgs();
|
||||
// ADD ARBITRARY APP LOGIC TO APPLOGIC.IDL, NOT HERE.
|
||||
// This is for XAML platform setup only.
|
||||
[default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
|
||||
{
|
||||
App();
|
||||
|
||||
void Initialize();
|
||||
|
||||
// For your own sanity, it's better to do setup outside the ctor.
|
||||
// If you do any setup in the ctor that ends up throwing an exception,
|
||||
// then it might look like TermApp just failed to activate, which will
|
||||
// cause you to chase down the rabbit hole of "why is TermApp not
|
||||
// registered?" when it definitely is.
|
||||
void Create();
|
||||
|
||||
void LoadSettings();
|
||||
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
|
||||
String Title { get; };
|
||||
|
||||
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
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;
|
||||
AppLogic Logic { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +49,24 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
is unfortunately static. -->
|
||||
<SolidColorBrush x:Name="ErrorTextBrush" Color="{ThemeResource SystemErrorTextColor}" />
|
||||
|
||||
<!-- Suppress all padding except left around the tabs. The TabView looks far better like this. -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">8,0,0,0</Thickness>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<!-- Define resources for Dark mode here -->
|
||||
<!-- The TabViewBackground is used on a control (DragBar, TitleBarControl) whose color is propagated to GDI.
|
||||
The default background is black or white with an alpha component, as it's intended to be layered on top of
|
||||
another control. Unfortunately, GDI cannot handle this: we need to either render the XAML to a surface and
|
||||
sample the pixels out of it, or premultiply the alpha into the background. For obvious reasons, we've chosen
|
||||
the latter. -->
|
||||
<SolidColorBrush x:Key="TabViewBackground" Color="#FF333333" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- Define resources for Light mode here -->
|
||||
<!-- See note about premultiplication above. -->
|
||||
<SolidColorBrush x:Key="TabViewBackground" Color="#FFCCCCCC" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
@@ -26,12 +26,6 @@ namespace winrt
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_OpenNewTab(std::nullopt);
|
||||
args.Handled(true);
|
||||
}
|
||||
void TerminalPage::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
@@ -63,7 +57,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_CloseWindow();
|
||||
CloseWindow();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -138,12 +132,24 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNewTabWithProfile(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::NewTabWithProfileArgs>())
|
||||
if (args == nullptr)
|
||||
{
|
||||
_OpenNewTab({ realArgs.ProfileIndex() });
|
||||
_OpenNewTab(std::nullopt);
|
||||
args.Handled(true);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::NewTabArgs>())
|
||||
{
|
||||
if (realArgs.ProfileIndex() == nullptr)
|
||||
{
|
||||
_OpenNewTab(std::nullopt);
|
||||
}
|
||||
else
|
||||
{
|
||||
_OpenNewTab(realArgs.ProfileIndex().Value());
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -163,8 +169,16 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::ResizePaneArgs>())
|
||||
{
|
||||
_ResizePane(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
if (realArgs.Direction() == TerminalApp::Direction::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ResizePane(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +187,16 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::MoveFocusArgs>())
|
||||
{
|
||||
_MoveFocus(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
if (realArgs.Direction() == TerminalApp::Direction::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_MoveFocus(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,4 +210,30 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleAdjustFontSize(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::AdjustFontSizeArgs>())
|
||||
{
|
||||
const auto termControl = _GetActiveControl();
|
||||
termControl.AdjustFontSize(realArgs.Delta());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleResetFontSize(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
const auto termControl = _GetActiveControl();
|
||||
termControl.ResetFontSize();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleFullscreen(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_ToggleFullscreen();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void AppKeyBindings::SetKeyBinding(const TerminalApp::ShortcutAction& action,
|
||||
void AppKeyBindings::SetKeyBinding(const TerminalApp::ActionAndArgs& actionAndArgs,
|
||||
const Settings::KeyChord& chord)
|
||||
{
|
||||
_keyShortcuts[chord] = action;
|
||||
_keyShortcuts[chord] = actionAndArgs;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -29,11 +30,33 @@ namespace winrt::TerminalApp::implementation
|
||||
_keyShortcuts.erase(chord);
|
||||
}
|
||||
|
||||
Microsoft::Terminal::Settings::KeyChord AppKeyBindings::GetKeyBinding(TerminalApp::ShortcutAction const& action)
|
||||
KeyChord AppKeyBindings::GetKeyBindingForAction(TerminalApp::ShortcutAction const& action)
|
||||
{
|
||||
for (auto& kv : _keyShortcuts)
|
||||
{
|
||||
if (kv.second == action)
|
||||
if (kv.second.Action() == action)
|
||||
{
|
||||
return kv.first;
|
||||
}
|
||||
}
|
||||
return { nullptr };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Lookup the keychord bound to a particular combination of ShortcutAction
|
||||
// and IActionArgs. This enables searching no only for the binding of a
|
||||
// particular ShortcutAction, but also a particular set of values for
|
||||
// arguments to that action.
|
||||
// Arguments:
|
||||
// - actionAndArgs: The ActionAndArgs to lookup the keybinding for.
|
||||
// Return Value:
|
||||
// - The bound keychord, if this ActionAndArgs is bound to a key, otherwise nullptr.
|
||||
KeyChord AppKeyBindings::GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs)
|
||||
{
|
||||
for (auto& kv : _keyShortcuts)
|
||||
{
|
||||
if (kv.second.Action() == actionAndArgs.Action() &&
|
||||
kv.second.Args().Equals(actionAndArgs.Args()))
|
||||
{
|
||||
return kv.first;
|
||||
}
|
||||
@@ -46,352 +69,191 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto keyIter = _keyShortcuts.find(kc);
|
||||
if (keyIter != _keyShortcuts.end())
|
||||
{
|
||||
const auto action = keyIter->second;
|
||||
return _DoAction(action);
|
||||
const auto actionAndArgs = keyIter->second;
|
||||
return _DoAction(actionAndArgs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::_DoAction(ShortcutAction action)
|
||||
bool AppKeyBindings::_DoAction(ActionAndArgs actionAndArgs)
|
||||
{
|
||||
const auto& action = actionAndArgs.Action();
|
||||
const auto& args = actionAndArgs.Args();
|
||||
auto eventArgs = args ? winrt::make_self<ActionEventArgs>(args) :
|
||||
winrt::make_self<ActionEventArgs>();
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case ShortcutAction::CopyText:
|
||||
{
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
args->TrimWhitespace(true);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_CopyTextHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::CopyTextWithoutNewlines:
|
||||
{
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
args->TrimWhitespace(false);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_CopyTextHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::PasteText:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_PasteTextHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_NewTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::OpenNewTabDropdown:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_OpenNewTabDropdownHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::DuplicateTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_DuplicateTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::OpenSettings:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_OpenSettingsHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::NewTab:
|
||||
case ShortcutAction::NewTabProfile0:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(0);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile1:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(1);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile2:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(2);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile3:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(3);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile4:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(4);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile5:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(5);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile6:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(6);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile7:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(7);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTabProfile8:
|
||||
{
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(8);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
_NewTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::NewWindow:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_NewWindowHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::CloseWindow:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_CloseWindowHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::CloseTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_CloseTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::ClosePane:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ClosePaneHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::ScrollUp:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollUpHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::ScrollDown:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollDownHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::ScrollUpPage:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollUpPageHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::ScrollDownPage:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollDownPageHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::NextTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_NextTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::PrevTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_PrevTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::SplitVertical:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_SplitVerticalHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::SplitHorizontal:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_SplitHorizontalHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::SwitchToTab:
|
||||
case ShortcutAction::SwitchToTab0:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(0);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab1:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(1);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab2:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(2);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab3:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(3);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab4:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(4);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab5:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(5);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab6:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(6);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab7:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(7);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SwitchToTab8:
|
||||
{
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
args->TabIndex(8);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::ResizePane:
|
||||
case ShortcutAction::ResizePaneLeft:
|
||||
{
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
args->Direction(Direction::Left);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_ResizePaneHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ResizePaneRight:
|
||||
{
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
args->Direction(Direction::Right);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_ResizePaneHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ResizePaneUp:
|
||||
{
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
args->Direction(Direction::Up);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_ResizePaneHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ResizePaneDown:
|
||||
{
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
args->Direction(Direction::Down);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_ResizePaneHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::MoveFocus:
|
||||
case ShortcutAction::MoveFocusLeft:
|
||||
{
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
args->Direction(Direction::Left);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_MoveFocusHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::MoveFocusRight:
|
||||
{
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
args->Direction(Direction::Right);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_MoveFocusHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::MoveFocusUp:
|
||||
{
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
args->Direction(Direction::Up);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_MoveFocusHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::MoveFocusDown:
|
||||
{
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
args->Direction(Direction::Down);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_MoveFocusHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::IncreaseFontSize:
|
||||
{
|
||||
_AdjustFontSizeHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::DecreaseFontSize:
|
||||
{
|
||||
_AdjustFontSizeHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
case ShortcutAction::ResetFontSize:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ResetFontSizeHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ToggleFullscreen:
|
||||
{
|
||||
_ToggleFullscreenHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -41,9 +41,12 @@ namespace winrt::TerminalApp::implementation
|
||||
AppKeyBindings() = default;
|
||||
|
||||
bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc);
|
||||
void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
|
||||
void SetKeyBinding(TerminalApp::ActionAndArgs const& actionAndArgs,
|
||||
winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
void ClearKeyBinding(winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBinding(TerminalApp::ShortcutAction const& action);
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBindingForAction(TerminalApp::ShortcutAction const& action);
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs);
|
||||
|
||||
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
|
||||
|
||||
@@ -54,10 +57,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// clang-format off
|
||||
TYPED_EVENT(CopyText, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(PasteText, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(NewTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(OpenNewTabDropdown,TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(DuplicateTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(NewTabWithProfile, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(NewTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(NewWindow, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(CloseWindow, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(CloseTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
@@ -67,8 +69,8 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(PrevTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(SplitVertical, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(SplitHorizontal, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(IncreaseFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(DecreaseFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(AdjustFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ResetFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ScrollUp, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ScrollDown, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ScrollUpPage, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
@@ -76,11 +78,12 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(OpenSettings, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ResizePane, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(MoveFocus, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ToggleFullscreen, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
std::unordered_map<winrt::Microsoft::Terminal::Settings::KeyChord, TerminalApp::ShortcutAction, KeyChordHash, KeyChordEquality> _keyShortcuts;
|
||||
bool _DoAction(ShortcutAction action);
|
||||
std::unordered_map<winrt::Microsoft::Terminal::Settings::KeyChord, TerminalApp::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
|
||||
bool _DoAction(ActionAndArgs actionAndArgs);
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::KeyBindingsTests;
|
||||
|
||||
@@ -4,24 +4,27 @@ import "../ActionArgs.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
// TODO: GH#1069 - Many of these shortcut actions are "legacy" now that we
|
||||
// have support for arbitrary args (#1142). We should remove them, and our
|
||||
// legacy deserializers.
|
||||
enum ShortcutAction
|
||||
{
|
||||
Invalid = 0,
|
||||
CopyText,
|
||||
CopyTextWithoutNewlines,
|
||||
PasteText,
|
||||
NewTab,
|
||||
OpenNewTabDropdown,
|
||||
DuplicateTab,
|
||||
NewTabProfile0,
|
||||
NewTabProfile1,
|
||||
NewTabProfile2,
|
||||
NewTabProfile3,
|
||||
NewTabProfile4,
|
||||
NewTabProfile5,
|
||||
NewTabProfile6,
|
||||
NewTabProfile7,
|
||||
NewTabProfile8,
|
||||
NewTab,
|
||||
NewTabProfile0, // Legacy
|
||||
NewTabProfile1, // Legacy
|
||||
NewTabProfile2, // Legacy
|
||||
NewTabProfile3, // Legacy
|
||||
NewTabProfile4, // Legacy
|
||||
NewTabProfile5, // Legacy
|
||||
NewTabProfile6, // Legacy
|
||||
NewTabProfile7, // Legacy
|
||||
NewTabProfile8, // Legacy
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
CloseTab,
|
||||
@@ -30,46 +33,58 @@ namespace TerminalApp
|
||||
PrevTab,
|
||||
SplitVertical,
|
||||
SplitHorizontal,
|
||||
SwitchToTab0,
|
||||
SwitchToTab1,
|
||||
SwitchToTab2,
|
||||
SwitchToTab3,
|
||||
SwitchToTab4,
|
||||
SwitchToTab5,
|
||||
SwitchToTab6,
|
||||
SwitchToTab7,
|
||||
SwitchToTab8,
|
||||
SwitchToTab,
|
||||
SwitchToTab0, // Legacy
|
||||
SwitchToTab1, // Legacy
|
||||
SwitchToTab2, // Legacy
|
||||
SwitchToTab3, // Legacy
|
||||
SwitchToTab4, // Legacy
|
||||
SwitchToTab5, // Legacy
|
||||
SwitchToTab6, // Legacy
|
||||
SwitchToTab7, // Legacy
|
||||
SwitchToTab8, // Legacy
|
||||
IncreaseFontSize,
|
||||
DecreaseFontSize,
|
||||
ResetFontSize,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
ScrollUpPage,
|
||||
ScrollDownPage,
|
||||
ResizePaneLeft,
|
||||
ResizePaneRight,
|
||||
ResizePaneUp,
|
||||
ResizePaneDown,
|
||||
MoveFocusLeft,
|
||||
MoveFocusRight,
|
||||
MoveFocusUp,
|
||||
MoveFocusDown,
|
||||
ResizePane,
|
||||
ResizePaneLeft, // Legacy
|
||||
ResizePaneRight, // Legacy
|
||||
ResizePaneUp, // Legacy
|
||||
ResizePaneDown, // Legacy
|
||||
MoveFocus,
|
||||
MoveFocusLeft, // Legacy
|
||||
MoveFocusRight, // Legacy
|
||||
MoveFocusUp, // Legacy
|
||||
MoveFocusDown, // Legacy
|
||||
ToggleFullscreen,
|
||||
OpenSettings
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ActionAndArgs {
|
||||
ActionAndArgs();
|
||||
IActionArgs Args;
|
||||
ShortcutAction Action;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
|
||||
{
|
||||
AppKeyBindings();
|
||||
|
||||
void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord);
|
||||
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.Settings.KeyChord chord);
|
||||
void ClearKeyBinding(Microsoft.Terminal.Settings.KeyChord chord);
|
||||
Microsoft.Terminal.Settings.KeyChord GetKeyBinding(ShortcutAction action);
|
||||
|
||||
Microsoft.Terminal.Settings.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
Microsoft.Terminal.Settings.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> CopyText;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> PasteText;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> NewTab;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> OpenNewTabDropdown;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> DuplicateTab;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> NewTabWithProfile;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> NewWindow;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> CloseWindow;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> CloseTab;
|
||||
@@ -79,8 +94,8 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> PrevTab;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> SplitVertical;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> SplitHorizontal;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> IncreaseFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> DecreaseFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> AdjustFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ResetFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ScrollUp;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ScrollDown;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ScrollUpPage;
|
||||
@@ -88,5 +103,6 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> OpenSettings;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ResizePane;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> MoveFocus;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ToggleFullscreen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppKeyBindings.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
@@ -18,25 +19,26 @@ using namespace winrt::TerminalApp;
|
||||
|
||||
static constexpr std::string_view KeysKey{ "keys" };
|
||||
static constexpr std::string_view CommandKey{ "command" };
|
||||
static constexpr std::string_view ActionKey{ "action" };
|
||||
|
||||
// This key is reserved to remove a keybinding, instead of mapping it to an action.
|
||||
static constexpr std::string_view UnboundKey{ "unbound" };
|
||||
|
||||
static constexpr std::string_view CopyTextKey{ "copy" };
|
||||
static constexpr std::string_view CopyTextWithoutNewlinesKey{ "copyTextWithoutNewlines" };
|
||||
static constexpr std::string_view CopyTextWithoutNewlinesKey{ "copyTextWithoutNewlines" }; // Legacy
|
||||
static constexpr std::string_view PasteTextKey{ "paste" };
|
||||
static constexpr std::string_view NewTabKey{ "newTab" };
|
||||
static constexpr std::string_view OpenNewTabDropdownKey{ "openNewTabDropdown" };
|
||||
static constexpr std::string_view DuplicateTabKey{ "duplicateTab" };
|
||||
static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" };
|
||||
static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" };
|
||||
static constexpr std::string_view NewTabWithProfile2Key{ "newTabProfile2" };
|
||||
static constexpr std::string_view NewTabWithProfile3Key{ "newTabProfile3" };
|
||||
static constexpr std::string_view NewTabWithProfile4Key{ "newTabProfile4" };
|
||||
static constexpr std::string_view NewTabWithProfile5Key{ "newTabProfile5" };
|
||||
static constexpr std::string_view NewTabWithProfile6Key{ "newTabProfile6" };
|
||||
static constexpr std::string_view NewTabWithProfile7Key{ "newTabProfile7" };
|
||||
static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" };
|
||||
static constexpr std::string_view NewTabKey{ "newTab" };
|
||||
static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile2Key{ "newTabProfile2" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile3Key{ "newTabProfile3" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile4Key{ "newTabProfile4" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile5Key{ "newTabProfile5" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile6Key{ "newTabProfile6" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile7Key{ "newTabProfile7" }; // Legacy
|
||||
static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" }; // Legacy
|
||||
static constexpr std::string_view NewWindowKey{ "newWindow" };
|
||||
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
|
||||
static constexpr std::string_view CloseTabKey{ "closeTab" };
|
||||
@@ -46,30 +48,35 @@ static constexpr std::string_view NextTabKey{ "nextTab" };
|
||||
static constexpr std::string_view PrevTabKey{ "prevTab" };
|
||||
static constexpr std::string_view IncreaseFontSizeKey{ "increaseFontSize" };
|
||||
static constexpr std::string_view DecreaseFontSizeKey{ "decreaseFontSize" };
|
||||
static constexpr std::string_view ResetFontSizeKey{ "resetFontSize" };
|
||||
static constexpr std::string_view ScrollupKey{ "scrollUp" };
|
||||
static constexpr std::string_view ScrolldownKey{ "scrollDown" };
|
||||
static constexpr std::string_view ScrolluppageKey{ "scrollUpPage" };
|
||||
static constexpr std::string_view ScrolldownpageKey{ "scrollDownPage" };
|
||||
static constexpr std::string_view SwitchToTab0Key{ "switchToTab0" };
|
||||
static constexpr std::string_view SwitchToTab1Key{ "switchToTab1" };
|
||||
static constexpr std::string_view SwitchToTab2Key{ "switchToTab2" };
|
||||
static constexpr std::string_view SwitchToTab3Key{ "switchToTab3" };
|
||||
static constexpr std::string_view SwitchToTab4Key{ "switchToTab4" };
|
||||
static constexpr std::string_view SwitchToTab5Key{ "switchToTab5" };
|
||||
static constexpr std::string_view SwitchToTab6Key{ "switchToTab6" };
|
||||
static constexpr std::string_view SwitchToTab7Key{ "switchToTab7" };
|
||||
static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" };
|
||||
static constexpr std::string_view OpenSettingsKey{ "openSettings" };
|
||||
static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
|
||||
static constexpr std::string_view SwitchToTab0Key{ "switchToTab0" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab1Key{ "switchToTab1" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab2Key{ "switchToTab2" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab3Key{ "switchToTab3" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab4Key{ "switchToTab4" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab5Key{ "switchToTab5" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab6Key{ "switchToTab6" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab7Key{ "switchToTab7" }; // Legacy
|
||||
static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" }; // Legacy
|
||||
static constexpr std::string_view OpenSettingsKey{ "openSettings" }; // Legacy
|
||||
static constexpr std::string_view SplitHorizontalKey{ "splitHorizontal" };
|
||||
static constexpr std::string_view SplitVerticalKey{ "splitVertical" };
|
||||
static constexpr std::string_view ResizePaneLeftKey{ "resizePaneLeft" };
|
||||
static constexpr std::string_view ResizePaneRightKey{ "resizePaneRight" };
|
||||
static constexpr std::string_view ResizePaneUpKey{ "resizePaneUp" };
|
||||
static constexpr std::string_view ResizePaneDownKey{ "resizePaneDown" };
|
||||
static constexpr std::string_view MoveFocusLeftKey{ "moveFocusLeft" };
|
||||
static constexpr std::string_view MoveFocusRightKey{ "moveFocusRight" };
|
||||
static constexpr std::string_view MoveFocusUpKey{ "moveFocusUp" };
|
||||
static constexpr std::string_view MoveFocusDownKey{ "moveFocusDown" };
|
||||
static constexpr std::string_view ResizePaneKey{ "resizePane" };
|
||||
static constexpr std::string_view ResizePaneLeftKey{ "resizePaneLeft" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneRightKey{ "resizePaneRight" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneUpKey{ "resizePaneUp" }; // Legacy
|
||||
static constexpr std::string_view ResizePaneDownKey{ "resizePaneDown" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusKey{ "moveFocus" };
|
||||
static constexpr std::string_view MoveFocusLeftKey{ "moveFocusLeft" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusRightKey{ "moveFocusRight" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusUpKey{ "moveFocusUp" }; // Legacy
|
||||
static constexpr std::string_view MoveFocusDownKey{ "moveFocusDown" }; // Legacy
|
||||
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
|
||||
|
||||
// Specifically use a map here over an unordered_map. We want to be able to
|
||||
// iterate over these entries in-order when we're serializing the keybindings.
|
||||
@@ -83,9 +90,9 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
|
||||
{ CopyTextKey, ShortcutAction::CopyText },
|
||||
{ CopyTextWithoutNewlinesKey, ShortcutAction::CopyTextWithoutNewlines },
|
||||
{ PasteTextKey, ShortcutAction::PasteText },
|
||||
{ NewTabKey, ShortcutAction::NewTab },
|
||||
{ OpenNewTabDropdownKey, ShortcutAction::OpenNewTabDropdown },
|
||||
{ DuplicateTabKey, ShortcutAction::DuplicateTab },
|
||||
{ NewTabKey, ShortcutAction::NewTab },
|
||||
{ NewTabWithProfile0Key, ShortcutAction::NewTabProfile0 },
|
||||
{ NewTabWithProfile1Key, ShortcutAction::NewTabProfile1 },
|
||||
{ NewTabWithProfile2Key, ShortcutAction::NewTabProfile2 },
|
||||
@@ -103,10 +110,12 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
|
||||
{ PrevTabKey, ShortcutAction::PrevTab },
|
||||
{ IncreaseFontSizeKey, ShortcutAction::IncreaseFontSize },
|
||||
{ DecreaseFontSizeKey, ShortcutAction::DecreaseFontSize },
|
||||
{ ResetFontSizeKey, ShortcutAction::ResetFontSize },
|
||||
{ ScrollupKey, ShortcutAction::ScrollUp },
|
||||
{ ScrolldownKey, ShortcutAction::ScrollDown },
|
||||
{ ScrolluppageKey, ShortcutAction::ScrollUpPage },
|
||||
{ ScrolldownpageKey, ShortcutAction::ScrollDownPage },
|
||||
{ SwitchToTabKey, ShortcutAction::SwitchToTab },
|
||||
{ SwitchToTab0Key, ShortcutAction::SwitchToTab0 },
|
||||
{ SwitchToTab1Key, ShortcutAction::SwitchToTab1 },
|
||||
{ SwitchToTab2Key, ShortcutAction::SwitchToTab2 },
|
||||
@@ -118,18 +127,187 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
|
||||
{ SwitchToTab8Key, ShortcutAction::SwitchToTab8 },
|
||||
{ SplitHorizontalKey, ShortcutAction::SplitHorizontal },
|
||||
{ SplitVerticalKey, ShortcutAction::SplitVertical },
|
||||
{ ResizePaneKey, ShortcutAction::ResizePane },
|
||||
{ ResizePaneLeftKey, ShortcutAction::ResizePaneLeft },
|
||||
{ ResizePaneRightKey, ShortcutAction::ResizePaneRight },
|
||||
{ ResizePaneUpKey, ShortcutAction::ResizePaneUp },
|
||||
{ ResizePaneDownKey, ShortcutAction::ResizePaneDown },
|
||||
{ MoveFocusKey, ShortcutAction::MoveFocus },
|
||||
{ MoveFocusLeftKey, ShortcutAction::MoveFocusLeft },
|
||||
{ MoveFocusRightKey, ShortcutAction::MoveFocusRight },
|
||||
{ MoveFocusUpKey, ShortcutAction::MoveFocusUp },
|
||||
{ MoveFocusDownKey, ShortcutAction::MoveFocusDown },
|
||||
{ OpenSettingsKey, ShortcutAction::OpenSettings },
|
||||
{ ToggleFullscreenKey, ShortcutAction::ToggleFullscreen },
|
||||
{ UnboundKey, ShortcutAction::Invalid },
|
||||
};
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a MoveFocusArgs for the
|
||||
// legacy MoveFocus[Direction] actions. These actions don't accept args from
|
||||
// json, instead, they just return a MoveFocusArgs with the Direction already
|
||||
// per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - direction: the direction to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// MoveFocus[Direction] args.
|
||||
std::function<IActionArgs(const Json::Value&)> LegacyParseMoveFocusArgs(Direction direction)
|
||||
{
|
||||
auto pfn = [direction](const Json::Value & /*value*/) -> IActionArgs {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::MoveFocusArgs>();
|
||||
args->Direction(direction);
|
||||
return *args;
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a ResizePaneArgs for the
|
||||
// legacy ResizePane[Direction] actions. These actions don't accept args from
|
||||
// json, instead, they just return a ResizePaneArgs with the Direction already
|
||||
// per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - direction: the direction to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// ResizePane[Direction] args.
|
||||
std::function<IActionArgs(const Json::Value&)> LegacyParseResizePaneArgs(Direction direction)
|
||||
{
|
||||
auto pfn = [direction](const Json::Value & /*value*/) -> IActionArgs {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::ResizePaneArgs>();
|
||||
args->Direction(direction);
|
||||
return *args;
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a NewTabWithProfileArgs for
|
||||
// the legacy NewTabWithProfile[Index] actions. These actions don't accept
|
||||
// args from json, instead, they just return a NewTabWithProfileArgs with the
|
||||
// index already per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - index: the profile index to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// NewTabWithProfile[Index] args.
|
||||
std::function<IActionArgs(const Json::Value&)> LegacyParseNewTabWithProfileArgs(int index)
|
||||
{
|
||||
auto pfn = [index](const Json::Value & /*value*/) -> IActionArgs {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
args->ProfileIndex(index);
|
||||
return *args;
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a function that can be used to generate a SwitchToTabArgs for the
|
||||
// legacy SwitchToTab[Index] actions. These actions don't accept args from
|
||||
// json, instead, they just return a SwitchToTabArgs with the index already
|
||||
// per-defined, based on the input param.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - index: the tab index to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into one of the legacy
|
||||
// SwitchToTab[Index] args.
|
||||
std::function<IActionArgs(const Json::Value&)> LegacyParseSwitchToTabArgs(int index)
|
||||
{
|
||||
auto pfn = [index](const Json::Value & /*value*/) -> IActionArgs {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::SwitchToTabArgs>();
|
||||
args->TabIndex(index);
|
||||
return *args;
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Used to generate a CopyTextArgs for the legacy CopyTextWithoutNewlines
|
||||
// action.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - direction: the direction to create the parse function for.
|
||||
// Return Value:
|
||||
// - A CopyTextArgs with TrimWhitespace set to true, to emulate "CopyTextWithoutNewlines".
|
||||
IActionArgs LegacyParseCopyTextWithoutNewlinesArgs(const Json::Value& /*json*/)
|
||||
{
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::CopyTextArgs>();
|
||||
args->TrimWhitespace(false);
|
||||
return *args;
|
||||
};
|
||||
|
||||
// Function Description:
|
||||
// - Used to generate a AdjustFontSizeArgs for IncreaseFontSize/DecreaseFontSize
|
||||
// actions with a delta of 1/-1.
|
||||
// - TODO: GH#1069 Remove this before 1.0, and force an upgrade to the new args.
|
||||
// Arguments:
|
||||
// - delta: the font size delta to create the parse function for.
|
||||
// Return Value:
|
||||
// - A function that can be used to "parse" json into an AdjustFontSizeArgs.
|
||||
std::function<IActionArgs(const Json::Value&)> LegacyParseAdjustFontSizeArgs(int delta)
|
||||
{
|
||||
auto pfn = [delta](const Json::Value & /*value*/) -> IActionArgs {
|
||||
auto args = winrt::make_self<winrt::TerminalApp::implementation::AdjustFontSizeArgs>();
|
||||
args->Delta(delta);
|
||||
return *args;
|
||||
};
|
||||
return pfn;
|
||||
}
|
||||
|
||||
// This is a map of ShortcutAction->function<IActionArgs(Json::Value)>. It holds
|
||||
// a set of deserializer functions that can be used to deserialize a IActionArgs
|
||||
// from json. Each type of IActionArgs that can accept arbitrary args should be
|
||||
// placed into this map, with the corresponding deserializer function as the
|
||||
// value.
|
||||
static const std::map<ShortcutAction, std::function<IActionArgs(const Json::Value&)>, std::less<>> argParsers{
|
||||
{ ShortcutAction::CopyText, winrt::TerminalApp::implementation::CopyTextArgs::FromJson },
|
||||
{ ShortcutAction::CopyTextWithoutNewlines, LegacyParseCopyTextWithoutNewlinesArgs },
|
||||
|
||||
{ ShortcutAction::NewTab, winrt::TerminalApp::implementation::NewTabArgs::FromJson },
|
||||
{ ShortcutAction::NewTabProfile0, LegacyParseNewTabWithProfileArgs(0) },
|
||||
{ ShortcutAction::NewTabProfile1, LegacyParseNewTabWithProfileArgs(1) },
|
||||
{ ShortcutAction::NewTabProfile2, LegacyParseNewTabWithProfileArgs(2) },
|
||||
{ ShortcutAction::NewTabProfile3, LegacyParseNewTabWithProfileArgs(3) },
|
||||
{ ShortcutAction::NewTabProfile4, LegacyParseNewTabWithProfileArgs(4) },
|
||||
{ ShortcutAction::NewTabProfile5, LegacyParseNewTabWithProfileArgs(5) },
|
||||
{ ShortcutAction::NewTabProfile6, LegacyParseNewTabWithProfileArgs(6) },
|
||||
{ ShortcutAction::NewTabProfile7, LegacyParseNewTabWithProfileArgs(7) },
|
||||
{ ShortcutAction::NewTabProfile8, LegacyParseNewTabWithProfileArgs(8) },
|
||||
|
||||
{ ShortcutAction::SwitchToTab, winrt::TerminalApp::implementation::SwitchToTabArgs::FromJson },
|
||||
{ ShortcutAction::SwitchToTab0, LegacyParseSwitchToTabArgs(0) },
|
||||
{ ShortcutAction::SwitchToTab1, LegacyParseSwitchToTabArgs(1) },
|
||||
{ ShortcutAction::SwitchToTab2, LegacyParseSwitchToTabArgs(2) },
|
||||
{ ShortcutAction::SwitchToTab3, LegacyParseSwitchToTabArgs(3) },
|
||||
{ ShortcutAction::SwitchToTab4, LegacyParseSwitchToTabArgs(4) },
|
||||
{ ShortcutAction::SwitchToTab5, LegacyParseSwitchToTabArgs(5) },
|
||||
{ ShortcutAction::SwitchToTab6, LegacyParseSwitchToTabArgs(6) },
|
||||
{ ShortcutAction::SwitchToTab7, LegacyParseSwitchToTabArgs(7) },
|
||||
{ ShortcutAction::SwitchToTab8, LegacyParseSwitchToTabArgs(8) },
|
||||
|
||||
{ ShortcutAction::ResizePane, winrt::TerminalApp::implementation::ResizePaneArgs::FromJson },
|
||||
{ ShortcutAction::ResizePaneLeft, LegacyParseResizePaneArgs(Direction::Left) },
|
||||
{ ShortcutAction::ResizePaneRight, LegacyParseResizePaneArgs(Direction::Right) },
|
||||
{ ShortcutAction::ResizePaneUp, LegacyParseResizePaneArgs(Direction::Up) },
|
||||
{ ShortcutAction::ResizePaneDown, LegacyParseResizePaneArgs(Direction::Down) },
|
||||
|
||||
{ ShortcutAction::MoveFocus, winrt::TerminalApp::implementation::MoveFocusArgs::FromJson },
|
||||
{ ShortcutAction::MoveFocusLeft, LegacyParseMoveFocusArgs(Direction::Left) },
|
||||
{ ShortcutAction::MoveFocusRight, LegacyParseMoveFocusArgs(Direction::Right) },
|
||||
{ ShortcutAction::MoveFocusUp, LegacyParseMoveFocusArgs(Direction::Up) },
|
||||
{ ShortcutAction::MoveFocusDown, LegacyParseMoveFocusArgs(Direction::Down) },
|
||||
|
||||
{ ShortcutAction::DecreaseFontSize, LegacyParseAdjustFontSizeArgs(-1) },
|
||||
{ ShortcutAction::IncreaseFontSize, LegacyParseAdjustFontSizeArgs(1) },
|
||||
|
||||
{ ShortcutAction::Invalid, nullptr },
|
||||
};
|
||||
|
||||
// Function Description:
|
||||
// - Small helper to create a json value serialization of a single
|
||||
// KeyBinding->Action maping. The created object is of schema:
|
||||
@@ -178,7 +356,7 @@ Json::Value winrt::TerminalApp::implementation::AppKeyBindings::ToJson()
|
||||
const auto searchedForName = actionName.first;
|
||||
const auto searchedForAction = actionName.second;
|
||||
|
||||
if (const auto chord{ GetKeyBinding(searchedForAction) })
|
||||
if (const auto chord{ GetKeyBindingForAction(searchedForAction) })
|
||||
{
|
||||
if (const auto serialization{ _ShortcutAsJsonObject(chord, searchedForName) })
|
||||
{
|
||||
@@ -190,6 +368,21 @@ Json::Value winrt::TerminalApp::implementation::AppKeyBindings::ToJson()
|
||||
return bindingsArray;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Attempts to match a string to a ShortcutAction. If there's no match, then
|
||||
// returns ShortcutAction::Invalid
|
||||
// Arguments:
|
||||
// - actionString: the string to match to a ShortcutAction
|
||||
// Return Value:
|
||||
// - The ShortcutAction corresponding to the given string, if a match exists.
|
||||
static ShortcutAction GetActionFromString(const std::string_view actionString)
|
||||
{
|
||||
// Try matching the command to one we have. If we can't find the
|
||||
// action name in our list of names, let's just unbind that key.
|
||||
const auto found = commandNames.find(actionString);
|
||||
return found != commandNames.end() ? found->second : ShortcutAction::Invalid;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Deserialize an AppKeyBindings from the key mappings that are in the array
|
||||
// `json`. The json array should contain an array of objects with both a
|
||||
@@ -225,18 +418,48 @@ void winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::V
|
||||
// Invalid is our placeholder that the action was not parsed.
|
||||
ShortcutAction action = ShortcutAction::Invalid;
|
||||
|
||||
// Keybindings can be serialized in two styles:
|
||||
// { "command": "switchToTab0", "keys": ["ctrl+1"] },
|
||||
// { "command": { "action": "switchToTab", "index": 0 }, "keys": ["ctrl+alt+1"] },
|
||||
|
||||
// 1. In the first case, the "command" is a string, that's the
|
||||
// action name. There are no provided args, so we'll pass
|
||||
// Json::Value::null to the parse function.
|
||||
// 2. In the second case, the "command" is an object. We'll use the
|
||||
// "action" in that object as the action name. We'll then pass
|
||||
// the "command" object to the arg parser, for furhter parsing.
|
||||
|
||||
auto argsVal = Json::Value::null;
|
||||
|
||||
// Only try to parse the action if it's actually a string value.
|
||||
// `null` will not pass this check.
|
||||
if (commandVal.isString())
|
||||
{
|
||||
auto commandString = commandVal.asString();
|
||||
|
||||
// Try matching the command to one we have. If we can't find the
|
||||
// action name in our list of names, let's just unbind that key.
|
||||
const auto found = commandNames.find(commandString);
|
||||
if (found != commandNames.end())
|
||||
action = GetActionFromString(commandString);
|
||||
}
|
||||
else if (commandVal.isObject())
|
||||
{
|
||||
const auto actionVal = commandVal[JsonKey(ActionKey)];
|
||||
if (actionVal.isString())
|
||||
{
|
||||
action = found->second;
|
||||
auto actionString = actionVal.asString();
|
||||
action = GetActionFromString(actionString);
|
||||
argsVal = commandVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Some keybindings can accept other arbitrary arguments. If it
|
||||
// does, we'll try to deserialize any "args" that were provided with
|
||||
// the binding.
|
||||
IActionArgs args{ nullptr };
|
||||
const auto deserializersIter = argParsers.find(action);
|
||||
if (deserializersIter != argParsers.end())
|
||||
{
|
||||
auto pfn = deserializersIter->second;
|
||||
if (pfn)
|
||||
{
|
||||
args = pfn(argsVal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +474,10 @@ void winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::V
|
||||
// found.
|
||||
if (action != ShortcutAction::Invalid)
|
||||
{
|
||||
SetKeyBinding(action, chord);
|
||||
auto actionAndArgs = winrt::make_self<ActionAndArgs>();
|
||||
actionAndArgs->Action(action);
|
||||
actionAndArgs->Args(args);
|
||||
SetKeyBinding(*actionAndArgs, chord);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
665
src/cascadia/TerminalApp/AppLogic.cpp
Normal file
@@ -0,0 +1,665 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppLogic.h"
|
||||
#include "AppLogic.g.cpp"
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
using IInspectable = Windows::Foundation::IInspectable;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// !!! IMPORTANT !!!
|
||||
// Make sure that these keys are in the same order as the
|
||||
// SettingsLoadWarnings/Errors enum is!
|
||||
static const std::array<std::wstring_view, 3> settingsLoadWarningsLabels {
|
||||
USES_RESOURCE(L"MissingDefaultProfileText"),
|
||||
USES_RESOURCE(L"DuplicateProfileText"),
|
||||
USES_RESOURCE(L"UnknownColorSchemeText")
|
||||
};
|
||||
static const std::array<std::wstring_view, 2> settingsLoadErrorsLabels {
|
||||
USES_RESOURCE(L"NoProfilesText"),
|
||||
USES_RESOURCE(L"AllProfilesHiddenText")
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Function Description:
|
||||
// - General-purpose helper for looking up a localized string for a
|
||||
// warning/error. First will look for the given key in the provided map of
|
||||
// keys->strings, where the values in the map are ResourceKeys. If it finds
|
||||
// one, it will lookup the localized string from that ResourceKey.
|
||||
// - If it does not find a key, it'll return an empty string
|
||||
// Arguments:
|
||||
// - key: the value to use to look for a resource key in the given map
|
||||
// - map: A map of keys->Resource keys.
|
||||
// Return Value:
|
||||
// - the localized string for the given type, if it exists.
|
||||
template<std::size_t N>
|
||||
static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_view, N> keys)
|
||||
{
|
||||
if (index < keys.size())
|
||||
{
|
||||
return GetLibraryResourceString(keys.at(index));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Gets the text from our ResourceDictionary for the given
|
||||
// SettingsLoadWarning. If there is no such text, we'll return nullptr.
|
||||
// - The warning should have an entry in settingsLoadWarningsLabels.
|
||||
// Arguments:
|
||||
// - warning: the SettingsLoadWarnings value to get the localized text for.
|
||||
// Return Value:
|
||||
// - localized text for the given warning
|
||||
static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(warning), settingsLoadWarningsLabels);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Gets the text from our ResourceDictionary for the given
|
||||
// SettingsLoadError. If there is no such text, we'll return nullptr.
|
||||
// - The warning should have an entry in settingsLoadErrorsLabels.
|
||||
// Arguments:
|
||||
// - error: the SettingsLoadErrors value to get the localized text for.
|
||||
// Return Value:
|
||||
// - localized text for the given error
|
||||
static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(error), settingsLoadErrorsLabels);
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Creates a Run of text to display an error message. The text is yellow or
|
||||
// red for dark/light theme, respectively.
|
||||
// Arguments:
|
||||
// - text: The text of the error message.
|
||||
// - resources: The application's resource loader.
|
||||
// Return Value:
|
||||
// - The fully styled text run.
|
||||
static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceDictionary& resources)
|
||||
{
|
||||
Documents::Run textRun;
|
||||
textRun.Text(text);
|
||||
|
||||
// Color the text red (light theme) or yellow (dark theme) based on the system theme
|
||||
winrt::IInspectable key = winrt::box_value(L"ErrorTextBrush");
|
||||
if (resources.HasKey(key))
|
||||
{
|
||||
winrt::IInspectable g = resources.Lookup(key);
|
||||
auto brush = g.try_as<winrt::Windows::UI::Xaml::Media::Brush>();
|
||||
textRun.Foreground(brush);
|
||||
}
|
||||
|
||||
return textRun;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
AppLogic::AppLogic() :
|
||||
_dialogLock{},
|
||||
_loadedInitialSettings{ false },
|
||||
_settingsLoadedResult{ S_OK }
|
||||
{
|
||||
// For your own sanity, it's better to do setup outside the ctor.
|
||||
// If you do any setup in the ctor that ends up throwing an exception,
|
||||
// then it might look like App just failed to activate, which will
|
||||
// cause you to chase down the rabbit hole of "why is App not
|
||||
// registered?" when it definitely is.
|
||||
|
||||
// The TerminalPage has to be constructed during our construction, to
|
||||
// make sure that there's a terminal page for callers of
|
||||
// SetTitleBarContent
|
||||
_root = winrt::make_self<TerminalPage>();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Build the UI for the terminal app. Before this method is called, it
|
||||
// should not be assumed that the TerminalApp is usable. The Settings
|
||||
// should be loaded before this is called, either with LoadSettings or
|
||||
// GetLaunchDimensions (which will call LoadSettings)
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppLogic::Create()
|
||||
{
|
||||
// Assert that we've already loaded our settings. We have to do
|
||||
// this as a MTA, before the app is Create()'d
|
||||
WINRT_ASSERT(_loadedInitialSettings);
|
||||
|
||||
_root->ShowDialog({ this, &AppLogic::_ShowDialog });
|
||||
|
||||
_root->SetSettings(_settings, false);
|
||||
_root->Loaded({ this, &AppLogic::_OnLoaded });
|
||||
_root->Create();
|
||||
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"AppCreated",
|
||||
TraceLoggingDescription("Event emitted when the application is started"),
|
||||
TraceLoggingBool(_settings->GlobalSettings().GetShowTabsInTitlebar(), "TabsInTitlebar"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Show a ContentDialog with buttons to take further action. Uses the
|
||||
// FrameworkElements provided as the title and content of this dialog, and
|
||||
// displays buttons (or a single button). Two buttons (primary and secondary)
|
||||
// will be displayed if this is an warning dialog for closing the termimal,
|
||||
// this allows the users to abondon the closing action. Otherwise, a single
|
||||
// close button will be displayed.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens.
|
||||
// Arguments:
|
||||
// sender: unused
|
||||
// dialog: the dialog object that is going to show up
|
||||
fire_and_forget AppLogic::_ShowDialog(const winrt::Windows::Foundation::IInspectable& sender, winrt::Windows::UI::Xaml::Controls::ContentDialog dialog)
|
||||
{
|
||||
// DON'T release this lock in a wil::scope_exit. The scope_exit will get
|
||||
// called when we await, which is not what we want.
|
||||
std::unique_lock lock{ _dialogLock, std::try_to_lock };
|
||||
if (!lock)
|
||||
{
|
||||
// Another dialog is visible.
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs.
|
||||
// Since we're hosting the dialog in a Xaml island, we need to connect it to the
|
||||
// xaml tree somehow.
|
||||
dialog.XamlRoot(_root->XamlRoot());
|
||||
|
||||
// IMPORTANT: Set the requested theme of the dialog, because the
|
||||
// PopupRoot isn't directly in the Xaml tree of our root. So the dialog
|
||||
// won't inherit our RequestedTheme automagically.
|
||||
dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
|
||||
// Display the dialog.
|
||||
co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
|
||||
|
||||
// After the dialog is dismissed, the dialog lock (held by `lock`) will
|
||||
// be released so another can be shown
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Displays a dialog for errors found while loading or validating the
|
||||
// settings. Uses the resources under the provided title and content keys
|
||||
// as the title and first content of the dialog, then also displays a
|
||||
// message for whatever exception was found while validating the settings.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See _ShowDialog for details
|
||||
// Arguments:
|
||||
// - titleKey: The key to use to lookup the title text from our resources.
|
||||
// - contentKey: The key to use to lookup the content text from our resources.
|
||||
void AppLogic::_ShowLoadErrorsDialog(const winrt::hstring& titleKey,
|
||||
const winrt::hstring& contentKey,
|
||||
HRESULT settingsLoadedResult)
|
||||
{
|
||||
auto title = GetLibraryResourceString(titleKey);
|
||||
auto buttonText = RS_(L"Ok");
|
||||
|
||||
Controls::TextBlock warningsTextBlock;
|
||||
// Make sure you can copy-paste
|
||||
warningsTextBlock.IsTextSelectionEnabled(true);
|
||||
// Make sure the lines of text wrap
|
||||
warningsTextBlock.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
winrt::Windows::UI::Xaml::Documents::Run errorRun;
|
||||
const auto errorLabel = GetLibraryResourceString(contentKey);
|
||||
errorRun.Text(errorLabel);
|
||||
warningsTextBlock.Inlines().Append(errorRun);
|
||||
|
||||
if (FAILED(settingsLoadedResult))
|
||||
{
|
||||
if (!_settingsLoadExceptionText.empty())
|
||||
{
|
||||
warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources()));
|
||||
}
|
||||
}
|
||||
|
||||
// Add a note that we're using the default settings in this case.
|
||||
winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun;
|
||||
const auto usingDefaultsText = RS_(L"UsingDefaultSettingsText");
|
||||
usingDefaultsRun.Text(usingDefaultsText);
|
||||
warningsTextBlock.Inlines().Append(usingDefaultsRun);
|
||||
|
||||
Controls::ContentDialog dialog;
|
||||
dialog.Title(winrt::box_value(title));
|
||||
dialog.Content(winrt::box_value(warningsTextBlock));
|
||||
dialog.CloseButtonText(buttonText);
|
||||
|
||||
_ShowDialog(nullptr, dialog);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Displays a dialog for warnings found while loading or validating the
|
||||
// settings. Displays messages for whatever warnings were found while
|
||||
// validating the settings.
|
||||
// - Only one dialog can be visible at a time. If another dialog is visible
|
||||
// when this is called, nothing happens. See _ShowDialog for details
|
||||
void AppLogic::_ShowLoadWarningsDialog()
|
||||
{
|
||||
auto title = RS_(L"SettingsValidateErrorTitle");
|
||||
auto buttonText = RS_(L"Ok");
|
||||
|
||||
Controls::TextBlock warningsTextBlock;
|
||||
// Make sure you can copy-paste
|
||||
warningsTextBlock.IsTextSelectionEnabled(true);
|
||||
// Make sure the lines of text wrap
|
||||
warningsTextBlock.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
const auto& warnings = _settings->GetWarnings();
|
||||
for (const auto& warning : warnings)
|
||||
{
|
||||
// Try looking up the warning message key for each warning.
|
||||
const auto warningText = _GetWarningText(warning);
|
||||
if (!warningText.empty())
|
||||
{
|
||||
warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources()));
|
||||
}
|
||||
}
|
||||
|
||||
Controls::ContentDialog dialog;
|
||||
dialog.Title(winrt::box_value(title));
|
||||
dialog.Content(winrt::box_value(warningsTextBlock));
|
||||
dialog.CloseButtonText(buttonText);
|
||||
|
||||
_ShowDialog(nullptr, dialog);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Triggered when the application is fiished loading. If we failed to load
|
||||
// the settings, then this will display the error dialog. This is done
|
||||
// here instead of when loading the settings, because we need our UI to be
|
||||
// visible to display the dialog, and when we're loading the settings,
|
||||
// the UI might not be visible yet.
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
void AppLogic::_OnLoaded(const IInspectable& /*sender*/,
|
||||
const RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle");
|
||||
const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText");
|
||||
_ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult);
|
||||
}
|
||||
else if (_settingsLoadedResult == S_FALSE)
|
||||
{
|
||||
_ShowLoadWarningsDialog();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the size in pixels of the client area we'll need to launch this
|
||||
// terminal app. This method will use the default profile's settings to do
|
||||
// this calculation, as well as the _system_ dpi scaling. See also
|
||||
// TermControl::GetProposedDimensions.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a point containing the requested dimensions in pixels.
|
||||
winrt::Windows::Foundation::Point AppLogic::GetLaunchDimensions(uint32_t dpi)
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
// Use the default profile to determine how big of a window we need.
|
||||
TerminalSettings settings = _settings->MakeSettings(std::nullopt);
|
||||
|
||||
// TODO MSFT:21150597 - If the global setting "Always show tab bar" is
|
||||
// set or if "Show tabs in title bar" is set, then we'll need to add
|
||||
// the height of the tab bar here.
|
||||
|
||||
return TermControl::GetProposedDimensions(settings, dpi);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the launch mode in json settings file. Now there
|
||||
// two launch mode: default, maximized. Default means the window
|
||||
// will launch according to the launch dimensions provided. Maximized
|
||||
// means the window will launch as a maximized window
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - LaunchMode enum that indicates the launch mode
|
||||
LaunchMode AppLogic::GetLaunchMode()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings->GlobalSettings().GetLaunchMode();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the user defined initial position from Json settings file.
|
||||
// This position represents the top left corner of the Terminal window.
|
||||
// This setting is optional, if not provided, we will use the system
|
||||
// default size, which is provided in IslandWindow::MakeWindow.
|
||||
// Arguments:
|
||||
// - defaultInitialX: the system default x coordinate value
|
||||
// - defaultInitialY: the system defualt y coordinate value
|
||||
// Return Value:
|
||||
// - a point containing the requested initial position in pixels.
|
||||
winrt::Windows::Foundation::Point AppLogic::GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY)
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Point point((float)defaultInitialX, (float)defaultInitialY);
|
||||
|
||||
auto initialX = _settings->GlobalSettings().GetInitialX();
|
||||
auto initialY = _settings->GlobalSettings().GetInitialY();
|
||||
if (initialX.has_value())
|
||||
{
|
||||
point.X = gsl::narrow_cast<float>(initialX.value());
|
||||
}
|
||||
if (initialY.has_value())
|
||||
{
|
||||
point.Y = gsl::narrow_cast<float>(initialY.value());
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings->GlobalSettings().GetRequestedTheme();
|
||||
}
|
||||
|
||||
bool AppLogic::GetShowTabsInTitlebar()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings->GlobalSettings().GetShowTabsInTitlebar();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to load the settings. If we fail for any reason, returns an error.
|
||||
// Return Value:
|
||||
// - S_OK if we successfully parsed the settings, otherwise an appropriate HRESULT.
|
||||
[[nodiscard]] HRESULT AppLogic::_TryLoadSettings() noexcept
|
||||
{
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
try
|
||||
{
|
||||
auto newSettings = CascadiaSettings::LoadAll();
|
||||
_settings = std::move(newSettings);
|
||||
const auto& warnings = _settings->GetWarnings();
|
||||
hr = warnings.size() == 0 ? S_OK : S_FALSE;
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
hr = e.code();
|
||||
_settingsLoadExceptionText = e.message();
|
||||
LOG_HR(hr);
|
||||
}
|
||||
catch (const ::TerminalApp::SettingsException& ex)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
_settingsLoadExceptionText = _GetErrorText(ex.Error());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
hr = wil::ResultFromCaughtException();
|
||||
LOG_HR(hr);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initialized our settings. See CascadiaSettings for more details.
|
||||
// Additionally hooks up our callbacks for keybinding events to the
|
||||
// keybindings object.
|
||||
// NOTE: This must be called from a MTA if we're running as a packaged
|
||||
// application. The Windows.Storage APIs require a MTA. If this isn't
|
||||
// happening during startup, it'll need to happen on a background thread.
|
||||
void AppLogic::LoadSettings()
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"SettingsLoadStarted",
|
||||
TraceLoggingDescription("Event emitted before loading the settings"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
// Attempt to load the settings.
|
||||
// If it fails,
|
||||
// - use Default settings,
|
||||
// - don't persist them (LoadAll won't save them in this case).
|
||||
// - _settingsLoadedResult will be set to an error, indicating that
|
||||
// we should display the loading error.
|
||||
// * We can't display the error now, because we might not have a
|
||||
// UI yet. We'll display the error in _OnLoaded.
|
||||
_settingsLoadedResult = _TryLoadSettings();
|
||||
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
_settings = CascadiaSettings::LoadDefaults();
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> delta = end - start;
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"SettingsLoadComplete",
|
||||
TraceLoggingDescription("Event emitted when loading the settings is finished"),
|
||||
TraceLoggingFloat64(delta.count(), "Duration"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
_loadedInitialSettings = true;
|
||||
|
||||
// Register for directory change notification.
|
||||
_RegisterSettingsChange();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Registers for changes to the settings folder and upon a updated settings
|
||||
// profile calls _ReloadSettings().
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppLogic::_RegisterSettingsChange()
|
||||
{
|
||||
// Get the containing folder.
|
||||
std::filesystem::path settingsPath{ CascadiaSettings::GetSettingsPath() };
|
||||
const auto folder = settingsPath.parent_path();
|
||||
|
||||
_reader.create(folder.c_str(),
|
||||
false,
|
||||
wil::FolderChangeEvents::All,
|
||||
[this, settingsPath](wil::FolderChangeEvent event, PCWSTR fileModified) {
|
||||
// We want file modifications, AND when files are renamed to be
|
||||
// profiles.json. This second case will oftentimes happen with text
|
||||
// editors, who will write a temp file, then rename it to be the
|
||||
// actual file you wrote. So listen for that too.
|
||||
if (!(event == wil::FolderChangeEvent::Modified ||
|
||||
event == wil::FolderChangeEvent::RenameNewName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path modifiedFilePath = fileModified;
|
||||
|
||||
// Getting basename (filename.ext)
|
||||
const auto settingsBasename = settingsPath.filename();
|
||||
const auto modifiedBasename = modifiedFilePath.filename();
|
||||
|
||||
if (settingsBasename == modifiedBasename)
|
||||
{
|
||||
this->_DispatchReloadSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Dispatches a settings reload with debounce.
|
||||
// Text editors implement Save in a bunch of different ways, so
|
||||
// this stops us from reloading too many times or too quickly.
|
||||
fire_and_forget AppLogic::_DispatchReloadSettings()
|
||||
{
|
||||
static constexpr auto FileActivityQuiesceTime{ std::chrono::milliseconds(50) };
|
||||
if (!_settingsReloadQueued.exchange(true))
|
||||
{
|
||||
co_await winrt::resume_after(FileActivityQuiesceTime);
|
||||
_ReloadSettings();
|
||||
_settingsReloadQueued.store(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Reloads the settings from the profile.json.
|
||||
void AppLogic::_ReloadSettings()
|
||||
{
|
||||
// Attempt to load our settings.
|
||||
// If it fails,
|
||||
// - don't change the settings (and don't actually apply the new settings)
|
||||
// - don't persist them.
|
||||
// - display a loading error
|
||||
_settingsLoadedResult = _TryLoadSettings();
|
||||
|
||||
if (FAILED(_settingsLoadedResult))
|
||||
{
|
||||
_root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle");
|
||||
const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText");
|
||||
_ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
else if (_settingsLoadedResult == S_FALSE)
|
||||
{
|
||||
_root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
_ShowLoadWarningsDialog();
|
||||
});
|
||||
}
|
||||
|
||||
// Here, we successfully reloaded the settings, and created a new
|
||||
// TerminalSettings object.
|
||||
|
||||
// Update the settings in TerminalPage
|
||||
_root->SetSettings(_settings, true);
|
||||
|
||||
_root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
// Refresh the UI theme
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void AppLogic::_ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme)
|
||||
{
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_requestedThemeChangedHandlers(*this, newTheme);
|
||||
}
|
||||
|
||||
UIElement AppLogic::GetRoot() noexcept
|
||||
{
|
||||
return _root.as<winrt::Windows::UI::Xaml::Controls::Control>();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the title of the currently focused terminal control. If there
|
||||
// isn't a control selected for any reason, returns "Windows Terminal"
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the title of the focused control if there is one, else "Windows Terminal"
|
||||
hstring AppLogic::Title()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->Title();
|
||||
}
|
||||
return { L"Windows Terminal" };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Used to tell the app that the titlebar has been clicked. The App won't
|
||||
// actually recieve any clicks in the titlebar area, so this is a helper
|
||||
// to clue the app in that a click has happened. The App will use this as
|
||||
// a indicator that it needs to dismiss any open flyouts.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppLogic::TitlebarClicked()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->TitlebarClicked();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Used to tell the app that the 'X' button has been clicked and
|
||||
// the user wants to close the app. We kick off the close warning
|
||||
// experience.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppLogic::WindowCloseButtonClicked()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->CloseWindow();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// Winrt events need a method for adding a callback to the event and removing the callback.
|
||||
// These macros will define them both for you.
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(AppLogic, RequestedThemeChanged, _requestedThemeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
}
|
||||
98
src/cascadia/TerminalApp/AppLogic.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AppLogic.g.h"
|
||||
|
||||
#include "Tab.h"
|
||||
#include "CascadiaSettings.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct AppLogic : AppLogicT<AppLogic>
|
||||
{
|
||||
public:
|
||||
AppLogic();
|
||||
~AppLogic() = default;
|
||||
|
||||
void Create();
|
||||
void LoadSettings();
|
||||
|
||||
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
|
||||
winrt::Windows::Foundation::Point GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY);
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
LaunchMode GetLaunchMode();
|
||||
bool GetShowTabsInTitlebar();
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
|
||||
hstring Title();
|
||||
void TitlebarClicked();
|
||||
|
||||
void WindowCloseButtonClicked();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RequestedThemeChanged, _requestedThemeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
|
||||
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 AppLogic.
|
||||
// ALSO: If you add any UIElements as roots here, make sure they're
|
||||
// updated in _AppLogiclyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
|
||||
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
|
||||
|
||||
HRESULT _settingsLoadedResult;
|
||||
winrt::hstring _settingsLoadExceptionText{};
|
||||
|
||||
bool _loadedInitialSettings;
|
||||
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
std::shared_mutex _dialogLock;
|
||||
|
||||
std::atomic<bool> _settingsReloadQueued{ false };
|
||||
|
||||
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 _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
[[nodiscard]] HRESULT _TryLoadSettings() noexcept;
|
||||
void _RegisterSettingsChange();
|
||||
fire_and_forget _DispatchReloadSettings();
|
||||
void _ReloadSettings();
|
||||
|
||||
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
|
||||
|
||||
// These are events that are handled by the TerminalPage, but are
|
||||
// exposed through the AppLogic. This macro is used to forward the event
|
||||
// directly to them.
|
||||
FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent);
|
||||
FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged);
|
||||
FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed);
|
||||
FORWARDED_TYPED_EVENT(ToggleFullscreen, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::ToggleFullscreenEventArgs, _root, ToggleFullscreen);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
struct AppLogic : AppLogicT<AppLogic, implementation::AppLogic>
|
||||
{
|
||||
};
|
||||
}
|
||||
44
src/cascadia/TerminalApp/AppLogic.idl
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "../TerminalPage.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
enum LaunchMode
|
||||
{
|
||||
DefaultMode,
|
||||
MaximizedMode,
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AppLogic
|
||||
{
|
||||
AppLogic();
|
||||
|
||||
// For your own sanity, it's better to do setup outside the ctor.
|
||||
// If you do any setup in the ctor that ends up throwing an exception,
|
||||
// then it might look like TermApp just failed to activate, which will
|
||||
// cause you to chase down the rabbit hole of "why is TermApp not
|
||||
// registered?" when it definitely is.
|
||||
void Create();
|
||||
|
||||
void LoadSettings();
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
|
||||
String Title { get; };
|
||||
|
||||
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
|
||||
Windows.Foundation.Point GetLaunchInitialPositions(Int32 defaultInitialX, Int32 defaultInitialY);
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
LaunchMode GetLaunchMode();
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
void TitlebarClicked();
|
||||
void WindowCloseButtonClicked();
|
||||
|
||||
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<Object, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ToggleFullscreenEventArgs> ToggleFullscreen;
|
||||
}
|
||||
}
|
||||
@@ -174,13 +174,18 @@ void CascadiaSettings::_ValidateSettings()
|
||||
_ValidateNoDuplicateProfiles();
|
||||
_ValidateDefaultProfileExists();
|
||||
|
||||
// TODO:GH#2547 ensure that all the profile's color scheme names are
|
||||
// Ensure that all the profile's color scheme names are
|
||||
// actually the names of schemes we've parsed. If the scheme doesn't exist,
|
||||
// just use the hardcoded defaults
|
||||
_ValidateAllSchemesExist();
|
||||
|
||||
// TODO:GH#2548 ensure there's at least one key bound. Display a warning if
|
||||
// there's _NO_ keys bound to any actions. That's highly irregular, and
|
||||
// likely an indication of an error somehow.
|
||||
|
||||
// TODO:GH#3522 With variable args to keybindings, it's possible that a user
|
||||
// set a keybinding without all the required args for an action. Display a
|
||||
// warning if an action didn't have a required arg.
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -355,4 +360,47 @@ void CascadiaSettings::_RemoveHiddenProfiles()
|
||||
_profiles.end(),
|
||||
[](auto&& profile) { return profile.IsHidden(); }),
|
||||
_profiles.end());
|
||||
|
||||
// Ensure that we still have some profiles here. If we don't, then throw an
|
||||
// exception, so the app can use the defaults.
|
||||
const bool hasProfiles = !_profiles.empty();
|
||||
if (!hasProfiles)
|
||||
{
|
||||
// Throw an exception. This is an invalid state, and we want the app to
|
||||
// be able to gracefully use the default settings.
|
||||
throw ::TerminalApp::SettingsException(::TerminalApp::SettingsLoadErrors::AllProfilesHidden);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Ensures that every profile has a valid "color scheme" set. If any profile
|
||||
// has a colorScheme set to a value which is _not_ the name of an actual color
|
||||
// scheme, we'll set the color table of the profile to something reasonable.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
// - Appends a SettingsLoadWarnings::UnknownColorScheme to our list of warnings if
|
||||
// we find any such duplicate.
|
||||
void CascadiaSettings::_ValidateAllSchemesExist()
|
||||
{
|
||||
bool foundInvalidScheme = false;
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
auto schemeName = profile.GetSchemeName();
|
||||
if (schemeName.has_value())
|
||||
{
|
||||
const auto found = _globals.GetColorSchemes().find(schemeName.value());
|
||||
if (found == _globals.GetColorSchemes().end())
|
||||
{
|
||||
profile.SetColorScheme({ L"Campbell" });
|
||||
foundInvalidScheme = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundInvalidScheme)
|
||||
{
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ public:
|
||||
|
||||
static std::unique_ptr<CascadiaSettings> LoadDefaults();
|
||||
static std::unique_ptr<CascadiaSettings> LoadAll();
|
||||
void SaveAll() const;
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::TerminalSettings MakeSettings(std::optional<GUID> profileGuid) const;
|
||||
|
||||
@@ -58,7 +57,6 @@ public:
|
||||
|
||||
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
|
||||
|
||||
Json::Value ToJson() const;
|
||||
static std::unique_ptr<CascadiaSettings> FromJson(const Json::Value& json);
|
||||
void LayerJson(const Json::Value& json);
|
||||
|
||||
@@ -87,6 +85,7 @@ private:
|
||||
void _ParseJsonString(std::string_view fileData, const bool isDefaultSettings);
|
||||
static const Json::Value& _GetProfilesJsonObject(const Json::Value& json);
|
||||
static const Json::Value& _GetDisabledProfileSourcesJsonObject(const Json::Value& json);
|
||||
bool _PrependSchemaDirective();
|
||||
bool _AppendDynamicProfilesToUserSettings();
|
||||
|
||||
void _LoadDynamicProfiles();
|
||||
@@ -103,6 +102,7 @@ private:
|
||||
void _ValidateNoDuplicateProfiles();
|
||||
void _ReorderProfilesToMatchUserSettingsOrder();
|
||||
void _RemoveHiddenProfiles();
|
||||
void _ValidateAllSchemesExist();
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ProfileTests;
|
||||
|
||||
@@ -27,6 +27,7 @@ static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Wi
|
||||
|
||||
static constexpr std::wstring_view DefaultsFilename{ L"defaults.json" };
|
||||
|
||||
static constexpr std::string_view SchemaKey{ "$schema" };
|
||||
static constexpr std::string_view ProfilesKey{ "profiles" };
|
||||
static constexpr std::string_view KeybindingsKey{ "keybindings" };
|
||||
static constexpr std::string_view GlobalsKey{ "globals" };
|
||||
@@ -36,6 +37,8 @@ static constexpr std::string_view DisabledProfileSourcesKey{ "disabledProfileSou
|
||||
|
||||
static constexpr std::string_view Utf8Bom{ u8"\uFEFF" };
|
||||
static constexpr std::string_view DefaultProfilesIndentation{ " " };
|
||||
static constexpr std::string_view SettingsSchemaFragment{ "\n"
|
||||
R"( "$schema": "https://aka.ms/terminal-profiles-schema")" };
|
||||
|
||||
// Method Description:
|
||||
// - Creates a CascadiaSettings from whatever's saved on disk, or instantiates
|
||||
@@ -85,6 +88,16 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// that need to be inserted into their user settings file.
|
||||
needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
|
||||
|
||||
if (needToWriteFile)
|
||||
{
|
||||
// For safety's sake, we need to re-parse the JSON document to ensure that
|
||||
// all future patches are applied with updated object offsets.
|
||||
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
|
||||
}
|
||||
|
||||
// Make sure there's a $schema at the top of the file.
|
||||
needToWriteFile = resultPtr->_PrependSchemaDirective() || needToWriteFile;
|
||||
|
||||
// TODO:GH#2721 If powershell core is installed, we need to set that to the
|
||||
// default profile, but only when the settings file was newly created. We'll
|
||||
// re-write the segment of the user settings for "default profile" to have
|
||||
@@ -228,22 +241,32 @@ void CascadiaSettings::_ParseJsonString(std::string_view fileData, const bool is
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Serialize this settings structure, and save it to a file. The location of
|
||||
// the file changes depending whether we're running as a packaged
|
||||
// application or not.
|
||||
// - Determines whether the user's settings file is missing a schema directive
|
||||
// and, if so, inserts one.
|
||||
// - Assumes that the body of the root object is at an indentation of 4 spaces, and
|
||||
// therefore each member should be indented 4 spaces. If the user's settings
|
||||
// have a different indentation, we'll still insert valid json, it'll just be
|
||||
// indented incorrectly.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CascadiaSettings::SaveAll() const
|
||||
// - true iff we've made changes to the _userSettingsString that should be persisted.
|
||||
bool CascadiaSettings::_PrependSchemaDirective()
|
||||
{
|
||||
const auto json = ToJson();
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
// Use 4 spaces to indent instead of \t
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
const auto serializedString = Json::writeString(wbuilder, json);
|
||||
if (_userSettings.isMember(JsonKey(SchemaKey)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_WriteSettings(serializedString);
|
||||
// start points at the opening { for the root object.
|
||||
auto offset = _userSettings.getOffsetStart() + 1;
|
||||
_userSettingsString.insert(offset, SettingsSchemaFragment);
|
||||
offset += SettingsSchemaFragment.size();
|
||||
if (_userSettings.size() > 0)
|
||||
{
|
||||
_userSettingsString.insert(offset, ",");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -276,6 +299,7 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
// Use 4 spaces to indent instead of \t
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
|
||||
|
||||
auto isInJsonObj = [](const auto& profile, const auto& json) {
|
||||
for (auto profileJson : _GetProfilesJsonObject(json))
|
||||
@@ -358,36 +382,6 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
||||
return changedFile;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Serialize this object to a JsonObject.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a JsonObject which is an equivalent serialization of this object.
|
||||
Json::Value CascadiaSettings::ToJson() const
|
||||
{
|
||||
Json::Value root;
|
||||
|
||||
Json::Value profilesArray;
|
||||
for (const auto& profile : _profiles)
|
||||
{
|
||||
profilesArray.append(profile.ToJson());
|
||||
}
|
||||
|
||||
Json::Value schemesArray;
|
||||
const auto& colorSchemes = _globals.GetColorSchemes();
|
||||
for (auto& scheme : colorSchemes)
|
||||
{
|
||||
schemesArray.append(scheme.ToJson());
|
||||
}
|
||||
|
||||
root[GlobalsKey.data()] = _globals.ToJson();
|
||||
root[ProfilesKey.data()] = profilesArray;
|
||||
root[SchemesKey.data()] = schemesArray;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a new instance of this class from a serialized JsonObject.
|
||||
// Arguments:
|
||||
@@ -413,6 +407,11 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value&
|
||||
// <none>
|
||||
void CascadiaSettings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// microsoft/terminal#2906: First layer the root object as our globals. If
|
||||
// there is also a `globals` object, layer that one on top of the settings
|
||||
// from the root.
|
||||
_globals.LayerJson(json);
|
||||
|
||||
if (auto globals{ json[GlobalsKey.data()] })
|
||||
{
|
||||
if (globals.isObject())
|
||||
@@ -420,13 +419,6 @@ void CascadiaSettings::LayerJson(const Json::Value& json)
|
||||
_globals.LayerJson(globals);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's no globals key in the root object, then try looking at the
|
||||
// root object for those properties instead, to gracefully upgrade.
|
||||
// This will attempt to do the legacy keybindings loading too
|
||||
_globals.LayerJson(json);
|
||||
}
|
||||
|
||||
if (auto schemes{ json[SchemesKey.data()] })
|
||||
{
|
||||
@@ -526,16 +518,15 @@ void CascadiaSettings::_LayerOrCreateColorScheme(const Json::Value& schemeJson)
|
||||
}
|
||||
else
|
||||
{
|
||||
auto scheme = ColorScheme::FromJson(schemeJson);
|
||||
_globals.GetColorSchemes().emplace_back(scheme);
|
||||
_globals.AddColorScheme(ColorScheme::FromJson(schemeJson));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Finds a color scheme from our list of color schemes that matches the given
|
||||
// json object. Uses ColorScheme::ShouldBeLayered to determine if the
|
||||
// Json::Value is a match or not. This method should be used to find a color
|
||||
// scheme to layer the given settings upon.
|
||||
// json object. Uses ColorScheme::GetNameFromJson to find the name and then
|
||||
// performs a lookup in the global map. This method should be used to find a
|
||||
// color scheme to layer the given settings upon.
|
||||
// - Returns nullptr if no such match exists.
|
||||
// Arguments:
|
||||
// - json: an object which should be a partial serialization of a ColorScheme object.
|
||||
@@ -544,15 +535,17 @@ void CascadiaSettings::_LayerOrCreateColorScheme(const Json::Value& schemeJson)
|
||||
// color scheme exists.
|
||||
ColorScheme* CascadiaSettings::_FindMatchingColorScheme(const Json::Value& schemeJson)
|
||||
{
|
||||
for (auto& scheme : _globals.GetColorSchemes())
|
||||
if (auto schemeName = ColorScheme::GetNameFromJson(schemeJson))
|
||||
{
|
||||
if (scheme.ShouldBeLayered(schemeJson))
|
||||
auto& schemes = _globals.GetColorSchemes();
|
||||
auto iterator = schemes.find(*schemeName);
|
||||
if (iterator != schemes.end())
|
||||
{
|
||||
// HERE BE DRAGONS: Returning a pointer to a type in the vector is
|
||||
// maybe not the _safest_ thing, but we have a mind to make Profile
|
||||
// and ColorScheme winrt types in the future, so this will be safer
|
||||
// then.
|
||||
return &scheme;
|
||||
return &iterator->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "ColorScheme.h"
|
||||
#include "DefaultSettings.h"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
@@ -16,6 +17,7 @@ static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view TableKey{ "colors" };
|
||||
static constexpr std::string_view ForegroundKey{ "foreground" };
|
||||
static constexpr std::string_view BackgroundKey{ "background" };
|
||||
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
|
||||
static constexpr std::array<std::string_view, 16> TableColors = {
|
||||
"black",
|
||||
"red",
|
||||
@@ -38,8 +40,9 @@ static constexpr std::array<std::string_view, 16> TableColors = {
|
||||
ColorScheme::ColorScheme() :
|
||||
_schemeName{ L"" },
|
||||
_table{},
|
||||
_defaultForeground{ RGB(242, 242, 242) },
|
||||
_defaultBackground{ RGB(12, 12, 12) }
|
||||
_defaultForeground{ DEFAULT_FOREGROUND_WITH_ALPHA },
|
||||
_defaultBackground{ DEFAULT_BACKGROUND_WITH_ALPHA },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -47,7 +50,8 @@ ColorScheme::ColorScheme(std::wstring name, COLORREF defaultFg, COLORREF default
|
||||
_schemeName{ name },
|
||||
_table{},
|
||||
_defaultForeground{ defaultFg },
|
||||
_defaultBackground{ defaultBg }
|
||||
_defaultBackground{ defaultBg },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -66,6 +70,7 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const
|
||||
{
|
||||
terminalSettings.DefaultForeground(_defaultForeground);
|
||||
terminalSettings.DefaultBackground(_defaultBackground);
|
||||
terminalSettings.SelectionBackground(_selectionBackground);
|
||||
|
||||
auto const tableCount = gsl::narrow_cast<int>(_table.size());
|
||||
for (int i = 0; i < tableCount; i++)
|
||||
@@ -86,6 +91,7 @@ Json::Value ColorScheme::ToJson() const
|
||||
root[JsonKey(NameKey)] = winrt::to_string(_schemeName);
|
||||
root[JsonKey(ForegroundKey)] = Utils::ColorToHexString(_defaultForeground);
|
||||
root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground);
|
||||
root[JsonKey(SelectionBackgroundKey)] = Utils::ColorToHexString(_selectionBackground);
|
||||
|
||||
int i = 0;
|
||||
for (const auto& colorName : TableColors)
|
||||
@@ -155,6 +161,11 @@ void ColorScheme::LayerJson(const Json::Value& json)
|
||||
const auto color = Utils::ColorFromHexString(bgString.asString());
|
||||
_defaultBackground = color;
|
||||
}
|
||||
if (auto sbString{ json[JsonKey(SelectionBackgroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_selectionBackground = color;
|
||||
}
|
||||
|
||||
// Legacy Deserialization. Leave in place to allow forward compatibility
|
||||
if (auto table{ json[JsonKey(TableKey)] })
|
||||
@@ -203,3 +214,25 @@ COLORREF ColorScheme::GetBackground() const noexcept
|
||||
{
|
||||
return _defaultBackground;
|
||||
}
|
||||
|
||||
COLORREF ColorScheme::GetSelectionBackground() const noexcept
|
||||
{
|
||||
return _selectionBackground;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Parse the name from the JSON representation of a ColorScheme.
|
||||
// Arguments:
|
||||
// - json: an object which should be a serialization of a ColorScheme object.
|
||||
// Return Value:
|
||||
// - the name of the color scheme represented by `json` as a std::wstring optional
|
||||
// i.e. the value of the `name` property.
|
||||
// - returns std::nullopt if `json` doesn't have the `name` property
|
||||
std::optional<std::wstring> TerminalApp::ColorScheme::GetNameFromJson(const Json::Value& json)
|
||||
{
|
||||
if (const auto name{ json[JsonKey(NameKey)] })
|
||||
{
|
||||
return GetWstringFromJson(name);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -49,12 +49,16 @@ public:
|
||||
std::array<COLORREF, COLOR_TABLE_SIZE>& GetTable() noexcept;
|
||||
COLORREF GetForeground() const noexcept;
|
||||
COLORREF GetBackground() const noexcept;
|
||||
COLORREF GetSelectionBackground() const noexcept;
|
||||
|
||||
static std::optional<std::wstring> GetNameFromJson(const Json::Value& json);
|
||||
|
||||
private:
|
||||
std::wstring _schemeName;
|
||||
std::array<COLORREF, COLOR_TABLE_SIZE> _table;
|
||||
COLORREF _defaultForeground;
|
||||
COLORREF _defaultBackground;
|
||||
COLORREF _selectionBackground;
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
@@ -20,12 +21,15 @@ static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
|
||||
static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" };
|
||||
static constexpr std::string_view InitialRowsKey{ "initialRows" };
|
||||
static constexpr std::string_view InitialColsKey{ "initialCols" };
|
||||
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
|
||||
static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" };
|
||||
static constexpr std::string_view RequestedThemeKey{ "requestedTheme" };
|
||||
static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" };
|
||||
static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" };
|
||||
static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" };
|
||||
|
||||
static constexpr std::string_view LaunchModeKey{ "launchMode" };
|
||||
static constexpr std::wstring_view DefaultLaunchModeValue{ L"default" };
|
||||
static constexpr std::wstring_view MaximizedLaunchModeValue{ L"maximized" };
|
||||
static constexpr std::wstring_view LightThemeValue{ L"light" };
|
||||
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
|
||||
static constexpr std::wstring_view SystemThemeValue{ L"system" };
|
||||
@@ -37,11 +41,14 @@ GlobalAppSettings::GlobalAppSettings() :
|
||||
_alwaysShowTabs{ true },
|
||||
_initialRows{ DEFAULT_ROWS },
|
||||
_initialCols{ DEFAULT_COLS },
|
||||
_initialX{},
|
||||
_initialY{},
|
||||
_showTitleInTitlebar{ true },
|
||||
_showTabsInTitlebar{ true },
|
||||
_requestedTheme{ ElementTheme::Default },
|
||||
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
|
||||
_copyOnSelect{ false }
|
||||
_copyOnSelect{ false },
|
||||
_launchMode{ LaunchMode::DefaultMode }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -49,12 +56,12 @@ GlobalAppSettings::~GlobalAppSettings()
|
||||
{
|
||||
}
|
||||
|
||||
const std::vector<ColorScheme>& GlobalAppSettings::GetColorSchemes() const noexcept
|
||||
std::unordered_map<std::wstring, ColorScheme>& GlobalAppSettings::GetColorSchemes() noexcept
|
||||
{
|
||||
return _colorSchemes;
|
||||
}
|
||||
|
||||
std::vector<ColorScheme>& GlobalAppSettings::GetColorSchemes() noexcept
|
||||
const std::unordered_map<std::wstring, ColorScheme>& GlobalAppSettings::GetColorSchemes() const noexcept
|
||||
{
|
||||
return _colorSchemes;
|
||||
}
|
||||
@@ -124,6 +131,16 @@ void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept
|
||||
_copyOnSelect = copyOnSelect;
|
||||
}
|
||||
|
||||
LaunchMode GlobalAppSettings::GetLaunchMode() const noexcept
|
||||
{
|
||||
return _launchMode;
|
||||
}
|
||||
|
||||
void GlobalAppSettings::SetLaunchMode(const LaunchMode launchMode)
|
||||
{
|
||||
_launchMode = launchMode;
|
||||
}
|
||||
|
||||
#pragma region ExperimentalSettings
|
||||
bool GlobalAppSettings::GetShowTabsInTitlebar() const noexcept
|
||||
{
|
||||
@@ -134,6 +151,17 @@ void GlobalAppSettings::SetShowTabsInTitlebar(const bool showTabsInTitlebar) noe
|
||||
{
|
||||
_showTabsInTitlebar = showTabsInTitlebar;
|
||||
}
|
||||
|
||||
std::optional<int32_t> GlobalAppSettings::GetInitialX() const noexcept
|
||||
{
|
||||
return _initialX;
|
||||
}
|
||||
|
||||
std::optional<int32_t> GlobalAppSettings::GetInitialY() const noexcept
|
||||
{
|
||||
return _initialY;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
// Method Description:
|
||||
@@ -147,6 +175,7 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce
|
||||
settings.KeyBindings(GetKeybindings());
|
||||
settings.InitialRows(_initialRows);
|
||||
settings.InitialCols(_initialCols);
|
||||
|
||||
settings.WordDelimiters(_wordDelimiters);
|
||||
settings.CopyOnSelect(_copyOnSelect);
|
||||
}
|
||||
@@ -164,11 +193,13 @@ Json::Value GlobalAppSettings::ToJson() const
|
||||
jsonObject[JsonKey(DefaultProfileKey)] = winrt::to_string(Utils::GuidToString(_defaultProfile));
|
||||
jsonObject[JsonKey(InitialRowsKey)] = _initialRows;
|
||||
jsonObject[JsonKey(InitialColsKey)] = _initialCols;
|
||||
jsonObject[JsonKey(InitialPositionKey)] = _SerializeInitialPosition(_initialX, _initialY);
|
||||
jsonObject[JsonKey(AlwaysShowTabsKey)] = _alwaysShowTabs;
|
||||
jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar;
|
||||
jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar;
|
||||
jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters);
|
||||
jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect;
|
||||
jsonObject[JsonKey(LaunchModeKey)] = winrt::to_string(_SerializeLaunchMode(_launchMode));
|
||||
jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme));
|
||||
jsonObject[JsonKey(KeybindingsKey)] = _keybindings->ToJson();
|
||||
|
||||
@@ -208,7 +239,10 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
_initialCols = initialCols.asInt();
|
||||
}
|
||||
|
||||
if (auto initialPosition{ json[JsonKey(InitialPositionKey)] })
|
||||
{
|
||||
_ParseInitialPosition(GetWstringFromJson(initialPosition), _initialX, _initialY);
|
||||
}
|
||||
if (auto showTitleInTitlebar{ json[JsonKey(ShowTitleInTitlebarKey)] })
|
||||
{
|
||||
_showTitleInTitlebar = showTitleInTitlebar.asBool();
|
||||
@@ -229,6 +263,11 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
_copyOnSelect = copyOnSelect.asBool();
|
||||
}
|
||||
|
||||
if (auto launchMode{ json[JsonKey(LaunchModeKey)] })
|
||||
{
|
||||
_launchMode = _ParseLaunchMode(GetWstringFromJson(launchMode));
|
||||
}
|
||||
|
||||
if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] })
|
||||
{
|
||||
_requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme));
|
||||
@@ -280,3 +319,126 @@ std::wstring_view GlobalAppSettings::_SerializeTheme(const ElementTheme theme) n
|
||||
return SystemThemeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the initial position string into
|
||||
// 2 coordinate values. We allow users to only provide one coordinate,
|
||||
// thus, we use comma as the separater:
|
||||
// (100, 100): standard input string
|
||||
// (, 100), (100, ): if a value is missing, we set this value as a default
|
||||
// (,): both x and y are set to default
|
||||
// (abc, 100): if a value is not valid, we treat it as default
|
||||
// (100, 100, 100): we only read the first two values, this is equivalent to (100, 100)
|
||||
// Arguments:
|
||||
// - initialPosition: the initial position string from json
|
||||
// initialX: reference to the _initialX member
|
||||
// initialY: reference to the _initialY member
|
||||
// Return Value:
|
||||
// - None
|
||||
void GlobalAppSettings::_ParseInitialPosition(const std::wstring& initialPosition,
|
||||
std::optional<int32_t>& initialX,
|
||||
std::optional<int32_t>& initialY) noexcept
|
||||
{
|
||||
const wchar_t singleCharDelim = L',';
|
||||
std::wstringstream tokenStream(initialPosition);
|
||||
std::wstring token;
|
||||
uint8_t initialPosIndex = 0;
|
||||
|
||||
// Get initial position values till we run out of delimiter separated values in the stream
|
||||
// or we hit max number of allowable values (= 2)
|
||||
// Non-numeral values or empty string will be caught as exception and we do not assign them
|
||||
for (; std::getline(tokenStream, token, singleCharDelim) && (initialPosIndex < 2); initialPosIndex++)
|
||||
{
|
||||
try
|
||||
{
|
||||
int32_t position = std::stoi(token);
|
||||
if (initialPosIndex == 0)
|
||||
{
|
||||
initialX.emplace(position);
|
||||
}
|
||||
|
||||
if (initialPosIndex == 1)
|
||||
{
|
||||
initialY.emplace(position);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting X/Y initial positions to a string
|
||||
// value.
|
||||
// Arguments:
|
||||
// - initialX: reference to the _initialX member
|
||||
// initialY: reference to the _initialY member
|
||||
// Return Value:
|
||||
// - The concatenated string for the the current initialX and initialY
|
||||
std::string GlobalAppSettings::_SerializeInitialPosition(const std::optional<int32_t>& initialX,
|
||||
const std::optional<int32_t>& initialY) noexcept
|
||||
{
|
||||
std::string serializedInitialPos = "";
|
||||
if (initialX.has_value())
|
||||
{
|
||||
serializedInitialPos += std::to_string(initialX.value());
|
||||
}
|
||||
|
||||
serializedInitialPos += ", ";
|
||||
|
||||
if (initialY.has_value())
|
||||
{
|
||||
serializedInitialPos += std::to_string(initialY.value());
|
||||
}
|
||||
|
||||
return serializedInitialPos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the user-specified launch mode
|
||||
// to a LaunchMode enum value
|
||||
// Arguments:
|
||||
// - launchModeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
LaunchMode GlobalAppSettings::_ParseLaunchMode(const std::wstring& launchModeString) noexcept
|
||||
{
|
||||
if (launchModeString == MaximizedLaunchModeValue)
|
||||
{
|
||||
return LaunchMode::MaximizedMode;
|
||||
}
|
||||
|
||||
return LaunchMode::DefaultMode;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a LaunchMode to its corresponding string
|
||||
// value.
|
||||
// Arguments:
|
||||
// - launchMode: The enum value to convert to a string.
|
||||
// Return Value:
|
||||
// - The string value for the given LaunchMode
|
||||
std::wstring_view GlobalAppSettings::_SerializeLaunchMode(const LaunchMode launchMode) noexcept
|
||||
{
|
||||
switch (launchMode)
|
||||
{
|
||||
case LaunchMode::MaximizedMode:
|
||||
return MaximizedLaunchModeValue;
|
||||
default:
|
||||
return DefaultLaunchModeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds the given colorscheme to our map of schemes, using its name as the key.
|
||||
// Arguments:
|
||||
// - scheme: the color scheme to add
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void GlobalAppSettings::AddColorScheme(ColorScheme scheme)
|
||||
{
|
||||
std::wstring name{ scheme.GetName() };
|
||||
_colorSchemes[name] = std::move(scheme);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ Author(s):
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class ColorSchemeTests;
|
||||
};
|
||||
|
||||
namespace TerminalApp
|
||||
@@ -34,8 +35,10 @@ public:
|
||||
GlobalAppSettings();
|
||||
~GlobalAppSettings();
|
||||
|
||||
const std::vector<ColorScheme>& GetColorSchemes() const noexcept;
|
||||
std::vector<ColorScheme>& GetColorSchemes() noexcept;
|
||||
std::unordered_map<std::wstring, ColorScheme>& GetColorSchemes() noexcept;
|
||||
const std::unordered_map<std::wstring, ColorScheme>& GetColorSchemes() const noexcept;
|
||||
void AddColorScheme(ColorScheme scheme);
|
||||
|
||||
void SetDefaultProfile(const GUID defaultProfile) noexcept;
|
||||
GUID GetDefaultProfile() const noexcept;
|
||||
|
||||
@@ -58,6 +61,13 @@ public:
|
||||
bool GetCopyOnSelect() const noexcept;
|
||||
void SetCopyOnSelect(const bool copyOnSelect) noexcept;
|
||||
|
||||
std::optional<int32_t> GetInitialX() const noexcept;
|
||||
|
||||
std::optional<int32_t> GetInitialY() const noexcept;
|
||||
|
||||
winrt::TerminalApp::LaunchMode GetLaunchMode() const noexcept;
|
||||
void SetLaunchMode(const winrt::TerminalApp::LaunchMode launchMode);
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept;
|
||||
|
||||
Json::Value ToJson() const;
|
||||
@@ -70,11 +80,14 @@ private:
|
||||
GUID _defaultProfile;
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::AppKeyBindings> _keybindings;
|
||||
|
||||
std::vector<ColorScheme> _colorSchemes;
|
||||
std::unordered_map<std::wstring, ColorScheme> _colorSchemes;
|
||||
|
||||
int32_t _initialRows;
|
||||
int32_t _initialCols;
|
||||
|
||||
std::optional<int32_t> _initialX;
|
||||
std::optional<int32_t> _initialY;
|
||||
|
||||
bool _showStatusline;
|
||||
bool _alwaysShowTabs;
|
||||
bool _showTitleInTitlebar;
|
||||
@@ -84,8 +97,21 @@ private:
|
||||
bool _copyOnSelect;
|
||||
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
|
||||
|
||||
winrt::TerminalApp::LaunchMode _launchMode;
|
||||
|
||||
static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept;
|
||||
static std::wstring_view _SerializeTheme(const winrt::Windows::UI::Xaml::ElementTheme theme) noexcept;
|
||||
|
||||
static void _ParseInitialPosition(const std::wstring& initialPosition,
|
||||
std::optional<int32_t>& initialX,
|
||||
std::optional<int32_t>& initialY) noexcept;
|
||||
|
||||
static std::string _SerializeInitialPosition(const std::optional<int32_t>& initialX,
|
||||
const std::optional<int32_t>& initialY) noexcept;
|
||||
|
||||
static std::wstring_view _SerializeLaunchMode(const winrt::TerminalApp::LaunchMode launchMode) noexcept;
|
||||
static winrt::TerminalApp::LaunchMode _ParseLaunchMode(const std::wstring& launchModeString) noexcept;
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
};
|
||||
|
||||