Compare commits
20 Commits
release-0.
...
release-0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
351d326594 | ||
|
|
2f2636f436 | ||
|
|
b22f77b76d | ||
|
|
e035c8fe93 | ||
|
|
5b33c6692b | ||
|
|
42e281deed | ||
|
|
f4e19e07ab | ||
|
|
d85689c1a8 | ||
|
|
c0ee499c71 | ||
|
|
3bae3c102a | ||
|
|
b61f1d3aea | ||
|
|
776b9a5103 | ||
|
|
f081a95db0 | ||
|
|
9461df9fa3 | ||
|
|
a5a8b41303 | ||
|
|
8195efa225 | ||
|
|
d745063feb | ||
|
|
495a29cc3e | ||
|
|
906c6e7882 | ||
|
|
88d7502c73 |
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: ''
|
||||
title: "Bug Report (IF I DO NOT CHANGE THIS THE ISSUE WILL BE AUTO-CLOSED)"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: "Documentation Issue 📚"
|
||||
name: Documentation Issue 📚
|
||||
about: Report issues in our documentation
|
||||
title: ''
|
||||
title: "Documentation Issue"
|
||||
labels: Issue-Docs
|
||||
assignees: ''
|
||||
|
||||
|
||||
69
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -1,35 +1,34 @@
|
||||
---
|
||||
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.
|
||||
-->
|
||||
---
|
||||
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.
|
||||
-->
|
||||
|
||||
10
.github/ISSUE_TEMPLATE/Guidance_Issue.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
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,166 +1,159 @@
|
||||
# Welcome to the Windows Terminal, Console and Command-Line repo
|
||||
# Welcome\!
|
||||
#### This repository contains the source code for:
|
||||
|
||||
This repository contains the source code for:
|
||||
* [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)
|
||||
|
||||
* [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
|
||||
## Installation
|
||||
|
||||
Related repositories include:
|
||||
_(Note: in order to run the Windows Terminal, you'll need to be running at least Windows build 18362 or higher.)_
|
||||
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
* [Cascadia Code Font](https://github.com/Microsoft/Cascadia-Code)
|
||||
### Microsoft Store
|
||||
|
||||
## Installing and running Windows Terminal
|
||||
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.
|
||||
|
||||
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
|
||||
<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>
|
||||
|
||||
### Manually installing builds from this repository
|
||||
### Chocolatey (Unofficial)
|
||||
|
||||
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:
|
||||
Download and upgrade the Windows Terminal from [Chocolatey](https://chocolatey.org).
|
||||
|
||||
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 using Chocolatey, run the following:
|
||||
|
||||
To upgrade Windows Terminal, run the following command from the command line or from PowerShell:
|
||||
```powershell
|
||||
choco upgrade microsoft-windows-terminal
|
||||
```
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
---
|
||||
|
||||
## Project Build Status
|
||||
### Build Status
|
||||
|
||||
Project|Build Status
|
||||
---|---
|
||||
Terminal|[](https://dev.azure.com/ms/Terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
|
||||
---
|
||||
# Windows Terminal Project's Timeline
|
||||
|
||||
## Windows Terminal v1.0 Roadmap
|
||||
The plan for delivering Windows Terminal v1.0 [is described here](/doc/terminal-v1-timeline.md), and will be updated as the project proceeds.
|
||||
|
||||
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
|
||||
# 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 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
Console's primary goal is to remain backwards-compatible with existing console subsystem applications.
|
||||
|
||||
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.
|
||||
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!
|
||||
|
||||
These limitations led us to create the new Windows Terminal.
|
||||
|
||||
> 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.
|
||||
## Shared Components
|
||||
|
||||
### Shared Components
|
||||
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.
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
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.
|
||||
## Building a new terminal
|
||||
|
||||
### Creating the new Windows Terminal
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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).
|
||||
# FAQ
|
||||
|
||||
---
|
||||
## Where can I download Windows Terminal?
|
||||
|
||||
## Resources
|
||||
The latest release of Windows Terminal can be downloaded from [this repo's Releases](https://github.com/microsoft/terminal/releases) page or [from the Microsoft Store](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
|
||||
|
||||
All project documentation is located in the `./doc` folder. If you would like to contribute to the documentation, please submit a pull request.
|
||||
|
||||
---
|
||||
* 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".
|
||||
|
||||
## Contributing
|
||||
|
||||
We are excited to work alongside you, our amazing community, to build and enhance Windows Terminal\!
|
||||
|
||||
***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.
|
||||
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.
|
||||
|
||||
## Communicating with the Team
|
||||
|
||||
The easiest way to communicate with the team is via GitHub issues.
|
||||
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**.
|
||||
|
||||
Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before creating a new issue.**
|
||||
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.
|
||||
|
||||
If you would like to ask a question that you feel doesn't warrant an issue (yet), please reach out to us via Twitter:
|
||||
|
||||
* 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)
|
||||
* Rich Turner, Program Manager: [@richturn\_ms](https://twitter.com/richturn_ms)
|
||||
|
||||
## Developer Guidance
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
|
||||
* Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
|
||||
## Prerequisites
|
||||
* 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.
|
||||
|
||||
* 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
|
||||
|
||||
@@ -170,9 +163,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 a set of convenience scripts & tools in the **/tools** directory:
|
||||
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.
|
||||
|
||||
### Building in PowerShell
|
||||
### PowerShell
|
||||
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
@@ -180,37 +173,27 @@ Set-MsBuildDevEnvironment
|
||||
Invoke-OpenConsoleBuild
|
||||
```
|
||||
|
||||
### Building in Cmd
|
||||
### CMD
|
||||
|
||||
```shell
|
||||
.\tools\razzle.cmd
|
||||
bcz
|
||||
```
|
||||
|
||||
## Debugging Terminal
|
||||
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.
|
||||
|
||||
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".
|
||||
## Coding Guidance
|
||||
|
||||
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
|
||||
Please review these brief docs below relating to our coding standards etc.
|
||||
|
||||
### 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!)
|
||||
> 👉 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\!)
|
||||
|
||||
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 [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 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,33 +36,16 @@ steps:
|
||||
restoreDirectory: '$(Build.SourcesDirectory)/packages'
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln (no packages)'
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
# 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"
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
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:
|
||||
@@ -106,7 +89,7 @@ steps:
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
|
||||
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy *.appx/*.msix to Artifacts (Non-PR builds only)'
|
||||
inputs:
|
||||
@@ -126,3 +109,4 @@ steps:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)/appx'
|
||||
ArtifactName: 'appx-$(BuildConfiguration)'
|
||||
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
|
||||
|
||||
|
||||
@@ -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' and '%(ReferencePath.Implementation)' != ''">
|
||||
<_ConsoleWinmdManifest Include="@(ReferencePath->'$(IntDir)\%(FileName).manifest')" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'">
|
||||
<WinMDPath>%(ReferencePath.FullPath)</WinMDPath>
|
||||
<Implementation>%(ReferencePath.Implementation)</Implementation>
|
||||
</_ConsoleWinmdManifest>
|
||||
|
||||
@@ -1,85 +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
|
||||
[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,11 +75,13 @@ Try {
|
||||
Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
|
||||
}
|
||||
|
||||
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"
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
} Finally {
|
||||
Remove-Item -Recurse -Force $AppxPackageRootPath
|
||||
}
|
||||
|
||||
11
custom.props
@@ -1,11 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,184 +1,181 @@
|
||||
# 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:
|
||||
|
||||

|
||||
# 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}"`. |
|
||||
| `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` | _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. This path must be formatted as a Windows path.|
|
||||
| `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:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
@@ -1,670 +0,0 @@
|
||||
{
|
||||
"$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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -21,9 +21,6 @@ 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
|
||||
|
||||
|
Before Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 110 KiB |
@@ -1,107 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -20,14 +20,14 @@ Ultimately, we're aiming for Terminal v1.0 to be feature-complete by Dec 2019, a
|
||||
|
||||
> ⚠ 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 |
|
||||
| Milestone end date | Phase | Key features |
|
||||
| --- | --- | --- |
|
||||
| 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-10-22 | 1910 | 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 |
|
||||
@@ -58,13 +58,13 @@ Incoming issues/asks/etc. are triaged several times a week, labelled appropriate
|
||||
* 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
|
||||
## v1.0 Features
|
||||
|
||||
The following are a list of the key scenarios we're aiming to deliver for Terminal v1.0.
|
||||
The following are a list of the key features/feature-areas 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 |
|
||||
| Release | Priority\* | Feature | 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 |
|
||||
@@ -1,67 +0,0 @@
|
||||
# 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_8wekyb3d8bbwe\LocalState\profiles.json`.
|
||||
The settings are stored in the file `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\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,11 +183,6 @@ 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:
|
||||
@@ -246,8 +241,6 @@ 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.
|
||||
|
||||
@@ -277,33 +270,8 @@ 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,10 +18,12 @@ To compile Windows Terminal yourself using the source code, follow the instructi
|
||||
|
||||
## Starting Windows Terminal
|
||||
|
||||
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`.
|
||||
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.
|
||||
|
||||
NOTE: The default shell is PowerShell; you can change this using the _Running a Different Shell_ procedure.
|
||||
|
||||
### Command line options
|
||||
|
||||
@@ -30,7 +32,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: <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>1</kbd>).
|
||||
default shell is displayed (default shortcut `Ctrl+Shift+1`).
|
||||
|
||||
## Running a Different Shell
|
||||
|
||||
@@ -47,17 +49,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)
|
||||
|
||||
## Selecting and Copying Text in Windows Terminal
|
||||
## Using cut and paste in the Terminal window
|
||||
|
||||
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.
|
||||
### With PowerShell
|
||||
|
||||
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.
|
||||
* 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
|
||||
|
||||
If there is not an active selection, a right-click will paste the text content from your clipboard to the terminal.
|
||||
### With Bash
|
||||
|
||||
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.
|
||||
* Copy - Select the text with mouse (default left button), then right click with mouse
|
||||
* Paste - Right click with mouse
|
||||
|
||||
## Add a "Open Windows Terminal Here" to File Explorer
|
||||
|
||||
@@ -70,7 +72,7 @@ All Windows Terminal settings are currently managed using the `profiles.json` fi
|
||||
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: <kbd>Ctrl</kbd>+<kbd>,</kbd>.
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: `Ctrl+,`.
|
||||
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.
|
||||
@@ -84,6 +86,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 <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.
|
||||
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.
|
||||
4. Please add more Tips and Tricks
|
||||
|
||||
86
pkg/appx/OpenConsolePackage.wapproj
Normal file
@@ -0,0 +1,86 @@
|
||||
<?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>
|
||||
34
pkg/appx/Package.appxmanifest
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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>
|
||||
BIN
pkg/appx/images/LockScreenLogo.scale-200.png
Normal file
|
After Width: | Height: | Size: 716 B |
BIN
pkg/appx/images/Square150x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
pkg/appx/images/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
pkg/appx/images/Square44x44Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 299 B |
|
After Width: | Height: | Size: 551 B |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 697 B |
|
After Width: | Height: | Size: 778 B |
BIN
pkg/appx/images/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 719 B |
BIN
pkg/appx/images/Wide310x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
res/Cascadia.ttf
@@ -18,4 +18,4 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code
|
||||
* from microsoft/cascadia-code@d733599504811e8f3969de20368817d20e162dba
|
||||
* from microsoft/cascadia-code@d3b1adacf2691dfadf8ebd8a08936d3ad8a062d0
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<!-- 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,71 +278,31 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
// .. 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)
|
||||
// .. 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)
|
||||
{
|
||||
// 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 left = _list.begin();
|
||||
if (iStart == left->GetLength() && NewAttr == left->GetAttributes())
|
||||
{
|
||||
upperBound += _list.at(i).GetLength();
|
||||
if (iStart >= lowerBound && iStart < upperBound)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
_list.erase(right);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,20 @@ AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow
|
||||
AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) noexcept :
|
||||
_pAttrRow{ attrRow },
|
||||
_run{ attrRow->_list.cbegin() },
|
||||
_currentAttributeIndex{ 0 },
|
||||
_exceeded{ false }
|
||||
_currentAttributeIndex{ 0 }
|
||||
{
|
||||
}
|
||||
|
||||
AttrRowIterator::operator bool() const
|
||||
{
|
||||
return !_exceeded && _run < _pAttrRow->_list.cend();
|
||||
return _run < _pAttrRow->_list.cend();
|
||||
}
|
||||
|
||||
bool AttrRowIterator::operator==(const AttrRowIterator& it) const
|
||||
{
|
||||
return (_pAttrRow == it._pAttrRow &&
|
||||
_run == it._run &&
|
||||
_currentAttributeIndex == it._currentAttributeIndex &&
|
||||
_exceeded == it._exceeded);
|
||||
_currentAttributeIndex == it._currentAttributeIndex);
|
||||
}
|
||||
|
||||
bool AttrRowIterator::operator!=(const AttrRowIterator& it) const
|
||||
@@ -54,16 +52,13 @@ AttrRowIterator AttrRowIterator::operator++(int)
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
if (!_exceeded)
|
||||
if (movement >= 0)
|
||||
{
|
||||
if (movement >= 0)
|
||||
{
|
||||
_increment(gsl::narrow<size_t>(movement));
|
||||
}
|
||||
else
|
||||
{
|
||||
_decrement(gsl::narrow<size_t>(-movement));
|
||||
}
|
||||
_increment(gsl::narrow<size_t>(movement));
|
||||
}
|
||||
else
|
||||
{
|
||||
_decrement(gsl::narrow<size_t>(-movement));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -89,13 +84,11 @@ 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();
|
||||
}
|
||||
|
||||
@@ -130,23 +123,14 @@ 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
|
||||
{
|
||||
// make sure we don't go out of bounds
|
||||
if (_run == _pAttrRow->_list.cbegin())
|
||||
{
|
||||
_exceeded = true;
|
||||
return;
|
||||
}
|
||||
count -= _currentAttributeIndex + 1;
|
||||
count -= _currentAttributeIndex;
|
||||
--_run;
|
||||
_currentAttributeIndex = _run->GetLength() - 1;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ 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);
|
||||
|
||||
@@ -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,6 +155,11 @@ 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);
|
||||
@@ -210,11 +215,6 @@ 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
|
||||
{
|
||||
WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Bold, isBold);
|
||||
_isBold = isBold;
|
||||
}
|
||||
|
||||
void TextAttribute::SetDefaultForeground() noexcept
|
||||
|
||||
@@ -27,8 +27,6 @@ Revision History:
|
||||
#include "WexTestClass.h"
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
class TextAttribute final
|
||||
{
|
||||
public:
|
||||
@@ -36,7 +34,7 @@ public:
|
||||
_wAttrLegacy{ 0 },
|
||||
_foreground{},
|
||||
_background{},
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
_isBold{ false }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -44,7 +42,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) },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
_isBold{ false }
|
||||
{
|
||||
// If we're given lead/trailing byte information with the legacy color, strip it.
|
||||
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
|
||||
@@ -55,7 +53,7 @@ public:
|
||||
_wAttrLegacy{ 0 },
|
||||
_foreground{ rgbForeground },
|
||||
_background{ rgbBackground },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
_isBold{ false }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,7 +62,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:
|
||||
@@ -87,7 +85,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,
|
||||
@@ -133,18 +131,7 @@ public:
|
||||
friend constexpr bool operator!=(const WORD& legacyAttr, const TextAttribute& attr) noexcept;
|
||||
|
||||
bool IsLegacy() 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;
|
||||
bool IsBold() const noexcept;
|
||||
|
||||
void SetForeground(const COLORREF rgbForeground) noexcept;
|
||||
void SetBackground(const COLORREF rgbBackground) noexcept;
|
||||
@@ -172,7 +159,7 @@ private:
|
||||
WORD _wAttrLegacy;
|
||||
TextColor _foreground;
|
||||
TextColor _background;
|
||||
ExtendedAttributes _extendedAttrs;
|
||||
bool _isBold;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
@@ -182,13 +169,6 @@ 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
|
||||
@@ -201,7 +181,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._extendedAttrs == b._extendedAttrs;
|
||||
a._isBold == b._isBold;
|
||||
}
|
||||
|
||||
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,13 +1,5 @@
|
||||
<?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" />
|
||||
@@ -19,7 +11,6 @@
|
||||
<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" />
|
||||
@@ -46,7 +37,6 @@
|
||||
<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" />
|
||||
@@ -59,6 +49,19 @@
|
||||
<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>
|
||||
@@ -48,7 +48,6 @@ SOURCES= \
|
||||
..\CharRowCell.cpp \
|
||||
..\CharRowCellReference.cpp \
|
||||
..\UnicodeStorage.cpp \
|
||||
..\search.cpp \
|
||||
|
||||
INCLUDES= \
|
||||
$(INCLUDES); \
|
||||
|
||||
@@ -1065,10 +1065,14 @@ 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 std::wstring_view fontFaceName, const COLORREF backgroundColor, const std::string& htmlTitle)
|
||||
std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPoints, const PCWCHAR 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
|
||||
@@ -1092,7 +1096,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo
|
||||
|
||||
htmlBuilder << "font-family:";
|
||||
htmlBuilder << "'";
|
||||
htmlBuilder << ConvertToA(CP_UTF8, fontFaceName);
|
||||
htmlBuilder << ConvertToA(CP_UTF8, faceNameView);
|
||||
htmlBuilder << "',";
|
||||
// even with different font, add monospace as fallback
|
||||
htmlBuilder << "monospace;";
|
||||
@@ -1145,7 +1149,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)
|
||||
@@ -1240,185 +1244,3 @@ 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,6 +69,8 @@ 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;
|
||||
|
||||
@@ -146,15 +148,10 @@ public:
|
||||
|
||||
static std::string GenHTML(const TextAndColor& rows,
|
||||
const int fontHeightPoints,
|
||||
const std::wstring_view fontFaceName,
|
||||
const PCWCHAR 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,13 +1,5 @@
|
||||
<?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" />
|
||||
@@ -25,12 +17,20 @@
|
||||
<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,6 +2,11 @@
|
||||
<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>5</VersionMinor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
|
||||
@@ -40,27 +45,251 @@
|
||||
</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>
|
||||
<!-- Fonts -->
|
||||
<Content Include="$(OpenConsoleDir)res\Cascadia.ttf" Condition="'$(WindowsTerminalReleaseBuild)'=='true'">
|
||||
<Link>Cascadia.ttf</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>
|
||||
@@ -110,13 +339,4 @@
|
||||
</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>
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
<?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,7 +98,6 @@ namespace TerminalAppLocalTests
|
||||
"name": "scheme0",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010100",
|
||||
"red": "#010000",
|
||||
"green": "#000100",
|
||||
"blue": "#000001"
|
||||
@@ -107,7 +106,6 @@ namespace TerminalAppLocalTests
|
||||
"name": "scheme1",
|
||||
"foreground": "#020202",
|
||||
"background": "#030303",
|
||||
"selectionBackground": "#020200",
|
||||
"red": "#020000",
|
||||
|
||||
"blue": "#000002"
|
||||
@@ -116,7 +114,6 @@ namespace TerminalAppLocalTests
|
||||
"name": "scheme0",
|
||||
"foreground": "#040404",
|
||||
"background": "#050505",
|
||||
"selectionBackground": "#030300",
|
||||
"red": "#030000",
|
||||
"green": "#000300"
|
||||
})" };
|
||||
@@ -129,8 +126,6 @@ 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]);
|
||||
@@ -141,7 +136,6 @@ 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]);
|
||||
@@ -152,7 +146,6 @@ 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]);
|
||||
@@ -176,7 +169,7 @@ namespace TerminalAppLocalTests
|
||||
"background": "#050505"
|
||||
})" };
|
||||
const std::string scheme3String{ R"({
|
||||
// by not providing a name, the scheme will have the name ""
|
||||
// "name": "scheme3",
|
||||
"foreground": "#060606",
|
||||
"background": "#070707"
|
||||
})" };
|
||||
@@ -195,85 +188,47 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
|
||||
settings._LayerOrCreateColorScheme(scheme0Json);
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
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_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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
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;
|
||||
@@ -37,33 +35,11 @@ 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()
|
||||
@@ -180,189 +156,4 @@ 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,8 +95,7 @@ namespace TerminalAppLocalTests
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010101"
|
||||
"background": "#010101"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name": "profile1",
|
||||
@@ -107,8 +106,7 @@ namespace TerminalAppLocalTests
|
||||
const std::string profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#030303",
|
||||
"selectionBackground": "#020202"
|
||||
"foreground": "#030303"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
@@ -122,9 +120,6 @@ 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());
|
||||
@@ -139,9 +134,6 @@ 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());
|
||||
@@ -157,9 +149,6 @@ 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());
|
||||
|
||||
@@ -48,12 +48,6 @@ namespace TerminalAppLocalTests
|
||||
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)
|
||||
{
|
||||
@@ -399,10 +393,9 @@ namespace TerminalAppLocalTests
|
||||
|
||||
settings->_ValidateSettings();
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings->_warnings.size());
|
||||
VERIFY_ARE_EQUAL(2u, 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());
|
||||
@@ -800,9 +793,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name" : "profile1"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{ "name": "Campbell" }
|
||||
]
|
||||
})" };
|
||||
|
||||
@@ -1201,217 +1191,4 @@ namespace TerminalAppLocalTests
|
||||
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,29 +1,5 @@
|
||||
<?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" />
|
||||
|
||||
@@ -64,6 +40,15 @@
|
||||
</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>
|
||||
@@ -85,19 +70,31 @@
|
||||
</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.191203001-prerelease\runtimes\win10-$(Native-Platform)\native\"</_MUXBinRoot>
|
||||
<_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.2.190611001-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.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.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.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 -->
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// 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);
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include <LibraryIncludes.h>
|
||||
@@ -1,11 +0,0 @@
|
||||
#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
|
||||
{
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#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,8 +7,7 @@
|
||||
|
||||
#include "ActionEventArgs.g.cpp"
|
||||
#include "CopyTextArgs.g.cpp"
|
||||
#include "NewTabArgs.g.cpp"
|
||||
#include "NewTabWithProfileArgs.g.cpp"
|
||||
#include "SwitchToTabArgs.g.cpp"
|
||||
#include "ResizePaneArgs.g.cpp"
|
||||
#include "MoveFocusArgs.g.cpp"
|
||||
#include "AdjustFontSizeArgs.g.cpp"
|
||||
|
||||
@@ -7,14 +7,12 @@
|
||||
// *.g.cpp to ActionArgs.cpp!
|
||||
#include "ActionEventArgs.g.h"
|
||||
#include "CopyTextArgs.g.h"
|
||||
#include "NewTabArgs.g.h"
|
||||
#include "NewTabWithProfileArgs.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
|
||||
@@ -36,211 +34,33 @@ namespace winrt::TerminalApp::implementation
|
||||
struct CopyTextArgs : public CopyTextArgsT<CopyTextArgs>
|
||||
{
|
||||
CopyTextArgs() = default;
|
||||
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;
|
||||
}
|
||||
GETSET_PROPERTY(bool, TrimWhitespace, false);
|
||||
};
|
||||
|
||||
struct NewTabArgs : public NewTabArgsT<NewTabArgs>
|
||||
struct NewTabWithProfileArgs : public NewTabWithProfileArgsT<NewTabWithProfileArgs>
|
||||
{
|
||||
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;
|
||||
}
|
||||
NewTabWithProfileArgs() = default;
|
||||
GETSET_PROPERTY(int32_t, ProfileIndex, 0);
|
||||
};
|
||||
|
||||
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::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;
|
||||
}
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::Left);
|
||||
};
|
||||
|
||||
struct MoveFocusArgs : public MoveFocusArgsT<MoveFocusArgs>
|
||||
{
|
||||
MoveFocusArgs() = default;
|
||||
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;
|
||||
}
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::Left);
|
||||
};
|
||||
|
||||
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,10 +3,9 @@
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
interface IActionArgs
|
||||
{
|
||||
Boolean Equals(IActionArgs other);
|
||||
};
|
||||
// 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 IActionEventArgs
|
||||
{
|
||||
@@ -16,8 +15,7 @@ namespace TerminalApp
|
||||
|
||||
enum Direction
|
||||
{
|
||||
None = 0,
|
||||
Left,
|
||||
Left = 0,
|
||||
Right,
|
||||
Up,
|
||||
Down
|
||||
@@ -33,11 +31,9 @@ namespace TerminalApp
|
||||
Boolean TrimWhitespace { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass NewTabArgs : IActionArgs
|
||||
[default_interface] runtimeclass NewTabWithProfileArgs : IActionArgs
|
||||
{
|
||||
// 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; };
|
||||
Int32 ProfileIndex { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SwitchToTabArgs : IActionArgs
|
||||
@@ -55,9 +51,4 @@ namespace TerminalApp
|
||||
Direction Direction { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AdjustFontSizeArgs : IActionArgs
|
||||
{
|
||||
Int32 Delta { get; };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,60 +3,615 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "App.h"
|
||||
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>
|
||||
|
||||
#include "App.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::ApplicationModel::Activation;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
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, 2> settingsLoadErrorsLabels {
|
||||
L"NoProfilesText",
|
||||
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.
|
||||
// - 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;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
App::App()
|
||||
App::App() :
|
||||
_dialogLock{},
|
||||
_loadedInitialSettings{ false },
|
||||
_settingsLoadedResult{ S_OK }
|
||||
{
|
||||
// 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)
|
||||
// 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.
|
||||
|
||||
// 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()
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
_isUwp = true;
|
||||
// Another dialog is visible.
|
||||
return;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
// 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
|
||||
}
|
||||
|
||||
AppLogic App::Logic()
|
||||
// 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)
|
||||
{
|
||||
static AppLogic logic;
|
||||
return logic;
|
||||
}
|
||||
auto title = _resourceLoader->GetLocalizedString(titleKey);
|
||||
auto buttonText = _resourceLoader->GetLocalizedString(L"Ok");
|
||||
|
||||
/// <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*/)
|
||||
{
|
||||
// if this is a UWP... it means its our problem to hook up the content to the window here.
|
||||
if (_isUwp)
|
||||
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))
|
||||
{
|
||||
auto content = Window::Current().Content();
|
||||
if (content == nullptr)
|
||||
if (!_settingsLoadExceptionText.empty())
|
||||
{
|
||||
auto logic = Logic();
|
||||
logic.LoadSettings();
|
||||
logic.Create();
|
||||
|
||||
auto page = logic.GetRoot().as<TerminalPage>();
|
||||
|
||||
Window::Current().Content(page);
|
||||
Window::Current().Activate();
|
||||
warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, Resources()));
|
||||
}
|
||||
}
|
||||
|
||||
// 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,8 +3,21 @@
|
||||
|
||||
#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
|
||||
{
|
||||
@@ -12,12 +25,70 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
public:
|
||||
App();
|
||||
void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs const&);
|
||||
~App() = default;
|
||||
|
||||
TerminalApp::AppLogic Logic();
|
||||
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);
|
||||
|
||||
private:
|
||||
bool _isUwp = false;
|
||||
// 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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "../AppLogic.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
// ADD ARBITRARY APP LOGIC TO APPLOGIC.IDL, NOT HERE.
|
||||
// This is for XAML platform setup only.
|
||||
delegate void LastTabClosedEventArgs();
|
||||
[default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
|
||||
{
|
||||
App();
|
||||
|
||||
AppLogic Logic { get; };
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,24 +49,13 @@ 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,6 +26,12 @@ 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)
|
||||
{
|
||||
@@ -57,7 +63,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
CloseWindow();
|
||||
_CloseWindow();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -132,24 +138,12 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
void TerminalPage::_HandleNewTabWithProfile(const IInspectable& /*sender*/,
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::NewTabWithProfileArgs>())
|
||||
{
|
||||
_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());
|
||||
}
|
||||
_OpenNewTab({ realArgs.ProfileIndex() });
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -169,16 +163,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::ResizePaneArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == TerminalApp::Direction::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ResizePane(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
_ResizePane(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,16 +173,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::MoveFocusArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == TerminalApp::Direction::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_MoveFocus(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
_MoveFocus(realArgs.Direction());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,30 +188,4 @@ 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,14 +9,13 @@
|
||||
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void AppKeyBindings::SetKeyBinding(const TerminalApp::ActionAndArgs& actionAndArgs,
|
||||
void AppKeyBindings::SetKeyBinding(const TerminalApp::ShortcutAction& action,
|
||||
const Settings::KeyChord& chord)
|
||||
{
|
||||
_keyShortcuts[chord] = actionAndArgs;
|
||||
_keyShortcuts[chord] = action;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -30,33 +29,11 @@ namespace winrt::TerminalApp::implementation
|
||||
_keyShortcuts.erase(chord);
|
||||
}
|
||||
|
||||
KeyChord AppKeyBindings::GetKeyBindingForAction(TerminalApp::ShortcutAction const& action)
|
||||
Microsoft::Terminal::Settings::KeyChord AppKeyBindings::GetKeyBinding(TerminalApp::ShortcutAction const& action)
|
||||
{
|
||||
for (auto& kv : _keyShortcuts)
|
||||
{
|
||||
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()))
|
||||
if (kv.second == action)
|
||||
{
|
||||
return kv.first;
|
||||
}
|
||||
@@ -69,191 +46,352 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto keyIter = _keyShortcuts.find(kc);
|
||||
if (keyIter != _keyShortcuts.end())
|
||||
{
|
||||
const auto actionAndArgs = keyIter->second;
|
||||
return _DoAction(actionAndArgs);
|
||||
const auto action = keyIter->second;
|
||||
return _DoAction(action);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::_DoAction(ActionAndArgs actionAndArgs)
|
||||
bool AppKeyBindings::_DoAction(ShortcutAction action)
|
||||
{
|
||||
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);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::CopyTextWithoutNewlines:
|
||||
{
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
args->TrimWhitespace(false);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_CopyTextHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::PasteText:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_PasteTextHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::NewTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_NewTabHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::OpenNewTabDropdown:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_OpenNewTabDropdownHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::DuplicateTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_DuplicateTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::OpenSettings:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_OpenSettingsHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
|
||||
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:
|
||||
{
|
||||
_NewTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
auto args = winrt::make_self<NewTabWithProfileArgs>();
|
||||
args->ProfileIndex(8);
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>(*args);
|
||||
_NewTabWithProfileHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
|
||||
case ShortcutAction::NewWindow:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_NewWindowHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::CloseWindow:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_CloseWindowHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::CloseTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_CloseTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ClosePane:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ClosePaneHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
|
||||
case ShortcutAction::ScrollUp:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollUpHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ScrollDown:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollDownHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ScrollUpPage:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollUpPageHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ScrollDownPage:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_ScrollDownPageHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
|
||||
case ShortcutAction::NextTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_NextTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::PrevTab:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_PrevTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
|
||||
case ShortcutAction::SplitVertical:
|
||||
{
|
||||
auto eventArgs = winrt::make_self<ActionEventArgs>();
|
||||
_SplitVerticalHandlers(*this, *eventArgs);
|
||||
break;
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::SplitHorizontal:
|
||||
{
|
||||
_SplitHorizontalHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::SwitchToTab:
|
||||
case ShortcutAction::SwitchToTab0:
|
||||
case ShortcutAction::SwitchToTab1:
|
||||
case ShortcutAction::SwitchToTab2:
|
||||
case ShortcutAction::SwitchToTab3:
|
||||
case ShortcutAction::SwitchToTab4:
|
||||
case ShortcutAction::SwitchToTab5:
|
||||
case ShortcutAction::SwitchToTab6:
|
||||
case ShortcutAction::SwitchToTab7:
|
||||
case ShortcutAction::SwitchToTab8:
|
||||
{
|
||||
_SwitchToTabHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::ResizePane:
|
||||
case ShortcutAction::ResizePaneLeft:
|
||||
case ShortcutAction::ResizePaneRight:
|
||||
case ShortcutAction::ResizePaneUp:
|
||||
case ShortcutAction::ResizePaneDown:
|
||||
{
|
||||
_ResizePaneHandlers(*this, *eventArgs);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShortcutAction::MoveFocus:
|
||||
case ShortcutAction::MoveFocusLeft:
|
||||
case ShortcutAction::MoveFocusRight:
|
||||
case ShortcutAction::MoveFocusUp:
|
||||
case ShortcutAction::MoveFocusDown:
|
||||
{
|
||||
_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);
|
||||
_SplitHorizontalHandlers(*this, *eventArgs);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
case ShortcutAction::ToggleFullscreen:
|
||||
|
||||
case ShortcutAction::SwitchToTab0:
|
||||
{
|
||||
_ToggleFullscreenHandlers(*this, *eventArgs);
|
||||
break;
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
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);
|
||||
return eventArgs->Handled();
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return eventArgs->Handled();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -41,12 +41,9 @@ namespace winrt::TerminalApp::implementation
|
||||
AppKeyBindings() = default;
|
||||
|
||||
bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc);
|
||||
|
||||
void SetKeyBinding(TerminalApp::ActionAndArgs const& actionAndArgs,
|
||||
winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
void ClearKeyBinding(winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBindingForAction(TerminalApp::ShortcutAction const& action);
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs);
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBinding(TerminalApp::ShortcutAction const& action);
|
||||
|
||||
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
|
||||
|
||||
@@ -57,9 +54,10 @@ 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(NewTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(NewTabWithProfile, 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);
|
||||
@@ -69,8 +67,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(AdjustFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(ResetFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(IncreaseFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs);
|
||||
TYPED_EVENT(DecreaseFontSize, 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);
|
||||
@@ -78,12 +76,11 @@ 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::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
|
||||
bool _DoAction(ActionAndArgs actionAndArgs);
|
||||
std::unordered_map<winrt::Microsoft::Terminal::Settings::KeyChord, TerminalApp::ShortcutAction, KeyChordHash, KeyChordEquality> _keyShortcuts;
|
||||
bool _DoAction(ShortcutAction action);
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::KeyBindingsTests;
|
||||
|
||||
@@ -4,27 +4,24 @@ 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,
|
||||
NewTab,
|
||||
NewTabProfile0, // Legacy
|
||||
NewTabProfile1, // Legacy
|
||||
NewTabProfile2, // Legacy
|
||||
NewTabProfile3, // Legacy
|
||||
NewTabProfile4, // Legacy
|
||||
NewTabProfile5, // Legacy
|
||||
NewTabProfile6, // Legacy
|
||||
NewTabProfile7, // Legacy
|
||||
NewTabProfile8, // Legacy
|
||||
NewTabProfile0,
|
||||
NewTabProfile1,
|
||||
NewTabProfile2,
|
||||
NewTabProfile3,
|
||||
NewTabProfile4,
|
||||
NewTabProfile5,
|
||||
NewTabProfile6,
|
||||
NewTabProfile7,
|
||||
NewTabProfile8,
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
CloseTab,
|
||||
@@ -33,58 +30,46 @@ namespace TerminalApp
|
||||
PrevTab,
|
||||
SplitVertical,
|
||||
SplitHorizontal,
|
||||
SwitchToTab,
|
||||
SwitchToTab0, // Legacy
|
||||
SwitchToTab1, // Legacy
|
||||
SwitchToTab2, // Legacy
|
||||
SwitchToTab3, // Legacy
|
||||
SwitchToTab4, // Legacy
|
||||
SwitchToTab5, // Legacy
|
||||
SwitchToTab6, // Legacy
|
||||
SwitchToTab7, // Legacy
|
||||
SwitchToTab8, // Legacy
|
||||
SwitchToTab0,
|
||||
SwitchToTab1,
|
||||
SwitchToTab2,
|
||||
SwitchToTab3,
|
||||
SwitchToTab4,
|
||||
SwitchToTab5,
|
||||
SwitchToTab6,
|
||||
SwitchToTab7,
|
||||
SwitchToTab8,
|
||||
IncreaseFontSize,
|
||||
DecreaseFontSize,
|
||||
ResetFontSize,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
ScrollUpPage,
|
||||
ScrollDownPage,
|
||||
ResizePane,
|
||||
ResizePaneLeft, // Legacy
|
||||
ResizePaneRight, // Legacy
|
||||
ResizePaneUp, // Legacy
|
||||
ResizePaneDown, // Legacy
|
||||
MoveFocus,
|
||||
MoveFocusLeft, // Legacy
|
||||
MoveFocusRight, // Legacy
|
||||
MoveFocusUp, // Legacy
|
||||
MoveFocusDown, // Legacy
|
||||
ToggleFullscreen,
|
||||
ResizePaneLeft,
|
||||
ResizePaneRight,
|
||||
ResizePaneUp,
|
||||
ResizePaneDown,
|
||||
MoveFocusLeft,
|
||||
MoveFocusRight,
|
||||
MoveFocusUp,
|
||||
MoveFocusDown,
|
||||
OpenSettings
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ActionAndArgs {
|
||||
ActionAndArgs();
|
||||
IActionArgs Args;
|
||||
ShortcutAction Action;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
|
||||
{
|
||||
AppKeyBindings();
|
||||
|
||||
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.Settings.KeyChord chord);
|
||||
void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord);
|
||||
void ClearKeyBinding(Microsoft.Terminal.Settings.KeyChord chord);
|
||||
|
||||
Microsoft.Terminal.Settings.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
Microsoft.Terminal.Settings.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
|
||||
Microsoft.Terminal.Settings.KeyChord GetKeyBinding(ShortcutAction action);
|
||||
|
||||
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;
|
||||
@@ -94,8 +79,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> AdjustFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ResetFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> IncreaseFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> DecreaseFontSize;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ScrollUp;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ScrollDown;
|
||||
event Windows.Foundation.TypedEventHandler<AppKeyBindings, ActionEventArgs> ScrollUpPage;
|
||||
@@ -103,6 +88,5 @@ 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,7 +8,6 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppKeyBindings.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
@@ -19,26 +18,25 @@ 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" }; // Legacy
|
||||
static constexpr std::string_view CopyTextWithoutNewlinesKey{ "copyTextWithoutNewlines" };
|
||||
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 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 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 NewWindowKey{ "newWindow" };
|
||||
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
|
||||
static constexpr std::string_view CloseTabKey{ "closeTab" };
|
||||
@@ -48,35 +46,30 @@ 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 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 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 SplitHorizontalKey{ "splitHorizontal" };
|
||||
static constexpr std::string_view SplitVerticalKey{ "splitVertical" };
|
||||
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" };
|
||||
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" };
|
||||
|
||||
// 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.
|
||||
@@ -90,9 +83,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 },
|
||||
@@ -110,12 +103,10 @@ 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 },
|
||||
@@ -127,187 +118,18 @@ 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:
|
||||
@@ -356,7 +178,7 @@ Json::Value winrt::TerminalApp::implementation::AppKeyBindings::ToJson()
|
||||
const auto searchedForName = actionName.first;
|
||||
const auto searchedForAction = actionName.second;
|
||||
|
||||
if (const auto chord{ GetKeyBindingForAction(searchedForAction) })
|
||||
if (const auto chord{ GetKeyBinding(searchedForAction) })
|
||||
{
|
||||
if (const auto serialization{ _ShortcutAsJsonObject(chord, searchedForName) })
|
||||
{
|
||||
@@ -368,21 +190,6 @@ 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
|
||||
@@ -418,48 +225,18 @@ 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();
|
||||
action = GetActionFromString(commandString);
|
||||
}
|
||||
else if (commandVal.isObject())
|
||||
{
|
||||
const auto actionVal = commandVal[JsonKey(ActionKey)];
|
||||
if (actionVal.isString())
|
||||
{
|
||||
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)
|
||||
// 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())
|
||||
{
|
||||
args = pfn(argsVal);
|
||||
action = found->second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,10 +251,7 @@ void winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::V
|
||||
// found.
|
||||
if (action != ShortcutAction::Invalid)
|
||||
{
|
||||
auto actionAndArgs = winrt::make_self<ActionAndArgs>();
|
||||
actionAndArgs->Action(action);
|
||||
actionAndArgs->Args(args);
|
||||
SetKeyBinding(*actionAndArgs, chord);
|
||||
SetKeyBinding(action, chord);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,665 +0,0 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
// 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>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// 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,18 +174,13 @@ void CascadiaSettings::_ValidateSettings()
|
||||
_ValidateNoDuplicateProfiles();
|
||||
_ValidateDefaultProfileExists();
|
||||
|
||||
// Ensure that all the profile's color scheme names are
|
||||
// TODO:GH#2547 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:
|
||||
@@ -371,36 +366,3 @@ void CascadiaSettings::_RemoveHiddenProfiles()
|
||||
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,6 +48,7 @@ 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;
|
||||
|
||||
@@ -57,6 +58,7 @@ 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);
|
||||
|
||||
@@ -102,7 +104,6 @@ private:
|
||||
void _ValidateNoDuplicateProfiles();
|
||||
void _ReorderProfilesToMatchUserSettingsOrder();
|
||||
void _RemoveHiddenProfiles();
|
||||
void _ValidateAllSchemesExist();
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ProfileTests;
|
||||
|
||||
@@ -240,6 +240,26 @@ 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.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CascadiaSettings::SaveAll() const
|
||||
{
|
||||
const auto json = ToJson();
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
// Use 4 spaces to indent instead of \t
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
|
||||
const auto serializedString = Json::writeString(wbuilder, json);
|
||||
|
||||
_WriteSettings(serializedString);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the user's settings file is missing a schema directive
|
||||
// and, if so, inserts one.
|
||||
@@ -382,6 +402,36 @@ 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:
|
||||
@@ -407,11 +457,6 @@ 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())
|
||||
@@ -419,6 +464,13 @@ 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()] })
|
||||
{
|
||||
@@ -518,15 +570,16 @@ void CascadiaSettings::_LayerOrCreateColorScheme(const Json::Value& schemeJson)
|
||||
}
|
||||
else
|
||||
{
|
||||
_globals.AddColorScheme(ColorScheme::FromJson(schemeJson));
|
||||
auto scheme = ColorScheme::FromJson(schemeJson);
|
||||
_globals.GetColorSchemes().emplace_back(scheme);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Finds a color scheme from our list of color schemes that matches the given
|
||||
// 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.
|
||||
// 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.
|
||||
// - Returns nullptr if no such match exists.
|
||||
// Arguments:
|
||||
// - json: an object which should be a partial serialization of a ColorScheme object.
|
||||
@@ -535,17 +588,15 @@ void CascadiaSettings::_LayerOrCreateColorScheme(const Json::Value& schemeJson)
|
||||
// color scheme exists.
|
||||
ColorScheme* CascadiaSettings::_FindMatchingColorScheme(const Json::Value& schemeJson)
|
||||
{
|
||||
if (auto schemeName = ColorScheme::GetNameFromJson(schemeJson))
|
||||
for (auto& scheme : _globals.GetColorSchemes())
|
||||
{
|
||||
auto& schemes = _globals.GetColorSchemes();
|
||||
auto iterator = schemes.find(*schemeName);
|
||||
if (iterator != schemes.end())
|
||||
if (scheme.ShouldBeLayered(schemeJson))
|
||||
{
|
||||
// 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 &iterator->second;
|
||||
return &scheme;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "ColorScheme.h"
|
||||
#include "DefaultSettings.h"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
@@ -17,7 +16,6 @@ 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",
|
||||
@@ -40,9 +38,8 @@ static constexpr std::array<std::string_view, 16> TableColors = {
|
||||
ColorScheme::ColorScheme() :
|
||||
_schemeName{ L"" },
|
||||
_table{},
|
||||
_defaultForeground{ DEFAULT_FOREGROUND_WITH_ALPHA },
|
||||
_defaultBackground{ DEFAULT_BACKGROUND_WITH_ALPHA },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND }
|
||||
_defaultForeground{ RGB(242, 242, 242) },
|
||||
_defaultBackground{ RGB(12, 12, 12) }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,8 +47,7 @@ ColorScheme::ColorScheme(std::wstring name, COLORREF defaultFg, COLORREF default
|
||||
_schemeName{ name },
|
||||
_table{},
|
||||
_defaultForeground{ defaultFg },
|
||||
_defaultBackground{ defaultBg },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND }
|
||||
_defaultBackground{ defaultBg }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -70,7 +66,6 @@ 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++)
|
||||
@@ -91,7 +86,6 @@ 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)
|
||||
@@ -161,11 +155,6 @@ 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)] })
|
||||
@@ -214,25 +203,3 @@ 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,16 +49,12 @@ 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,7 +7,6 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
@@ -21,15 +20,12 @@ 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" };
|
||||
@@ -41,14 +37,11 @@ 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 },
|
||||
_launchMode{ LaunchMode::DefaultMode }
|
||||
_copyOnSelect{ false }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,12 +49,12 @@ GlobalAppSettings::~GlobalAppSettings()
|
||||
{
|
||||
}
|
||||
|
||||
std::unordered_map<std::wstring, ColorScheme>& GlobalAppSettings::GetColorSchemes() noexcept
|
||||
const std::vector<ColorScheme>& GlobalAppSettings::GetColorSchemes() const noexcept
|
||||
{
|
||||
return _colorSchemes;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::wstring, ColorScheme>& GlobalAppSettings::GetColorSchemes() const noexcept
|
||||
std::vector<ColorScheme>& GlobalAppSettings::GetColorSchemes() noexcept
|
||||
{
|
||||
return _colorSchemes;
|
||||
}
|
||||
@@ -131,16 +124,6 @@ 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
|
||||
{
|
||||
@@ -151,17 +134,6 @@ 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:
|
||||
@@ -175,7 +147,6 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce
|
||||
settings.KeyBindings(GetKeybindings());
|
||||
settings.InitialRows(_initialRows);
|
||||
settings.InitialCols(_initialCols);
|
||||
|
||||
settings.WordDelimiters(_wordDelimiters);
|
||||
settings.CopyOnSelect(_copyOnSelect);
|
||||
}
|
||||
@@ -193,13 +164,11 @@ 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();
|
||||
|
||||
@@ -239,10 +208,7 @@ 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();
|
||||
@@ -263,11 +229,6 @@ 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));
|
||||
@@ -319,126 +280,3 @@ 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,7 +21,6 @@ Author(s):
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class ColorSchemeTests;
|
||||
};
|
||||
|
||||
namespace TerminalApp
|
||||
@@ -35,10 +34,8 @@ public:
|
||||
GlobalAppSettings();
|
||||
~GlobalAppSettings();
|
||||
|
||||
std::unordered_map<std::wstring, ColorScheme>& GetColorSchemes() noexcept;
|
||||
const std::unordered_map<std::wstring, ColorScheme>& GetColorSchemes() const noexcept;
|
||||
void AddColorScheme(ColorScheme scheme);
|
||||
|
||||
const std::vector<ColorScheme>& GetColorSchemes() const noexcept;
|
||||
std::vector<ColorScheme>& GetColorSchemes() noexcept;
|
||||
void SetDefaultProfile(const GUID defaultProfile) noexcept;
|
||||
GUID GetDefaultProfile() const noexcept;
|
||||
|
||||
@@ -61,13 +58,6 @@ 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;
|
||||
@@ -80,14 +70,11 @@ private:
|
||||
GUID _defaultProfile;
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::AppKeyBindings> _keybindings;
|
||||
|
||||
std::unordered_map<std::wstring, ColorScheme> _colorSchemes;
|
||||
std::vector<ColorScheme> _colorSchemes;
|
||||
|
||||
int32_t _initialRows;
|
||||
int32_t _initialCols;
|
||||
|
||||
std::optional<int32_t> _initialX;
|
||||
std::optional<int32_t> _initialY;
|
||||
|
||||
bool _showStatusline;
|
||||
bool _alwaysShowTabs;
|
||||
bool _showTitleInTitlebar;
|
||||
@@ -97,21 +84,8 @@ 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;
|
||||
};
|
||||
|
||||
@@ -21,18 +21,18 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// These event handlers simply forward each buttons click events up to the
|
||||
// events we've exposed.
|
||||
void MinMaxCloseControl::_MinimizeClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
||||
void MinMaxCloseControl::_MinimizeClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
RoutedEventArgs const& e)
|
||||
{
|
||||
_minimizeClickHandlers(*this, e);
|
||||
}
|
||||
|
||||
void MinMaxCloseControl::_MaximizeClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
||||
void MinMaxCloseControl::_MaximizeClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
RoutedEventArgs const& e)
|
||||
{
|
||||
_maximizeClickHandlers(*this, e);
|
||||
}
|
||||
void MinMaxCloseControl::_CloseClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
||||
void MinMaxCloseControl::_CloseClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
RoutedEventArgs const& e)
|
||||
{
|
||||
_closeClickHandlers(*this, e);
|
||||
|
||||
@@ -8,7 +8,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="Transparent"
|
||||
Background="{ThemeResource SystemChromeLowColor}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal"
|
||||
@@ -26,7 +26,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<SolidColorBrush x:Key="CaptionButtonBackground" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver" Color="#e81123"/>
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver" Color="Red"/>
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePointerOver" Color="White"/>
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPressed" Color="#f1707a"/>
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePressed" Color="Black"/>
|
||||
@@ -39,7 +39,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed" ResourceKey="SystemControlForegroundBaseHighBrush"/>
|
||||
<SolidColorBrush x:Key="CaptionButtonBackground" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver" Color="#e81123"/>
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver" Color="Red"/>
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePointerOver" Color="White"/>
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPressed" Color="#f1707a"/>
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePressed" Color="Black"/>
|
||||
@@ -131,7 +131,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ResourceDictionary>
|
||||
</StackPanel.Resources>
|
||||
|
||||
<Button Height="36.0" MinWidth="46.0" Width="46.0" x:Name="MinimizeButton" Style="{StaticResource CaptionButton}" Click="_MinimizeClick"
|
||||
<Button Height="36.0" MinWidth="45.0" Width="45.0" x:Name="MinimizeButton" Style="{StaticResource CaptionButton}" Click="_MinimizeClick"
|
||||
AutomationProperties.Name="Minimize">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
@@ -139,7 +139,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<Button Height="36.0" MinWidth="46.0" Width="46.0" x:Name="MaximizeButton" Style="{StaticResource CaptionButton}" Click="_MaximizeClick"
|
||||
<Button Height="36.0" MinWidth="45.0" Width="45.0" x:Name="MaximizeButton" Style="{StaticResource CaptionButton}" Click="_MaximizeClick"
|
||||
AutomationProperties.Name="Maximize">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
@@ -148,7 +148,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<Button Height="36.0" MinWidth="46.0" Width="46.0" x:Name="CloseButton" Style="{StaticResource CaptionButton}" Click="_CloseClick"
|
||||
<Button Height="36.0" MinWidth="45.0" Width="45.0" x:Name="CloseButton" Style="{StaticResource CaptionButton}" Click="_CloseClick"
|
||||
AutomationProperties.Name="Close">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
|
||||
@@ -5,50 +5,38 @@
|
||||
#include "Pane.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Xaml::Media;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::TerminalApp;
|
||||
|
||||
static const int PaneBorderSize = 2;
|
||||
static const int CombinedPaneBorderSize = 2 * PaneBorderSize;
|
||||
static const int PaneSeparatorSize = 4;
|
||||
static const float Half = 0.50f;
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr };
|
||||
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr };
|
||||
|
||||
Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocused) :
|
||||
_control{ control },
|
||||
_lastActive{ lastFocused },
|
||||
_lastFocused{ lastFocused },
|
||||
_profile{ profile }
|
||||
{
|
||||
_root.Children().Append(_border);
|
||||
_border.Child(_control);
|
||||
|
||||
_root.Children().Append(_control);
|
||||
_connectionClosedToken = _control.ConnectionClosed({ this, &Pane::_ControlClosedHandler });
|
||||
|
||||
// On the first Pane's creation, lookup resources we'll use to theme the
|
||||
// Pane, including the brushed to use for the focused/unfocused border
|
||||
// color.
|
||||
if (s_focusedBorderBrush == nullptr || s_unfocusedBorderBrush == nullptr)
|
||||
// Set the background of the pane to match that of the theme's default grid
|
||||
// background. This way, we'll match the small underline under the tabs, and
|
||||
// the UI will be consistent on bot light and dark modes.
|
||||
const auto res = Application::Current().Resources();
|
||||
const auto key = winrt::box_value(L"BackgroundGridThemeStyle");
|
||||
if (res.HasKey(key))
|
||||
{
|
||||
_SetupResources();
|
||||
const auto g = res.Lookup(key);
|
||||
const auto style = g.try_as<winrt::Windows::UI::Xaml::Style>();
|
||||
// try_as fails by returning nullptr
|
||||
if (style)
|
||||
{
|
||||
_root.Style(style);
|
||||
}
|
||||
}
|
||||
|
||||
// Register an event with the control to have it inform us when it gains focus.
|
||||
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
|
||||
|
||||
// When our border is tapped, make sure to transfer focus to our control.
|
||||
// LOAD-BEARING: This will NOT work if the border's BorderBrush is set to
|
||||
// Colors::Transparent! The border won't get Tapped events, and they'll fall
|
||||
// through to something else.
|
||||
_border.Tapped([this](auto&, auto& e) {
|
||||
_FocusFirstChild();
|
||||
e.Handled(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -120,13 +108,14 @@ bool Pane::_Resize(const Direction& direction)
|
||||
// actualDimension is the size in DIPs of this pane in the direction we're
|
||||
// resizing.
|
||||
auto actualDimension = changeWidth ? actualSize.Width : actualSize.Height;
|
||||
actualDimension -= PaneSeparatorSize;
|
||||
|
||||
const auto firstMinSize = _firstChild->_GetMinSize();
|
||||
const auto secondMinSize = _secondChild->_GetMinSize();
|
||||
|
||||
// These are the minimum amount of space we need for each of our children
|
||||
const auto firstMinDimension = (changeWidth ? firstMinSize.Width : firstMinSize.Height) + PaneBorderSize;
|
||||
const auto secondMinDimension = (changeWidth ? secondMinSize.Width : secondMinSize.Height) + PaneBorderSize;
|
||||
const auto firstMinDimension = changeWidth ? firstMinSize.Width : firstMinSize.Height;
|
||||
const auto secondMinDimension = changeWidth ? secondMinSize.Width : secondMinSize.Height;
|
||||
|
||||
const auto firstMinPercent = firstMinDimension / actualDimension;
|
||||
const auto secondMinPercent = secondMinDimension / actualDimension;
|
||||
@@ -135,11 +124,6 @@ bool Pane::_Resize(const Direction& direction)
|
||||
// to reserve for the second.
|
||||
const auto firstMaxPercent = 1.0f - secondMinPercent;
|
||||
|
||||
if (firstMaxPercent < firstMinPercent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_firstPercent = std::clamp(_firstPercent.value() - amount, firstMinPercent, firstMaxPercent);
|
||||
// Update the other child to fill the remaining percent
|
||||
_secondPercent = 1.0f - _firstPercent.value();
|
||||
@@ -174,8 +158,8 @@ bool Pane::ResizePane(const Direction& direction)
|
||||
// If it is, and the requested resize direction matches our separator, then
|
||||
// we're the pane that needs to adjust its separator.
|
||||
// If our separator is the wrong direction, then we can't handle it.
|
||||
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastActive;
|
||||
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastActive;
|
||||
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastFocused;
|
||||
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastFocused;
|
||||
if (firstIsFocused || secondIsFocused)
|
||||
{
|
||||
return _Resize(direction);
|
||||
@@ -233,7 +217,7 @@ bool Pane::_NavigateFocus(const Direction& direction)
|
||||
|
||||
// Transfer focus to our child, and update the focus of our tree.
|
||||
newlyFocusedChild->_FocusFirstChild();
|
||||
UpdateVisuals();
|
||||
UpdateFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -263,8 +247,8 @@ bool Pane::NavigateFocus(const Direction& direction)
|
||||
// Check if either our first or second child is the currently focused leaf.
|
||||
// If it is, and the requested move direction matches our separator, then
|
||||
// we're the pane that needs to handle this focus move.
|
||||
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastActive;
|
||||
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastActive;
|
||||
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastFocused;
|
||||
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastFocused;
|
||||
if (firstIsFocused || secondIsFocused)
|
||||
{
|
||||
return _NavigateFocus(direction);
|
||||
@@ -357,63 +341,37 @@ Controls::Grid Pane::GetRootElement()
|
||||
// not currently focused.
|
||||
// Return Value:
|
||||
// - nullptr if we're a leaf and unfocused, or no children were marked
|
||||
// `_lastActive`, else returns this
|
||||
std::shared_ptr<Pane> Pane::GetActivePane()
|
||||
// `_lastFocused`, else returns this
|
||||
std::shared_ptr<Pane> Pane::GetFocusedPane()
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
return _lastActive ? shared_from_this() : nullptr;
|
||||
return _lastFocused ? shared_from_this() : nullptr;
|
||||
}
|
||||
|
||||
auto firstFocused = _firstChild->GetActivePane();
|
||||
auto firstFocused = _firstChild->GetFocusedPane();
|
||||
if (firstFocused != nullptr)
|
||||
{
|
||||
return firstFocused;
|
||||
}
|
||||
return _secondChild->GetActivePane();
|
||||
return _secondChild->GetFocusedPane();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the TermControl of this pane. If this Pane is not a leaf, this will return nullptr.
|
||||
// - Returns nullptr if no children of this pane were the last control to be
|
||||
// focused, or the TermControl that _was_ the last control to be focused (if
|
||||
// there was one).
|
||||
// - This control might not currently be focused, if the tab itself is not
|
||||
// currently focused.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - nullptr if this Pane is a parent, otherwise the TermControl of this Pane.
|
||||
TermControl Pane::GetTerminalControl()
|
||||
// - nullptr if no children were marked `_lastFocused`, else the TermControl
|
||||
// that was last focused.
|
||||
TermControl Pane::GetFocusedTerminalControl()
|
||||
{
|
||||
return _IsLeaf() ? _control : nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Recursively remove the "Active" state from this Pane and all it's children.
|
||||
// - Updates our visuals to match our new state, including highlighting our borders.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::ClearActive()
|
||||
{
|
||||
_lastActive = false;
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
_firstChild->ClearActive();
|
||||
_secondChild->ClearActive();
|
||||
}
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the "Active" state on this Pane. Only one Pane in a tree of Panes
|
||||
// should be "active", and that pane should be a leaf.
|
||||
// - Updates our visuals to match our new state, including highlighting our borders.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::SetActive()
|
||||
{
|
||||
_lastActive = true;
|
||||
UpdateVisuals();
|
||||
auto lastFocused = GetFocusedPane();
|
||||
return lastFocused ? lastFocused->_control : nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -427,7 +385,7 @@ void Pane::SetActive()
|
||||
// focused, else the GUID of the profile of the last control to be focused
|
||||
std::optional<GUID> Pane::GetFocusedProfile()
|
||||
{
|
||||
auto lastFocused = GetActivePane();
|
||||
auto lastFocused = GetFocusedPane();
|
||||
return lastFocused ? lastFocused->_profile : std::nullopt;
|
||||
}
|
||||
|
||||
@@ -439,7 +397,7 @@ std::optional<GUID> Pane::GetFocusedProfile()
|
||||
// - true iff we were the last pane focused in this tree of panes.
|
||||
bool Pane::WasLastFocused() const noexcept
|
||||
{
|
||||
return _lastActive;
|
||||
return _lastFocused;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -466,21 +424,35 @@ bool Pane::_HasFocusedChild() const noexcept
|
||||
// We're intentionally making this one giant expression, so the compiler
|
||||
// will skip the following lookups if one of the lookups before it returns
|
||||
// true
|
||||
return (_control && _lastActive) ||
|
||||
return (_control && _control.FocusState() != FocusState::Unfocused) ||
|
||||
(_firstChild && _firstChild->_HasFocusedChild()) ||
|
||||
(_secondChild && _secondChild->_HasFocusedChild());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the focus state of this pane. We'll make sure to colorize our
|
||||
// borders depending on if we are the active pane or not.
|
||||
// - Update the focus state of this pane, and all its descendants.
|
||||
// * If this is a leaf node, and our control is actively focused, we'll mark
|
||||
// ourselves as the _lastFocused.
|
||||
// * If we're not a leaf, we'll recurse on our children to check them.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::UpdateVisuals()
|
||||
void Pane::UpdateFocus()
|
||||
{
|
||||
_border.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush);
|
||||
if (_IsLeaf())
|
||||
{
|
||||
const auto controlFocused = _control &&
|
||||
_control.FocusState() != FocusState::Unfocused;
|
||||
|
||||
_lastFocused = controlFocused;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastFocused = false;
|
||||
_firstChild->UpdateFocus();
|
||||
_secondChild->UpdateFocus();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -558,13 +530,6 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
// If the only child left is a leaf, that means we're a leaf now.
|
||||
if (remainingChild->_IsLeaf())
|
||||
{
|
||||
// When the remaining child is a leaf, that means both our children were
|
||||
// previously leaves, and the only difference in their borders is the
|
||||
// border that we gave them. Take a bitwise AND of those two children to
|
||||
// remove that border. Other borders the children might have, they
|
||||
// inherited from us, so the flag will be set for both children.
|
||||
_borders = _firstChild->_borders & _secondChild->_borders;
|
||||
|
||||
// take the control and profile of the pane that _wasn't_ closed.
|
||||
_control = remainingChild->_control;
|
||||
_profile = remainingChild->_profile;
|
||||
@@ -583,42 +548,28 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
|
||||
// If either of our children was focused, we want to take that focus from
|
||||
// them.
|
||||
_lastActive = _firstChild->_lastActive || _secondChild->_lastActive;
|
||||
_lastFocused = _firstChild->_lastFocused || _secondChild->_lastFocused;
|
||||
|
||||
// Remove all the ui elements of our children. This'll make sure we can
|
||||
// re-attach the TermControl to our Grid.
|
||||
_firstChild->_root.Children().Clear();
|
||||
_secondChild->_root.Children().Clear();
|
||||
_firstChild->_border.Child(nullptr);
|
||||
_secondChild->_border.Child(nullptr);
|
||||
|
||||
// Reset our UI:
|
||||
_root.Children().Clear();
|
||||
_border.Child(nullptr);
|
||||
_root.ColumnDefinitions().Clear();
|
||||
_root.RowDefinitions().Clear();
|
||||
_separatorRoot = { nullptr };
|
||||
|
||||
// Reattach the TermControl to our grid.
|
||||
_root.Children().Append(_border);
|
||||
_border.Child(_control);
|
||||
_root.Children().Append(_control);
|
||||
|
||||
// Make sure to set our _splitState before focusing the control. If you
|
||||
// fail to do this, when the tab handles the GotFocus event and asks us
|
||||
// what our active control is, we won't technically be a "leaf", and
|
||||
// GetTerminalControl will return null.
|
||||
_splitState = SplitState::None;
|
||||
|
||||
// re-attach our handler for the control's GotFocus event.
|
||||
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
|
||||
|
||||
// If we're inheriting the "last active" state from one of our children,
|
||||
// focus our control now. This should trigger our own GotFocus event.
|
||||
if (_lastActive)
|
||||
if (_lastFocused)
|
||||
{
|
||||
_control.Focus(FocusState::Programmatic);
|
||||
}
|
||||
|
||||
_UpdateBorders();
|
||||
_splitState = SplitState::None;
|
||||
|
||||
// Release our children.
|
||||
_firstChild = nullptr;
|
||||
@@ -626,18 +577,6 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine which border flag we gave to the child when we first split
|
||||
// it, so that we can take just that flag away from them.
|
||||
Borders clearBorderFlag = Borders::None;
|
||||
if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
clearBorderFlag = closeFirst ? Borders::Top : Borders::Bottom;
|
||||
}
|
||||
else if (_splitState == SplitState::Vertical)
|
||||
{
|
||||
clearBorderFlag = closeFirst ? Borders::Left : Borders::Right;
|
||||
}
|
||||
|
||||
// First stash away references to the old panes and their tokens
|
||||
const auto oldFirstToken = _firstClosedToken;
|
||||
const auto oldSecondToken = _secondClosedToken;
|
||||
@@ -646,6 +585,7 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
|
||||
// Steal all the state from our child
|
||||
_splitState = remainingChild->_splitState;
|
||||
_separatorRoot = remainingChild->_separatorRoot;
|
||||
_firstChild = remainingChild->_firstChild;
|
||||
_secondChild = remainingChild->_secondChild;
|
||||
|
||||
@@ -663,7 +603,6 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
|
||||
// Reset our UI:
|
||||
_root.Children().Clear();
|
||||
_border.Child(nullptr);
|
||||
_root.ColumnDefinitions().Clear();
|
||||
_root.RowDefinitions().Clear();
|
||||
|
||||
@@ -687,21 +626,13 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
// Remove the child's UI elements from the child's grid, so we can
|
||||
// attach them to us instead.
|
||||
remainingChild->_root.Children().Clear();
|
||||
remainingChild->_border.Child(nullptr);
|
||||
|
||||
_root.Children().Append(_firstChild->GetRootElement());
|
||||
_root.Children().Append(_separatorRoot);
|
||||
_root.Children().Append(_secondChild->GetRootElement());
|
||||
|
||||
// Take the flag away from the children that they inherited from their
|
||||
// parent, and update their borders to visually match
|
||||
WI_ClearAllFlags(_firstChild->_borders, clearBorderFlag);
|
||||
WI_ClearAllFlags(_secondChild->_borders, clearBorderFlag);
|
||||
_UpdateBorders();
|
||||
_firstChild->_UpdateBorders();
|
||||
_secondChild->_UpdateBorders();
|
||||
|
||||
// If the closed child was focused, transfer the focus to it's first sibling.
|
||||
if (closedChild->_lastActive)
|
||||
if (closedChild->_lastFocused)
|
||||
{
|
||||
_FocusFirstChild();
|
||||
}
|
||||
@@ -709,6 +640,7 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
// Release the pointers that the child was holding.
|
||||
remainingChild->_firstChild = nullptr;
|
||||
remainingChild->_secondChild = nullptr;
|
||||
remainingChild->_separatorRoot = { nullptr };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,7 +682,10 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
{
|
||||
_root.ColumnDefinitions().Clear();
|
||||
|
||||
// Create two columns in this grid: one for each pane
|
||||
// Create three columns in this grid: one for each pane, and one for the separator.
|
||||
auto separatorColDef = Controls::ColumnDefinition();
|
||||
separatorColDef.Width(GridLengthHelper::Auto());
|
||||
|
||||
const auto paneSizes = _GetPaneSizes(rootSize.Width);
|
||||
|
||||
auto firstColDef = Controls::ColumnDefinition();
|
||||
@@ -760,13 +695,17 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
secondColDef.Width(GridLengthHelper::FromPixels(paneSizes.second));
|
||||
|
||||
_root.ColumnDefinitions().Append(firstColDef);
|
||||
_root.ColumnDefinitions().Append(separatorColDef);
|
||||
_root.ColumnDefinitions().Append(secondColDef);
|
||||
}
|
||||
else if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
_root.RowDefinitions().Clear();
|
||||
|
||||
// Create two rows in this grid: one for each pane
|
||||
// Create three rows in this grid: one for each pane, and one for the separator.
|
||||
auto separatorRowDef = Controls::RowDefinition();
|
||||
separatorRowDef.Height(GridLengthHelper::Auto());
|
||||
|
||||
const auto paneSizes = _GetPaneSizes(rootSize.Height);
|
||||
|
||||
auto firstRowDef = Controls::RowDefinition();
|
||||
@@ -776,6 +715,7 @@ void Pane::_CreateRowColDefinitions(const Size& rootSize)
|
||||
secondRowDef.Height(GridLengthHelper::FromPixels(paneSizes.second));
|
||||
|
||||
_root.RowDefinitions().Append(firstRowDef);
|
||||
_root.RowDefinitions().Append(separatorRowDef);
|
||||
_root.RowDefinitions().Append(secondRowDef);
|
||||
}
|
||||
}
|
||||
@@ -794,36 +734,23 @@ void Pane::_CreateSplitContent()
|
||||
gsl::narrow_cast<float>(_root.ActualHeight()) };
|
||||
|
||||
_CreateRowColDefinitions(actualSize);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the thickness of each side of our borders to match our _borders state.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::_UpdateBorders()
|
||||
{
|
||||
double top = 0, bottom = 0, left = 0, right = 0;
|
||||
|
||||
Thickness newBorders{ 0 };
|
||||
if (WI_IsFlagSet(_borders, Borders::Top))
|
||||
if (_splitState == SplitState::Vertical)
|
||||
{
|
||||
top = PaneBorderSize;
|
||||
// Create the pane separator
|
||||
_separatorRoot = Controls::Grid{};
|
||||
_separatorRoot.Width(PaneSeparatorSize);
|
||||
// NaN is the special value XAML uses for "Auto" sizing.
|
||||
_separatorRoot.Height(NAN);
|
||||
}
|
||||
if (WI_IsFlagSet(_borders, Borders::Bottom))
|
||||
else if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
bottom = PaneBorderSize;
|
||||
// Create the pane separator
|
||||
_separatorRoot = Controls::Grid{};
|
||||
_separatorRoot.Height(PaneSeparatorSize);
|
||||
// NaN is the special value XAML uses for "Auto" sizing.
|
||||
_separatorRoot.Width(NAN);
|
||||
}
|
||||
if (WI_IsFlagSet(_borders, Borders::Left))
|
||||
{
|
||||
left = PaneBorderSize;
|
||||
}
|
||||
if (WI_IsFlagSet(_borders, Borders::Right))
|
||||
{
|
||||
right = PaneBorderSize;
|
||||
}
|
||||
_border.BorderThickness(ThicknessHelper::FromLengths(left, top, right, bottom));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -837,28 +764,14 @@ void Pane::_ApplySplitDefinitions()
|
||||
if (_splitState == SplitState::Vertical)
|
||||
{
|
||||
Controls::Grid::SetColumn(_firstChild->GetRootElement(), 0);
|
||||
Controls::Grid::SetColumn(_secondChild->GetRootElement(), 1);
|
||||
|
||||
_firstChild->_borders = _borders | Borders::Right;
|
||||
_secondChild->_borders = _borders | Borders::Left;
|
||||
_borders = Borders::None;
|
||||
|
||||
_UpdateBorders();
|
||||
_firstChild->_UpdateBorders();
|
||||
_secondChild->_UpdateBorders();
|
||||
Controls::Grid::SetColumn(_separatorRoot, 1);
|
||||
Controls::Grid::SetColumn(_secondChild->GetRootElement(), 2);
|
||||
}
|
||||
else if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
Controls::Grid::SetRow(_firstChild->GetRootElement(), 0);
|
||||
Controls::Grid::SetRow(_secondChild->GetRootElement(), 1);
|
||||
|
||||
_firstChild->_borders = _borders | Borders::Bottom;
|
||||
_secondChild->_borders = _borders | Borders::Top;
|
||||
_borders = Borders::None;
|
||||
|
||||
_UpdateBorders();
|
||||
_firstChild->_UpdateBorders();
|
||||
_secondChild->_UpdateBorders();
|
||||
Controls::Grid::SetRow(_separatorRoot, 1);
|
||||
Controls::Grid::SetRow(_secondChild->GetRootElement(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -897,24 +810,24 @@ bool Pane::CanSplit(SplitState splitType)
|
||||
// - profile: The profile GUID to associate with the newly created pane.
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - The two newly created Panes
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
// - <none>
|
||||
void Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
if (_firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->Split(splitType, profile, control);
|
||||
_firstChild->Split(splitType, profile, control);
|
||||
}
|
||||
else if (_secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->Split(splitType, profile, control);
|
||||
_secondChild->Split(splitType, profile, control);
|
||||
}
|
||||
|
||||
return { nullptr, nullptr };
|
||||
return;
|
||||
}
|
||||
|
||||
return _Split(splitType, profile, control);
|
||||
_Split(splitType, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -932,7 +845,7 @@ bool Pane::_CanSplit(SplitState splitType)
|
||||
|
||||
if (splitType == SplitState::Vertical)
|
||||
{
|
||||
const auto widthMinusSeparator = actualSize.Width - CombinedPaneBorderSize;
|
||||
const auto widthMinusSeparator = actualSize.Width - PaneSeparatorSize;
|
||||
const auto newWidth = widthMinusSeparator * Half;
|
||||
|
||||
return newWidth > minSize.Width;
|
||||
@@ -940,7 +853,7 @@ bool Pane::_CanSplit(SplitState splitType)
|
||||
|
||||
if (splitType == SplitState::Horizontal)
|
||||
{
|
||||
const auto heightMinusSeparator = actualSize.Height - CombinedPaneBorderSize;
|
||||
const auto heightMinusSeparator = actualSize.Height - PaneSeparatorSize;
|
||||
const auto newHeight = heightMinusSeparator * Half;
|
||||
|
||||
return newHeight > minSize.Height;
|
||||
@@ -957,8 +870,8 @@ bool Pane::_CanSplit(SplitState splitType)
|
||||
// - profile: The profile GUID to associate with the newly created pane.
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - The two newly created Panes
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
// - <none>
|
||||
void Pane::_Split(SplitState splitType, const GUID& profile, const TermControl& control)
|
||||
{
|
||||
// Lock the create/close lock so that another operation won't concurrently
|
||||
// modify our tree
|
||||
@@ -968,11 +881,6 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
|
||||
_control.ConnectionClosed(_connectionClosedToken);
|
||||
_connectionClosedToken.value = 0;
|
||||
|
||||
// Remove our old GotFocus handler from the control. We don't what the
|
||||
// control telling us that it's now focused, we want it telling its new
|
||||
// parent.
|
||||
_gotFocusRevoker.revoke();
|
||||
|
||||
_splitState = splitType;
|
||||
|
||||
_firstPercent = { Half };
|
||||
@@ -983,7 +891,6 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
|
||||
// Remove any children we currently have. We can't add the existing
|
||||
// TermControl to a new grid until we do this.
|
||||
_root.Children().Clear();
|
||||
_border.Child(nullptr);
|
||||
|
||||
// Create two new Panes
|
||||
// Move our control, guid into the first one.
|
||||
@@ -994,6 +901,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
|
||||
_secondChild = std::make_shared<Pane>(profile, control);
|
||||
|
||||
_root.Children().Append(_firstChild->GetRootElement());
|
||||
_root.Children().Append(_separatorRoot);
|
||||
_root.Children().Append(_secondChild->GetRootElement());
|
||||
|
||||
_ApplySplitDefinitions();
|
||||
@@ -1001,18 +909,16 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState
|
||||
// Register event handlers on our children to handle their Close events
|
||||
_SetupChildCloseHandlers();
|
||||
|
||||
_lastActive = false;
|
||||
|
||||
return { _firstChild, _secondChild };
|
||||
_lastFocused = false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the size in pixels of each of our children, given the full size they
|
||||
// should fill. Since these children own their own separators (borders), this
|
||||
// size is their portion of our _entire_ size.
|
||||
// should fill. Accounts for the size of the separator that should be between
|
||||
// them as well.
|
||||
// Arguments:
|
||||
// - fullSize: the amount of space in pixels that should be filled by our
|
||||
// children and their separators
|
||||
// children and their separator
|
||||
// Return Value:
|
||||
// - a pair with the size of our first child and the size of our second child,
|
||||
// respectively.
|
||||
@@ -1023,16 +929,16 @@ std::pair<float, float> Pane::_GetPaneSizes(const float& fullSize)
|
||||
THROW_HR(E_FAIL);
|
||||
}
|
||||
|
||||
const auto firstSize = fullSize * _firstPercent.value();
|
||||
const auto secondSize = fullSize * _secondPercent.value();
|
||||
|
||||
const auto sizeMinusSeparator = fullSize - PaneSeparatorSize;
|
||||
const auto firstSize = sizeMinusSeparator * _firstPercent.value();
|
||||
const auto secondSize = sizeMinusSeparator * _secondPercent.value();
|
||||
return { firstSize, secondSize };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the absolute minimum size that this pane can be resized to and still
|
||||
// have 1x1 character visible, in each of its children. If we're a leaf, we'll
|
||||
// include the space needed for borders _within_ us.
|
||||
// have 1x1 character visible, in each of its children. This includes the
|
||||
// space needed for the separator.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
@@ -1042,87 +948,14 @@ Size Pane::_GetMinSize() const
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
auto controlSize = _control.MinimumSize();
|
||||
auto newWidth = controlSize.Width;
|
||||
auto newHeight = controlSize.Height;
|
||||
|
||||
newWidth += WI_IsFlagSet(_borders, Borders::Left) ? CombinedPaneBorderSize : 0;
|
||||
newWidth += WI_IsFlagSet(_borders, Borders::Right) ? CombinedPaneBorderSize : 0;
|
||||
newHeight += WI_IsFlagSet(_borders, Borders::Top) ? CombinedPaneBorderSize : 0;
|
||||
newHeight += WI_IsFlagSet(_borders, Borders::Bottom) ? CombinedPaneBorderSize : 0;
|
||||
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto firstSize = _firstChild->_GetMinSize();
|
||||
const auto secondSize = _secondChild->_GetMinSize();
|
||||
|
||||
const auto newWidth = firstSize.Width + secondSize.Width;
|
||||
const auto newHeight = firstSize.Height + secondSize.Height;
|
||||
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
}
|
||||
|
||||
// Event Description:
|
||||
// - Called when our control gains focus. We'll use this to trigger our GotFocus
|
||||
// callback. The tab that's hosting us should have registered a callback which
|
||||
// can be used to mark us as active.
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::_ControlGotFocusHandler(winrt::Windows::Foundation::IInspectable const& /* sender */,
|
||||
RoutedEventArgs const& /* args */)
|
||||
{
|
||||
_GotFocusHandlers(shared_from_this());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Attempts to load some XAML resources that the Pane will need. This includes:
|
||||
// * The Color we'll use for active Panes's borders - SystemAccentColor
|
||||
// * The Brush we'll use for inactive Panes - TabViewBackground (to match the
|
||||
// color of the titlebar)
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::_SetupResources()
|
||||
{
|
||||
const auto res = Application::Current().Resources();
|
||||
const auto accentColorKey = winrt::box_value(L"SystemAccentColor");
|
||||
if (res.HasKey(accentColorKey))
|
||||
{
|
||||
const auto colorFromResources = res.Lookup(accentColorKey);
|
||||
// If SystemAccentColor is _not_ a Color for some reason, use
|
||||
// Transparent as the color, so we don't do this process again on
|
||||
// the next pane (by leaving s_focusedBorderBrush nullptr)
|
||||
auto actualColor = winrt::unbox_value_or<Color>(colorFromResources, Colors::Black());
|
||||
s_focusedBorderBrush = SolidColorBrush(actualColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// DON'T use Transparent here - if it's "Transparent", then it won't
|
||||
// be able to hittest for clicks, and then clicking on the border
|
||||
// will eat focus.
|
||||
s_focusedBorderBrush = SolidColorBrush{ Colors::Black() };
|
||||
return _control.MinimumSize();
|
||||
}
|
||||
|
||||
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
|
||||
if (res.HasKey(accentColorKey))
|
||||
{
|
||||
winrt::Windows::Foundation::IInspectable obj = res.Lookup(tabViewBackgroundKey);
|
||||
s_unfocusedBorderBrush = obj.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// DON'T use Transparent here - if it's "Transparent", then it won't
|
||||
// be able to hittest for clicks, and then clicking on the border
|
||||
// will eat focus.
|
||||
s_unfocusedBorderBrush = SolidColorBrush{ Colors::Black() };
|
||||
}
|
||||
const auto firstSize = _firstChild->_GetMinSize();
|
||||
const auto secondSize = _secondChild->_GetMinSize();
|
||||
const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0);
|
||||
const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0);
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
DEFINE_EVENT(Pane, Closed, _closedHandlers, ConnectionClosedEventArgs);
|
||||
DEFINE_EVENT(Pane, GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
||||
@@ -23,16 +23,6 @@
|
||||
#include <winrt/TerminalApp.h>
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
enum class Borders : int
|
||||
{
|
||||
None = 0x0,
|
||||
Top = 0x1,
|
||||
Bottom = 0x2,
|
||||
Left = 0x4,
|
||||
Right = 0x8
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(Borders);
|
||||
|
||||
class Pane : public std::enable_shared_from_this<Pane>
|
||||
{
|
||||
public:
|
||||
@@ -43,43 +33,33 @@ public:
|
||||
Horizontal = 2
|
||||
};
|
||||
|
||||
Pane(const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control,
|
||||
const bool lastFocused = false);
|
||||
Pane(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control, const bool lastFocused = false);
|
||||
|
||||
std::shared_ptr<Pane> GetActivePane();
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl GetTerminalControl();
|
||||
std::shared_ptr<Pane> GetFocusedPane();
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl GetFocusedTerminalControl();
|
||||
std::optional<GUID> GetFocusedProfile();
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
|
||||
|
||||
bool WasLastFocused() const noexcept;
|
||||
void UpdateVisuals();
|
||||
void ClearActive();
|
||||
void SetActive();
|
||||
void UpdateFocus();
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings,
|
||||
const GUID& profile);
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
bool ResizePane(const winrt::TerminalApp::Direction& direction);
|
||||
bool NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
bool CanSplit(SplitState splitType);
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(SplitState splitType,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
void Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
void Close();
|
||||
|
||||
DECLARE_EVENT(Closed, _closedHandlers, winrt::Microsoft::Terminal::TerminalControl::ConnectionClosedEventArgs);
|
||||
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
||||
private:
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _root{};
|
||||
winrt::Windows::UI::Xaml::Controls::Border _border{};
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _separatorRoot{ nullptr };
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl _control{ nullptr };
|
||||
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
|
||||
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;
|
||||
|
||||
std::shared_ptr<Pane> _firstChild{ nullptr };
|
||||
std::shared_ptr<Pane> _secondChild{ nullptr };
|
||||
@@ -87,31 +67,23 @@ private:
|
||||
std::optional<float> _firstPercent{ std::nullopt };
|
||||
std::optional<float> _secondPercent{ std::nullopt };
|
||||
|
||||
bool _lastActive{ false };
|
||||
bool _lastFocused{ false };
|
||||
std::optional<GUID> _profile{ std::nullopt };
|
||||
winrt::event_token _connectionClosedToken{ 0 };
|
||||
winrt::event_token _firstClosedToken{ 0 };
|
||||
winrt::event_token _secondClosedToken{ 0 };
|
||||
|
||||
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
|
||||
|
||||
std::shared_mutex _createCloseLock{};
|
||||
|
||||
Borders _borders{ Borders::None };
|
||||
|
||||
bool _IsLeaf() const noexcept;
|
||||
bool _HasFocusedChild() const noexcept;
|
||||
void _SetupChildCloseHandlers();
|
||||
|
||||
bool _CanSplit(SplitState splitType);
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(SplitState splitType,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
void _Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
void _CreateRowColDefinitions(const winrt::Windows::Foundation::Size& rootSize);
|
||||
void _CreateSplitContent();
|
||||
void _ApplySplitDefinitions();
|
||||
void _UpdateBorders();
|
||||
|
||||
bool _Resize(const winrt::TerminalApp::Direction& direction);
|
||||
bool _NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
@@ -124,8 +96,6 @@ private:
|
||||
std::pair<float, float> _GetPaneSizes(const float& fullSize);
|
||||
|
||||
winrt::Windows::Foundation::Size _GetMinSize() const;
|
||||
void _ControlGotFocusHandler(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
// Function Description:
|
||||
// - Returns true if the given direction can be used with the given split
|
||||
@@ -160,6 +130,4 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _SetupResources();
|
||||
};
|
||||
|
||||
@@ -24,10 +24,8 @@ static constexpr std::string_view HiddenKey{ "hidden" };
|
||||
|
||||
static constexpr std::string_view ForegroundKey{ "foreground" };
|
||||
static constexpr std::string_view BackgroundKey{ "background" };
|
||||
static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" };
|
||||
static constexpr std::string_view ColorTableKey{ "colorTable" };
|
||||
static constexpr std::string_view TabTitleKey{ "tabTitle" };
|
||||
static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
|
||||
static constexpr std::string_view HistorySizeKey{ "historySize" };
|
||||
static constexpr std::string_view SnapOnInputKey{ "snapOnInput" };
|
||||
static constexpr std::string_view CursorColorKey{ "cursorColor" };
|
||||
@@ -91,10 +89,8 @@ Profile::Profile(const std::optional<GUID>& guid) :
|
||||
|
||||
_defaultForeground{},
|
||||
_defaultBackground{},
|
||||
_selectionBackground{},
|
||||
_colorTable{},
|
||||
_tabTitle{},
|
||||
_suppressApplicationTitle{},
|
||||
_historySize{ DEFAULT_HISTORY_SIZE },
|
||||
_snapOnInput{ true },
|
||||
_cursorColor{ DEFAULT_CURSOR_COLOR },
|
||||
@@ -145,6 +141,27 @@ void Profile::SetSource(std::wstring_view sourceNamespace) noexcept
|
||||
_source = sourceNamespace;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Searches a list of color schemes to find one matching the given name. Will
|
||||
//return the first match in the list, if the list has multiple schemes with the same name.
|
||||
// Arguments:
|
||||
// - schemes: a list of schemes to search
|
||||
// - schemeName: the name of the sceme to look for
|
||||
// Return Value:
|
||||
// - a non-ownership pointer to the matching scheme if we found one, else nullptr
|
||||
const ColorScheme* _FindScheme(const std::vector<ColorScheme>& schemes,
|
||||
const std::wstring& schemeName)
|
||||
{
|
||||
for (auto& scheme : schemes)
|
||||
{
|
||||
if (scheme.GetName() == schemeName)
|
||||
{
|
||||
return &scheme;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a TerminalSettings from this object. Apply our settings, as well as
|
||||
// any colors from our color scheme, if we have one.
|
||||
@@ -152,7 +169,7 @@ void Profile::SetSource(std::wstring_view sourceNamespace) noexcept
|
||||
// - schemes: a list of schemes to look for our color scheme in, if we have one.
|
||||
// Return Value:
|
||||
// - a new TerminalSettings object with our settings in it.
|
||||
TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::wstring, ColorScheme>& schemes) const
|
||||
TerminalSettings Profile::CreateTerminalSettings(const std::vector<ColorScheme>& schemes) const
|
||||
{
|
||||
TerminalSettings terminalSettings{};
|
||||
|
||||
@@ -189,17 +206,12 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
// use the profile name
|
||||
terminalSettings.StartingTitle(_tabTitle ? _tabTitle.value() : _name);
|
||||
|
||||
if (_suppressApplicationTitle)
|
||||
{
|
||||
terminalSettings.SuppressApplicationTitle(_suppressApplicationTitle);
|
||||
}
|
||||
|
||||
if (_schemeName)
|
||||
{
|
||||
const auto found = schemes.find(_schemeName.value());
|
||||
if (found != schemes.end())
|
||||
const ColorScheme* const matchingScheme = _FindScheme(schemes, _schemeName.value());
|
||||
if (matchingScheme)
|
||||
{
|
||||
found->second.ApplyScheme(terminalSettings);
|
||||
matchingScheme->ApplyScheme(terminalSettings);
|
||||
}
|
||||
}
|
||||
if (_defaultForeground)
|
||||
@@ -210,10 +222,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
{
|
||||
terminalSettings.DefaultBackground(_defaultBackground.value());
|
||||
}
|
||||
if (_selectionBackground)
|
||||
{
|
||||
terminalSettings.SelectionBackground(_selectionBackground.value());
|
||||
}
|
||||
|
||||
if (_scrollbarState)
|
||||
{
|
||||
@@ -221,9 +229,9 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
terminalSettings.ScrollState(result);
|
||||
}
|
||||
|
||||
if (HasBackgroundImage())
|
||||
if (_backgroundImage)
|
||||
{
|
||||
terminalSettings.BackgroundImage(GetExpandedBackgroundImagePath().c_str());
|
||||
terminalSettings.BackgroundImage(_backgroundImage.value());
|
||||
}
|
||||
|
||||
if (_backgroundImageOpacity)
|
||||
@@ -271,10 +279,6 @@ Json::Value Profile::ToJson() const
|
||||
{
|
||||
root[JsonKey(BackgroundKey)] = Utils::ColorToHexString(_defaultBackground.value());
|
||||
}
|
||||
if (_selectionBackground)
|
||||
{
|
||||
root[JsonKey(SelectionBackgroundKey)] = Utils::ColorToHexString(_selectionBackground.value());
|
||||
}
|
||||
if (_schemeName)
|
||||
{
|
||||
const auto scheme = winrt::to_string(_schemeName.value());
|
||||
@@ -329,11 +333,6 @@ Json::Value Profile::ToJson() const
|
||||
root[JsonKey(TabTitleKey)] = winrt::to_string(_tabTitle.value());
|
||||
}
|
||||
|
||||
if (_suppressApplicationTitle)
|
||||
{
|
||||
root[JsonKey(SuppressApplicationTitleKey)] = _suppressApplicationTitle;
|
||||
}
|
||||
|
||||
if (_startingDirectory)
|
||||
{
|
||||
root[JsonKey(StartingDirectoryKey)] = winrt::to_string(_startingDirectory.value());
|
||||
@@ -601,8 +600,6 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
|
||||
JsonUtils::GetOptionalColor(json, BackgroundKey, _defaultBackground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, SelectionBackgroundKey, _selectionBackground);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKey, _schemeName);
|
||||
// TODO:GH#1069 deprecate old settings key
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKeyOld, _schemeName);
|
||||
@@ -681,11 +678,6 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
auto useAcrylic{ json[JsonKey(UseAcrylicKey)] };
|
||||
_useAcrylic = useAcrylic.asBool();
|
||||
}
|
||||
if (json.isMember(JsonKey(SuppressApplicationTitleKey)))
|
||||
{
|
||||
auto suppressApplicationTitle{ json[JsonKey(SuppressApplicationTitleKey)] };
|
||||
_suppressApplicationTitle = suppressApplicationTitle.asBool();
|
||||
}
|
||||
if (json.isMember(JsonKey(CloseOnExitKey)))
|
||||
{
|
||||
auto closeOnExit{ json[JsonKey(CloseOnExitKey)] };
|
||||
@@ -722,11 +714,6 @@ void Profile::SetColorScheme(std::optional<std::wstring> schemeName) noexcept
|
||||
_schemeName = std::move(schemeName);
|
||||
}
|
||||
|
||||
std::optional<std::wstring>& Profile::GetSchemeName() noexcept
|
||||
{
|
||||
return _schemeName;
|
||||
}
|
||||
|
||||
void Profile::SetAcrylicOpacity(double opacity) noexcept
|
||||
{
|
||||
_acrylicTransparency = opacity;
|
||||
@@ -762,11 +749,6 @@ void Profile::SetDefaultBackground(COLORREF defaultBackground) noexcept
|
||||
_defaultBackground = defaultBackground;
|
||||
}
|
||||
|
||||
void Profile::SetSelectionBackground(COLORREF selectionBackground) noexcept
|
||||
{
|
||||
_selectionBackground = selectionBackground;
|
||||
}
|
||||
|
||||
void Profile::SetCloseOnExit(bool defaultClose) noexcept
|
||||
{
|
||||
_closeOnExit = defaultClose;
|
||||
@@ -782,11 +764,6 @@ bool Profile::HasIcon() const noexcept
|
||||
return _icon.has_value() && !_icon.value().empty();
|
||||
}
|
||||
|
||||
bool Profile::HasBackgroundImage() const noexcept
|
||||
{
|
||||
return _backgroundImage.has_value() && !_backgroundImage.value().empty();
|
||||
}
|
||||
|
||||
// Method Description
|
||||
// - Sets this profile's tab title.
|
||||
// Arguments:
|
||||
@@ -796,15 +773,6 @@ void Profile::SetTabTitle(std::wstring tabTitle) noexcept
|
||||
_tabTitle = std::move(tabTitle);
|
||||
}
|
||||
|
||||
// Method Description
|
||||
// - Sets if the application title will be suppressed in this profile.
|
||||
// Arguments:
|
||||
// - suppressApplicationTitle: boolean
|
||||
void Profile::SetSuppressApplicationTitle(bool suppressApplicationTitle) noexcept
|
||||
{
|
||||
_suppressApplicationTitle = suppressApplicationTitle;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets this profile's icon path.
|
||||
// Arguments:
|
||||
@@ -831,23 +799,6 @@ winrt::hstring Profile::GetExpandedIconPath() const
|
||||
return envExpandedPath;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns this profile's background image path, if one is set, expanding
|
||||
// any environment variables in the path, if there are any.
|
||||
// Return Value:
|
||||
// - This profile's expanded background image path / the empty string.
|
||||
winrt::hstring Profile::GetExpandedBackgroundImagePath() const
|
||||
{
|
||||
winrt::hstring result{};
|
||||
|
||||
if (HasBackgroundImage())
|
||||
{
|
||||
result = wil::ExpandEnvironmentStringsW<std::wstring>(_backgroundImage.value().data());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the name of this profile.
|
||||
// Arguments:
|
||||
@@ -859,11 +810,6 @@ std::wstring_view Profile::GetName() const noexcept
|
||||
return _name;
|
||||
}
|
||||
|
||||
bool Profile::GetSuppressApplicationTitle() const noexcept
|
||||
{
|
||||
return _suppressApplicationTitle;
|
||||
}
|
||||
|
||||
bool Profile::HasConnectionType() const noexcept
|
||||
{
|
||||
return _connectionType.has_value();
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
~Profile();
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::TerminalSettings CreateTerminalSettings(const std::unordered_map<std::wstring, ColorScheme>& schemes) const;
|
||||
winrt::Microsoft::Terminal::Settings::TerminalSettings CreateTerminalSettings(const std::vector<::TerminalApp::ColorScheme>& schemes) const;
|
||||
|
||||
Json::Value ToJson() const;
|
||||
Json::Value DiffToJson(const Profile& other) const;
|
||||
@@ -65,9 +65,7 @@ public:
|
||||
|
||||
void SetFontFace(std::wstring fontFace) noexcept;
|
||||
void SetColorScheme(std::optional<std::wstring> schemeName) noexcept;
|
||||
std::optional<std::wstring>& GetSchemeName() noexcept;
|
||||
void SetTabTitle(std::wstring tabTitle) noexcept;
|
||||
void SetSuppressApplicationTitle(bool suppressApplicationTitle) noexcept;
|
||||
void SetAcrylicOpacity(double opacity) noexcept;
|
||||
void SetCommandline(std::wstring cmdline) noexcept;
|
||||
void SetStartingDirectory(std::wstring startingDirectory) noexcept;
|
||||
@@ -75,7 +73,6 @@ public:
|
||||
void SetUseAcrylic(bool useAcrylic) noexcept;
|
||||
void SetDefaultForeground(COLORREF defaultForeground) noexcept;
|
||||
void SetDefaultBackground(COLORREF defaultBackground) noexcept;
|
||||
void SetSelectionBackground(COLORREF selectionBackground) noexcept;
|
||||
void SetCloseOnExit(bool defaultClose) noexcept;
|
||||
void SetConnectionType(GUID connectionType) noexcept;
|
||||
|
||||
@@ -83,11 +80,7 @@ public:
|
||||
winrt::hstring GetExpandedIconPath() const;
|
||||
void SetIconPath(std::wstring_view path);
|
||||
|
||||
bool HasBackgroundImage() const noexcept;
|
||||
winrt::hstring GetExpandedBackgroundImagePath() const;
|
||||
|
||||
bool GetCloseOnExit() const noexcept;
|
||||
bool GetSuppressApplicationTitle() const noexcept;
|
||||
bool IsHidden() const noexcept;
|
||||
|
||||
void GenerateGuidIfNecessary() noexcept;
|
||||
@@ -121,10 +114,8 @@ private:
|
||||
|
||||
std::optional<uint32_t> _defaultForeground;
|
||||
std::optional<uint32_t> _defaultBackground;
|
||||
std::optional<uint32_t> _selectionBackground;
|
||||
std::array<uint32_t, COLOR_TABLE_SIZE> _colorTable;
|
||||
std::optional<std::wstring> _tabTitle;
|
||||
bool _suppressApplicationTitle;
|
||||
int32_t _historySize;
|
||||
bool _snapOnInput;
|
||||
uint32_t _cursorColor;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -127,10 +127,6 @@
|
||||
</data>
|
||||
<data name="DuplicateProfileText" xml:space="preserve">
|
||||
<value>Found multiple profiles with the same GUID in your settings file - ignoring duplicates. Make sure each profile's GUID is unique.
|
||||
</value>
|
||||
</data>
|
||||
<data name="UnknownColorSchemeText" xml:space="preserve">
|
||||
<value>Found a profile with an invalid "colorScheme". Defaulting that profile to the default colors. Make sure that when setting a "colorScheme", the value matches the "name" of a color scheme in the "schemes" list.
|
||||
</value>
|
||||
</data>
|
||||
<data name="NoProfilesText" xml:space="preserve">
|
||||
@@ -157,7 +153,7 @@ Temporarily using the Windows Terminal default settings.
|
||||
<value>Encountered errors while loading user settings</value>
|
||||
</data>
|
||||
<data name="Ok" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
<value>Ok</value>
|
||||
</data>
|
||||
<data name="ReloadJsonParseErrorTitle" xml:space="preserve">
|
||||
<value>Failed to reload settings</value>
|
||||
@@ -210,4 +206,4 @@ Temporarily using the Windows Terminal default settings.
|
||||
<data name="CloseWindowWarningTitle" xml:space="preserve">
|
||||
<value>Do you want to close all tabs?</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
@@ -17,7 +17,7 @@ ScopedResourceLoader::ScopedResourceLoader(const std::wstring_view resourceLocat
|
||||
// - Gets the resource map associated with the scoped resource subcompartment.
|
||||
// Return Value:
|
||||
// - the resource map associated with the scoped resource subcompartment.
|
||||
ResourceMap ScopedResourceLoader::GetResourceMap() const
|
||||
ResourceMap ScopedResourceLoader::GetResourceMap()
|
||||
{
|
||||
return _resourceMap;
|
||||
}
|
||||
@@ -31,18 +31,7 @@ ResourceMap ScopedResourceLoader::GetResourceMap() const
|
||||
// - resourceName: the key up by which to look the resource
|
||||
// Return Value:
|
||||
// - The final localized string for the given key.
|
||||
winrt::hstring ScopedResourceLoader::GetLocalizedString(const std::wstring_view resourceName) const
|
||||
winrt::hstring ScopedResourceLoader::GetLocalizedString(const std::wstring_view resourceName)
|
||||
{
|
||||
return _resourceMap.GetValue(resourceName, _resourceContext).ValueAsString();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns whether this resource loader can find a resource with the given key.
|
||||
// Arguments:
|
||||
// - resourceName: the key up by which to look the resource
|
||||
// Return Value:
|
||||
// - A boolean indicating whether the resource was found
|
||||
bool ScopedResourceLoader::HasResourceWithName(const std::wstring_view resourceName) const
|
||||
{
|
||||
return _resourceMap.HasKey(resourceName);
|
||||
}
|
||||
@@ -7,9 +7,8 @@ class ScopedResourceLoader
|
||||
{
|
||||
public:
|
||||
ScopedResourceLoader(const std::wstring_view resourceLocatorBase);
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceMap GetResourceMap() const;
|
||||
winrt::hstring GetLocalizedString(const std::wstring_view resourceName) const;
|
||||
bool HasResourceWithName(const std::wstring_view resourceName) const;
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceMap GetResourceMap();
|
||||
winrt::hstring GetLocalizedString(const std::wstring_view resourceName);
|
||||
|
||||
private:
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceMap _resourceMap;
|
||||
@@ -10,10 +10,7 @@ using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
}
|
||||
static const int TabViewFontSize = 12;
|
||||
|
||||
Tab::Tab(const GUID& profile, const TermControl& control)
|
||||
{
|
||||
@@ -23,14 +20,13 @@ Tab::Tab(const GUID& profile, const TermControl& control)
|
||||
_closedHandlers();
|
||||
});
|
||||
|
||||
_activePane = _rootPane;
|
||||
|
||||
_MakeTabViewItem();
|
||||
}
|
||||
|
||||
void Tab::_MakeTabViewItem()
|
||||
{
|
||||
_tabViewItem = ::winrt::MUX::Controls::TabViewItem{};
|
||||
_tabViewItem = ::winrt::Microsoft::UI::Xaml::Controls::TabViewItem{};
|
||||
_tabViewItem.FontSize(TabViewFontSize);
|
||||
}
|
||||
|
||||
UIElement Tab::GetRootElement()
|
||||
@@ -49,12 +45,12 @@ UIElement Tab::GetRootElement()
|
||||
// Return Value:
|
||||
// - nullptr if no children were marked `_lastFocused`, else the TermControl
|
||||
// that was last focused.
|
||||
TermControl Tab::GetActiveTerminalControl() const
|
||||
TermControl Tab::GetFocusedTerminalControl()
|
||||
{
|
||||
return _activePane->GetTerminalControl();
|
||||
return _rootPane->GetFocusedTerminalControl();
|
||||
}
|
||||
|
||||
winrt::MUX::Controls::TabViewItem Tab::GetTabViewItem()
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewItem Tab::GetTabViewItem()
|
||||
{
|
||||
return _tabViewItem;
|
||||
}
|
||||
@@ -101,20 +97,7 @@ void Tab::SetFocused(const bool focused)
|
||||
// focused, else the GUID of the profile of the last control to be focused
|
||||
std::optional<GUID> Tab::GetFocusedProfile() const noexcept
|
||||
{
|
||||
return _activePane->GetFocusedProfile();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl object
|
||||
// Arguments:
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::BindEventHandlers(const TermControl& control) noexcept
|
||||
{
|
||||
_AttachEventHandlersToPane(_rootPane);
|
||||
_AttachEventHandlersToControl(control);
|
||||
return _rootPane->GetFocusedProfile();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -139,13 +122,27 @@ void Tab::_Focus()
|
||||
{
|
||||
_focused = true;
|
||||
|
||||
auto lastFocusedControl = GetActiveTerminalControl();
|
||||
auto lastFocusedControl = _rootPane->GetFocusedTerminalControl();
|
||||
if (lastFocusedControl)
|
||||
{
|
||||
lastFocusedControl.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the focus state of this tab's tree of panes. If one of the controls
|
||||
// under this tab is focused, then it will be marked as the last focused. If
|
||||
// there are no focused panes, then there will not be a last focused control
|
||||
// when this returns.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::UpdateFocus()
|
||||
{
|
||||
_rootPane->UpdateFocus();
|
||||
}
|
||||
|
||||
void Tab::UpdateIcon(const winrt::hstring iconPath)
|
||||
{
|
||||
// Don't reload our icon if it hasn't changed.
|
||||
@@ -156,13 +153,8 @@ void Tab::UpdateIcon(const winrt::hstring iconPath)
|
||||
|
||||
_lastIconPath = iconPath;
|
||||
|
||||
std::weak_ptr<Tab> weakThis{ shared_from_this() };
|
||||
|
||||
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
|
||||
if (auto tab{ weakThis.lock() })
|
||||
{
|
||||
tab->_tabViewItem.IconSource(GetColoredIcon<winrt::MUX::Controls::IconSource>(tab->_lastIconPath));
|
||||
}
|
||||
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() {
|
||||
_tabViewItem.Icon(GetColoredIcon(_lastIconPath));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,9 +165,9 @@ void Tab::UpdateIcon(const winrt::hstring iconPath)
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the title string of the last focused terminal control in our tree.
|
||||
winrt::hstring Tab::GetActiveTitle() const
|
||||
winrt::hstring Tab::GetFocusedTitle() const
|
||||
{
|
||||
const auto lastFocusedControl = GetActiveTerminalControl();
|
||||
const auto lastFocusedControl = _rootPane->GetFocusedTerminalControl();
|
||||
return lastFocusedControl ? lastFocusedControl.Title() : L"";
|
||||
}
|
||||
|
||||
@@ -189,13 +181,8 @@ void Tab::SetTabText(const winrt::hstring& text)
|
||||
{
|
||||
// Copy the hstring, so we don't capture a dead reference
|
||||
winrt::hstring textCopy{ text };
|
||||
std::weak_ptr<Tab> weakThis{ shared_from_this() };
|
||||
|
||||
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [text = std::move(textCopy), weakThis]() {
|
||||
if (auto tab{ weakThis.lock() })
|
||||
{
|
||||
tab->_tabViewItem.Header(winrt::box_value(text));
|
||||
}
|
||||
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [text = std::move(textCopy), this]() {
|
||||
_tabViewItem.Header(winrt::box_value(text));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -209,7 +196,7 @@ void Tab::SetTabText(const winrt::hstring& text)
|
||||
// - <none>
|
||||
void Tab::Scroll(const int delta)
|
||||
{
|
||||
auto control = GetActiveTerminalControl();
|
||||
auto control = GetFocusedTerminalControl();
|
||||
control.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [control, delta]() {
|
||||
const auto currentOffset = control.GetScrollOffset();
|
||||
control.KeyboardScrollViewport(currentOffset + delta);
|
||||
@@ -224,7 +211,7 @@ void Tab::Scroll(const int delta)
|
||||
// - True if the focused pane can be split. False otherwise.
|
||||
bool Tab::CanSplitPane(Pane::SplitState splitType)
|
||||
{
|
||||
return _activePane->CanSplit(splitType);
|
||||
return _rootPane->CanSplit(splitType);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -238,14 +225,7 @@ bool Tab::CanSplitPane(Pane::SplitState splitType)
|
||||
// - <none>
|
||||
void Tab::SplitPane(Pane::SplitState splitType, const GUID& profile, TermControl& control)
|
||||
{
|
||||
auto [first, second] = _activePane->Split(splitType, profile, control);
|
||||
|
||||
_AttachEventHandlersToControl(control);
|
||||
|
||||
// Add a event handlers to the new panes' GotFocus event. When the pane
|
||||
// gains focus, we'll mark it as the new active pane.
|
||||
_AttachEventHandlersToPane(first);
|
||||
_AttachEventHandlersToPane(second);
|
||||
_rootPane->Split(splitType, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -257,8 +237,6 @@ void Tab::SplitPane(Pane::SplitState splitType, const GUID& profile, TermControl
|
||||
// - <none>
|
||||
void Tab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propogate
|
||||
// throughout the entire tree.
|
||||
_rootPane->ResizeContent(newSize);
|
||||
}
|
||||
|
||||
@@ -271,8 +249,6 @@ void Tab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
|
||||
// - <none>
|
||||
void Tab::ResizePane(const winrt::TerminalApp::Direction& direction)
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propogate
|
||||
// throughout the entire tree.
|
||||
_rootPane->ResizePane(direction);
|
||||
}
|
||||
|
||||
@@ -285,8 +261,6 @@ void Tab::ResizePane(const winrt::TerminalApp::Direction& direction)
|
||||
// - <none>
|
||||
void Tab::NavigateFocus(const winrt::TerminalApp::Direction& direction)
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propogate
|
||||
// throughout the entire tree.
|
||||
_rootPane->NavigateFocus(direction);
|
||||
}
|
||||
|
||||
@@ -300,65 +274,8 @@ void Tab::NavigateFocus(const winrt::TerminalApp::Direction& direction)
|
||||
// - <none>
|
||||
void Tab::ClosePane()
|
||||
{
|
||||
_activePane->Close();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Register any event handlers that we may need with the given TermControl.
|
||||
// This should be called on each and every TermControl that we add to the tree
|
||||
// of Panes in this tab. We'll add events too:
|
||||
// * notify us when the control's title changed, so we can update our own
|
||||
// title (if necessary)
|
||||
// Arguments:
|
||||
// - control: the TermControl to add events to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_AttachEventHandlersToControl(const TermControl& control)
|
||||
{
|
||||
std::weak_ptr<Tab> weakThis{ shared_from_this() };
|
||||
|
||||
control.TitleChanged([weakThis](auto newTitle) {
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.lock() })
|
||||
{
|
||||
// The title of the control changed, but not necessarily the title of the tab.
|
||||
// Set the tab's text to the active panes' text.
|
||||
tab->SetTabText(tab->GetActiveTitle());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Add an event handler to this pane's GotFocus event. When that pane gains
|
||||
// focus, we'll mark it as the new active pane. We'll also query the title of
|
||||
// that pane when it's focused to set our own text, and finally, we'll trigger
|
||||
// our own ActivePaneChanged event.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
std::weak_ptr<Tab> weakThis{ shared_from_this() };
|
||||
|
||||
pane->GotFocus([weakThis](std::shared_ptr<Pane> sender) {
|
||||
// Do nothing if the Tab's lifetime is expired or pane isn't new.
|
||||
auto tab{ weakThis.lock() };
|
||||
if (tab && sender != tab->_activePane)
|
||||
{
|
||||
// Clear the active state of the entire tree, and mark only the sender as active.
|
||||
tab->_rootPane->ClearActive();
|
||||
tab->_activePane = sender;
|
||||
tab->_activePane->SetActive();
|
||||
|
||||
// Update our own title text to match the newly-active pane.
|
||||
tab->SetTabText(tab->GetActiveTitle());
|
||||
|
||||
// Raise our own ActivePaneChanged event.
|
||||
tab->_ActivePaneChangedHandlers();
|
||||
}
|
||||
});
|
||||
auto focused = _rootPane->GetFocusedPane();
|
||||
focused->Close();
|
||||
}
|
||||
|
||||
DEFINE_EVENT(Tab, Closed, _closedHandlers, ConnectionClosedEventArgs);
|
||||
DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
|
||||
@@ -5,17 +5,14 @@
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
#include "Pane.h"
|
||||
|
||||
class Tab : public std::enable_shared_from_this<Tab>
|
||||
class Tab
|
||||
{
|
||||
public:
|
||||
Tab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
// Called after construction to setup events with weak_ptr
|
||||
void BindEventHandlers(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control) noexcept;
|
||||
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewItem GetTabViewItem();
|
||||
winrt::Windows::UI::Xaml::UIElement GetRootElement();
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl GetActiveTerminalControl() const;
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl GetFocusedTerminalControl();
|
||||
std::optional<GUID> GetFocusedProfile() const noexcept;
|
||||
|
||||
bool IsFocused() const noexcept;
|
||||
@@ -26,6 +23,7 @@ public:
|
||||
bool CanSplitPane(Pane::SplitState splitType);
|
||||
void SplitPane(Pane::SplitState splitType, const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
void UpdateFocus();
|
||||
void UpdateIcon(const winrt::hstring iconPath);
|
||||
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
@@ -33,17 +31,15 @@ public:
|
||||
void NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
|
||||
winrt::hstring GetActiveTitle() const;
|
||||
winrt::hstring GetFocusedTitle() const;
|
||||
void SetTabText(const winrt::hstring& text);
|
||||
|
||||
void ClosePane();
|
||||
|
||||
DECLARE_EVENT(Closed, _closedHandlers, winrt::Microsoft::Terminal::TerminalControl::ConnectionClosedEventArgs);
|
||||
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pane> _rootPane{ nullptr };
|
||||
std::shared_ptr<Pane> _activePane{ nullptr };
|
||||
winrt::hstring _lastIconPath{};
|
||||
|
||||
bool _focused{ false };
|
||||
@@ -51,7 +47,4 @@ private:
|
||||
|
||||
void _MakeTabViewItem();
|
||||
void _Focus();
|
||||
|
||||
void _AttachEventHandlersToControl(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "TabRowControl.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
|
||||