Compare commits
24 Commits
dev/miniks
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f26774296 | ||
|
|
7855b804e9 | ||
|
|
327ddd7023 | ||
|
|
ff846d15a2 | ||
|
|
d4614bf041 | ||
|
|
847d878c5e | ||
|
|
4204d2535c | ||
|
|
e46ba65665 | ||
|
|
9767abd3f3 | ||
|
|
e1421ced89 | ||
|
|
361d4f559a | ||
|
|
9d34507a67 | ||
|
|
74feda108f | ||
|
|
ba91ae4eb8 | ||
|
|
420d7142fb | ||
|
|
267bb289a7 | ||
|
|
a064931f37 | ||
|
|
f77d47648b | ||
|
|
1a7d934ec9 | ||
|
|
6c869ebb26 | ||
|
|
88e843d474 | ||
|
|
7886f16714 | ||
|
|
7967e1740c | ||
|
|
744bc2190c |
40
.github/linters/.markdown-lint.yml
vendored
@@ -1,40 +0,0 @@
|
||||
---
|
||||
###########################
|
||||
###########################
|
||||
## Markdown Linter rules ##
|
||||
###########################
|
||||
###########################
|
||||
|
||||
# Linter rules doc:
|
||||
# - https://github.com/DavidAnson/markdownlint
|
||||
#
|
||||
# Note:
|
||||
# To comment out a single error:
|
||||
# <!-- markdownlint-disable -->
|
||||
# any violations you want
|
||||
# <!-- markdownlint-restore -->
|
||||
#
|
||||
# To run the linter locally:
|
||||
# 1. install the npm package:
|
||||
# `npm install -g markdownlint-cli`
|
||||
# 2. Then run it in the root of the repo with
|
||||
# `markdownlint -c .github\linters\.markdown-lint.yml ./*.md`
|
||||
|
||||
###############
|
||||
# Rules by id #
|
||||
###############
|
||||
MD004: false # Unordered list style
|
||||
MD007:
|
||||
indent: 2 # Unordered list indentation
|
||||
MD013:
|
||||
line_length: 400 # Line length 80 is far to short
|
||||
MD026:
|
||||
punctuation: ".,;:!。,;:" # List of not allowed
|
||||
MD029: false # Ordered list item prefix
|
||||
MD033: false # Allow inline HTML
|
||||
MD036: false # Emphasis used instead of a heading
|
||||
|
||||
#################
|
||||
# Rules by tags #
|
||||
#################
|
||||
blank_lines: false # Error on blank lines
|
||||
51
.github/workflows/linter.yml
vendored
@@ -1,51 +0,0 @@
|
||||
---
|
||||
###########################
|
||||
###########################
|
||||
## Linter GitHub Actions ##
|
||||
###########################
|
||||
###########################
|
||||
name: Lint Code Base
|
||||
|
||||
#
|
||||
# Documentation:
|
||||
# https://help.github.com/en/articles/workflow-syntax-for-github-actions
|
||||
#
|
||||
|
||||
#############################
|
||||
# Start the job on all push #
|
||||
#############################
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
###############
|
||||
# Set the Job #
|
||||
###############
|
||||
jobs:
|
||||
build:
|
||||
# Name the Job
|
||||
name: Lint Code Base
|
||||
# Set the agent to run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
##################
|
||||
# Load all steps #
|
||||
##################
|
||||
steps:
|
||||
##########################
|
||||
# Checkout the code base #
|
||||
##########################
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
################################
|
||||
# Run Linter against code base #
|
||||
################################
|
||||
- name: Lint Code Base
|
||||
uses: github/super-linter@v3.10.0
|
||||
env:
|
||||
VALIDATE_ALL_CODEBASE: false
|
||||
DEFAULT_BRANCH: master
|
||||
MARKDOWN_CONFIG_FILE: .markdown-lint.yml
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VALIDATE_EDITORCONFIG: false
|
||||
@@ -140,13 +140,6 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and you
|
||||
1. Create & push a feature branch
|
||||
1. Create a [Draft Pull Request (PR)](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
|
||||
1. Work on your changes
|
||||
1. Build and see if it works. Consult [How to build OpenConsole](./doc/building.md) if you have problems.
|
||||
|
||||
### Testing
|
||||
|
||||
Testing is a key component in the development workflow. Both Windows Terminal and Windows Console use TAEF(the Test Authoring and Execution Framework) as the main framework for testing.
|
||||
|
||||
If your changes affect existing test cases, or you're working on brand new features and also the accompanying test cases, see [TAEF](./doc/TAEF.md) for more information about how to validate your work locally.
|
||||
|
||||
### Code Review
|
||||
|
||||
|
||||
30
NOTICE.md
@@ -48,6 +48,36 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## telnetpp
|
||||
|
||||
**Source**: https://github.com/KazDragon/telnetpp
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Matthew Chaplain a.k.a KazDragon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## chromium/base/numerics
|
||||
|
||||
**Source**: https://github.com/chromium/chromium/tree/master/base/numerics
|
||||
|
||||
109
OpenConsole.sln
@@ -87,8 +87,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Feature", "src\h
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
|
||||
{FC802440-AD6A-4919-8F2C-7701F2B38D79} = {FC802440-AD6A-4919-8F2C-7701F2B38D79}
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}
|
||||
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser.UnitTests", "src\terminal\parser\ut_parser\Parser.UnitTests.vcxproj", "{12144E07-FE63-4D33-9231-748B8D8C3792}"
|
||||
@@ -180,7 +180,6 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia\TerminalApp\dll\TerminalApp.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
EndProjectSection
|
||||
@@ -226,21 +225,16 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_TerminalApp", "src\cascadia\ut_app\TerminalApp.UnitTests.vcxproj", "{CA5CAD1A-9333-4D05-B12A-1905CBF112F9}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererUia", "src\renderer\uia\lib\uia.vcxproj", "{48D21369-3D7B-4431-9967-24E81292CF63}"
|
||||
@@ -256,8 +250,6 @@ EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia\LocalTests_TerminalApp\TestHostApp\TestHostApp.vcxproj", "{A021EDFF-45C8-4DC2-BEF7-36E1B3B8CFE8}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}"
|
||||
@@ -315,24 +307,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "s
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model", "src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj", "{CA5CAD1A-082C-4476-9F33-94B339494076}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", "src\cascadia\LocalTests_SettingsModel\SettingsModel.LocalTests.vcxproj", "{CA5CAD1A-9B68-456A-B13E-C8218070DC42}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|Any CPU = AuditMode|Any CPU
|
||||
@@ -2012,84 +1986,6 @@ Global
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x64.Build.0 = Release|x64
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.Build.0 = Release|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x64.Build.0 = Release|x64
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076}.Release|x86.Build.0 = Release|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x64.Build.0 = Debug|x64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.ActiveCfg = Release|x64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2170,9 +2066,6 @@ Global
|
||||
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
|
||||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
||||
221
README.md
@@ -7,25 +7,21 @@ This repository contains the source code for:
|
||||
* 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
|
||||
* [Sample projects](https://github.com/Microsoft/Terminal/tree/master/samples) that show how to consume the Windows Console APIs
|
||||
|
||||
Related repositories include:
|
||||
|
||||
* [Windows Terminal Documentation](https://docs.microsoft.com/windows/terminal)
|
||||
([Repo: Contribute to the docs](https://github.com/MicrosoftDocs/terminal))
|
||||
* [Windows Terminal Documentation](https://docs.microsoft.com/windows/terminal) ([Repo: Contribute to the docs](https://github.com/MicrosoftDocs/terminal))
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
* [Cascadia Code Font](https://github.com/Microsoft/Cascadia-Code)
|
||||
|
||||
## Installing and running Windows Terminal
|
||||
|
||||
> 🔴 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
|
||||
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
|
||||
|
||||
### Microsoft Store [Recommended]
|
||||
|
||||
Install the [Windows Terminal from the Microsoft Store][store-install-link].
|
||||
This allows you to always be on the latest version when we release new builds
|
||||
with automatic upgrades.
|
||||
Install the [Windows Terminal from the Microsoft Store][store-install-link]. This allows you to always be on the latest version when we release new builds with automatic upgrades.
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
@@ -33,21 +29,16 @@ This is our preferred method.
|
||||
|
||||
#### Via GitHub
|
||||
|
||||
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).
|
||||
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:
|
||||
> ⚠ Note: If you install Terminal manually:
|
||||
>
|
||||
> * 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!
|
||||
> * Be sure to install the [Desktop Bridge VC++ v14 Redistributable Package](https://www.microsoft.com/en-us/download/details.aspx?id=53175) otherwise Terminal may not install and/or run and may crash at startup
|
||||
> * Terminal will not auto-update when new builds are released so you will need to regularly install the latest Terminal release to receive all the latest fixes and improvements!
|
||||
|
||||
#### Via Windows Package Manager CLI (aka winget)
|
||||
|
||||
[winget](https://github.com/microsoft/winget-cli) users can download and install
|
||||
the latest Terminal release by installing the `Microsoft.WindowsTerminal`
|
||||
package:
|
||||
[winget](https://github.com/microsoft/winget-cli) users can download and install the latest Terminal release by installing the `Microsoft.WindowsTerminal` package:
|
||||
|
||||
```powershell
|
||||
winget install --id=Microsoft.WindowsTerminal -e
|
||||
@@ -55,8 +46,7 @@ winget install --id=Microsoft.WindowsTerminal -e
|
||||
|
||||
#### Via Chocolatey (unofficial)
|
||||
|
||||
[Chocolatey](https://chocolatey.org) users can download and install the latest
|
||||
Terminal release by installing the `microsoft-windows-terminal` package:
|
||||
[Chocolatey](https://chocolatey.org) users can download and install the latest Terminal release by installing the `microsoft-windows-terminal` package:
|
||||
|
||||
```powershell
|
||||
choco install microsoft-windows-terminal
|
||||
@@ -68,15 +58,11 @@ To upgrade Windows Terminal using Chocolatey, run the following:
|
||||
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 [Windows Terminal package page](https://chocolatey.org/packages/microsoft-windows-terminal) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
|
||||
|
||||
#### Via Scoop (unofficial)
|
||||
|
||||
[Scoop](https://scoop.sh) users can download and install the latest Terminal
|
||||
release by installing the `windows-terminal` package:
|
||||
[Scoop](https://scoop.sh) users can download and install the latest Terminal release by installing the `windows-terminal` package:
|
||||
|
||||
```powershell
|
||||
scoop install windows-terminal
|
||||
@@ -88,123 +74,70 @@ To update Windows Terminal using Scoop, run the following:
|
||||
scoop update windows-terminal
|
||||
```
|
||||
|
||||
If you have any issues when installing/updating the package, please search for
|
||||
or report the same on the [issues
|
||||
page](https://github.com/lukesampson/scoop-extras/issues) of Scoop Extras bucket
|
||||
repository.
|
||||
If you have any issues when installing/updating the package, please search for or report the same on the [issues page](https://github.com/lukesampson/scoop-extras/issues) of Scoop Extras bucket repository.
|
||||
|
||||
---
|
||||
|
||||
## Windows Terminal 2.0 Roadmap
|
||||
|
||||
The plan for delivering Windows Terminal 2.0 [is described
|
||||
here](/doc/terminal-v2-roadmap.md) and will be updated as the project proceeds.
|
||||
The plan for delivering Windows Terminal 2.0 [is described here](/doc/terminal-v2-roadmap.md) and will be updated as the project proceeds.
|
||||
|
||||
## Project Build Status
|
||||
|
||||
Project|Build Status
|
||||
---|---
|
||||
Terminal|[](https://dev.azure.com/ms/Terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
Terminal|[](https://dev.azure.com/ms/Terminal/_build?definitionId=136)
|
||||
ColorTool|
|
||||
|
||||
---
|
||||
|
||||
## Terminal & Console Overview
|
||||
|
||||
Please take a few minutes to review the overview below before diving into the
|
||||
code:
|
||||
Please take a few minutes to review the overview below before diving into the code:
|
||||
|
||||
### 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.
|
||||
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, `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 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.
|
||||
|
||||
Since taking ownership of the Windows command-line in 2014, the team added
|
||||
several new features to the Console, including background transparency,
|
||||
line-based selection, support for [ANSI / Virtual Terminal
|
||||
sequences](https://en.wikipedia.org/wiki/ANSI_escape_code), [24-bit
|
||||
color](https://devblogs.microsoft.com/commandline/24-bit-color-in-the-windows-console/),
|
||||
a [Pseudoconsole
|
||||
("ConPTY")](https://devblogs.microsoft.com/commandline/windows-command-line-introducing-the-windows-pseudo-console-conpty/),
|
||||
and more.
|
||||
Since 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.
|
||||
|
||||
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.
|
||||
However, because Windows Console's primary goal is to maintain backward compatibility, we have been unable to add many of the features the community (and the team) have been wanting for the last several years including tabs, unicode text, and emoji.
|
||||
|
||||
These limitations led us to create the new Windows Terminal.
|
||||
|
||||
> 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.
|
||||
> 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
|
||||
|
||||
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).
|
||||
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 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.
|
||||
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.
|
||||
|
||||
### Creating the new Windows Terminal
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
For more information about Windows Terminal, you may find some of these
|
||||
resources useful and interesting:
|
||||
For more information about Windows Terminal, you may find some of these resources useful and interesting:
|
||||
|
||||
* [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)
|
||||
* [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)
|
||||
|
||||
---
|
||||
|
||||
@@ -214,50 +147,35 @@ resources useful and interesting:
|
||||
|
||||
Cause: You're launching the incorrect solution in Visual Studio.
|
||||
|
||||
Solution: Make sure you're building & deploying the `CascadiaPackage` project in
|
||||
Visual Studio.
|
||||
Solution: Make sure you're building & deploying the `CascadiaPackage` project in Visual Studio.
|
||||
|
||||
> ⚠ 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/)).
|
||||
> ⚠ 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/)).
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
All project documentation is located at aka.ms/terminal-docs. If you would like
|
||||
to contribute to the documentation, please submit a pull request on the [Windows
|
||||
Terminal Documentation repo](https://github.com/MicrosoftDocs/terminal).
|
||||
All project documentation is located at aka.ms/terminal-docs. If you would like to contribute to the documentation, please submit a pull request on the [Windows Terminal Documentation repo](https://github.com/MicrosoftDocs/terminal).
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We are excited to work alongside you, our amazing community, to build and
|
||||
enhance Windows Terminal\!
|
||||
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.
|
||||
***BEFORE you start work on a feature/fix***, please read & follow our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/CONTRIBUTING.md) to help avoid any wasted or duplicate effort.
|
||||
|
||||
## Communicating with the Team
|
||||
|
||||
The easiest way to communicate with the team is via GitHub issues.
|
||||
|
||||
Please file new issues, feature requests and suggestions, but **DO search for
|
||||
similar open/closed pre-existing issues before creating a new issue.**
|
||||
Please file new issues, feature requests and suggestions, but **DO search for similar open/closed pre-existing issues before creating a new issue.**
|
||||
|
||||
If you would like to ask a question that you feel doesn't warrant an issue
|
||||
(yet), please reach out to us via Twitter:
|
||||
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)
|
||||
* Kayla Cinnamon, Program Manager: [@cinnamon\_msft](https://twitter.com/cinnamon_msft)
|
||||
* Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett)
|
||||
* Michael Niksa, Senior Developer:
|
||||
[@michaelniksa](https://twitter.com/MichaelNiksa)
|
||||
* 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)
|
||||
* Leon Liang, Developer: [@leonmsft](https://twitter.com/leonmsft)
|
||||
@@ -267,19 +185,11 @@ If you would like to ask a question that you feel doesn't warrant an issue
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* You must be running Windows 1903 (build >= 10.0.18362.0) or later to run
|
||||
Windows Terminal
|
||||
* You must [enable Developer Mode in the Windows Settings
|
||||
app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)
|
||||
to locally install and run Windows Terminal
|
||||
* You must have the [Windows 10 1903
|
||||
SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
|
||||
installed
|
||||
* You must have at least [VS
|
||||
2019](https://visualstudio.microsoft.com/downloads/) installed
|
||||
* You must install the following Workloads via the VS Installer. Note: Opening
|
||||
the solution in VS 2019 will [prompt you to install missing components
|
||||
automatically](https://devblogs.microsoft.com/setup/configure-visual-studio-across-your-organization-with-vsconfig/):
|
||||
* 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**
|
||||
@@ -287,17 +197,13 @@ If you would like to ask a question that you feel doesn't warrant an issue
|
||||
|
||||
## Building the Code
|
||||
|
||||
This repository uses [git
|
||||
submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for some of its
|
||||
dependencies. To make sure submodules are restored or updated, be sure to run
|
||||
the following prior to building:
|
||||
This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for some of its dependencies. To make sure submodules are restored or updated, be sure to run the following prior to building:
|
||||
|
||||
```shell
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line
|
||||
using a set of convenience scripts & tools in the **/tools** directory:
|
||||
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
|
||||
|
||||
### Building in PowerShell
|
||||
|
||||
@@ -316,28 +222,19 @@ bcz
|
||||
|
||||
## Running & Debugging
|
||||
|
||||
To debug the Windows Terminal in VS, right click on `CascadiaPackage` (in the
|
||||
Solution Explorer) and go to properties. In the Debug menu, change "Application
|
||||
process" and "Background task process" to "Native Only".
|
||||
To debug the Windows Terminal in VS, right click on `CascadiaPackage` (in the Solution Explorer) and go to properties. In the Debug menu, change "Application process" and "Background task process" to "Native Only".
|
||||
|
||||
You should then be able to build & debug the Terminal project by hitting
|
||||
<kbd>F5</kbd>.
|
||||
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
|
||||
|
||||
> 👉 You will _not_ be able to launch the Terminal directly by running the
|
||||
> WindowsTerminal.exe. For more details on why, see
|
||||
> [#926](https://github.com/microsoft/terminal/issues/926),
|
||||
> [#4043](https://github.com/microsoft/terminal/issues/4043)
|
||||
> 👉 You will _not_ be able to launch the Terminal directly by running the WindowsTerminal.exe. For more details on why, see [#926](https://github.com/microsoft/terminal/issues/926), [#4043](https://github.com/microsoft/terminal/issues/4043)
|
||||
|
||||
### 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 write some new ones!)
|
||||
|
||||
This is a work in progress as we learn what we'll need to provide people in
|
||||
order to be effective contributors to our project.
|
||||
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)
|
||||
@@ -346,12 +243,10 @@ order to be effective contributors to our project.
|
||||
|
||||
---
|
||||
|
||||
## Code of Conduct
|
||||
# Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of
|
||||
Conduct][conduct-code]. For more information see the [Code of Conduct
|
||||
FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any
|
||||
additional questions or comments.
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code].
|
||||
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
|
||||
|
||||
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
|
||||
17
SUPPORT.md
@@ -1,17 +0,0 @@
|
||||
# Support
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses [GitHub issues][gh-issue] to [track bugs][gh-bug] and [feature requests][gh-feature]. Please search the existing issues before filing new issues to avoid duplicates. For new topics, file your bug or feature request as a new issue.
|
||||
|
||||
For help and questions about using this project, please look at the [docs site for Windows Terminal][docs] and our [Contributor's Guide][contributor] if you want to work on Windows Terminal.
|
||||
|
||||
## Microsoft Support Policy
|
||||
|
||||
Support for Windows Terminal is limited to the resources listed above.
|
||||
|
||||
[gh-issue]: https://github.com/microsoft/terminal/issues/new/choose
|
||||
[gh-bug]: https://github.com/microsoft/terminal/issues/new?assignees=&labels=Issue-Bug&template=bug_report.md&title=
|
||||
[gh-feature]: https://github.com/microsoft/terminal/issues/new?assignees=&labels=Issue-Feature&template=Feature_Request.md&title=
|
||||
[docs]: https://docs.microsoft.com/windows/terminal
|
||||
[contributor]: https://github.com/microsoft/terminal/blob/master/CONTRIBUTING.md
|
||||
@@ -3,7 +3,6 @@ trigger:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- feature/*
|
||||
paths:
|
||||
exclude:
|
||||
- doc/*
|
||||
@@ -14,7 +13,6 @@ pr:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- feature/*
|
||||
paths:
|
||||
exclude:
|
||||
- doc/*
|
||||
|
||||
@@ -41,8 +41,6 @@ steps:
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }}"
|
||||
clean: true
|
||||
# The build agents cannot currently support parallel build due to the
|
||||
# memory requirements of our PCH files.
|
||||
maximumCpuCount: false
|
||||
|
||||
- task: PowerShell@2
|
||||
|
||||
@@ -106,10 +106,6 @@ Try {
|
||||
Throw "Failed to find wt.exe/wtd.exe -- check the WAP packaging project"
|
||||
}
|
||||
|
||||
If ($null -eq (Get-Item "$AppxPackageRootPath\OpenConsole.exe" -EA:Ignore)) {
|
||||
Throw "Failed to find OpenConsole.exe -- check the WAP packaging project"
|
||||
}
|
||||
|
||||
} Finally {
|
||||
Remove-Item -Recurse -Force $AppxPackageRootPath
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>5</VersionMinor>
|
||||
<VersionMinor>3</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
38
doc/TAEF.md
@@ -1,30 +1,9 @@
|
||||
### TAEF Overview ###
|
||||
|
||||
### TAEF ###
|
||||
TAEF, the Test Authoring and Execution Framework, is used extensively within the Windows organization to test the operating system code in a unified manner for system, driver, and application code. As the console is a Windows OS Component, we strive to continue using the same system such that tests can be ran in a unified manner both externally to Microsoft as well as inside the official OS Build/Test system.
|
||||
|
||||
The [official documentation](https://docs.microsoft.com/en-us/windows-hardware/drivers/taef/) for TAEF describes the basic architecture, usage, and functionality of the test system. It is similar to Visual Studio test, but a bit more comprehensive and flexible.
|
||||
The [official documentation](https://msdn.microsoft.com/en-us/library/windows/hardware/hh439725\(v=vs.85\).aspx) for TAEF describes the basic architecture, usage, and functionality of the test system. It is similar to Visual Studio test, but a bit more comprehensive and flexible.
|
||||
|
||||
### Writing Tests
|
||||
|
||||
You may want to read the section [Authoring Tests in C++](https://docs.microsoft.com/en-us/windows-hardware/drivers/taef/authoring-tests-in-c--) before getting your hands dirty. Note that the quoted header name in `#include "WexTestClass.h"` might be a bit confusing. You are not required to copy TAEF headers into the project folder.
|
||||
|
||||
Use the [TAEF Verify Macros for C++](https://docs.microsoft.com/en-us/windows-hardware/drivers/taef/verify) in your test code to perform verifications.
|
||||
|
||||
### Running Tests
|
||||
|
||||
If you have Visual Studio and related C++ components installed, and you have successfully restored NuGets, you should have the TAEF test runner `te.exe` available locally as part of the `Taef.Redist.Wlk` package.
|
||||
|
||||
> Note that you cannot easily run TAEF tests directly through Visual Studio. The `Taef.Redist.Wlk` NuGet package comes with an adapter that will let you browse and execute TAEF tests inside of Visual Studio, but its performance and reliability prevent us from recommending it here.
|
||||
|
||||
In a "normal" CMD environment, `te.exe` may not be directly available. Try the following command to set up the development enviroment first:
|
||||
|
||||
```shell
|
||||
.\tools\razzle.cmd
|
||||
```
|
||||
|
||||
Then you should be able to use `%TAEF%` as an alias of the actual `te.exe`.
|
||||
|
||||
For the purposes of the OpenConsole project, you can run the tests using the `te.exe` that matches the architecture for which the test was built (x86/x64):
|
||||
For the purposes of the console project, you can run the tests using the *TE.exe* that matches the architecture for which the test was build (x86/x64) in the pattern
|
||||
|
||||
te.exe Console.Unit.Tests.dll
|
||||
|
||||
@@ -36,15 +15,6 @@ Limiting the tests to be run is also useful with:
|
||||
|
||||
Any pattern of class/method names can be specified after the */name:* flag with wildcard patterns.
|
||||
|
||||
For any further details on the functionality of the TAEF test runner, please see the [Executing Tests](https://docs.microsoft.com/en-us/windows-hardware/drivers/taef/executing-tests) section in the official documentation. Or run the embedded help with
|
||||
For any further details on the functionality of the TAEF test runner, *TE.exe*, please see the documentation above or run the embedded help with
|
||||
|
||||
te.exe /!
|
||||
|
||||
If you use PowerShell, try the following command:
|
||||
|
||||
```powershell
|
||||
Import-Module .\tools\OpenConsole.psm1
|
||||
Invoke-OpenConsoleTests
|
||||
```
|
||||
|
||||
`Invoke-OpenConsoleTests` supports a number of options, which you can enumerate by running `Invoke-OpenConsoleTests -?`.
|
||||
|
||||
@@ -67,12 +67,12 @@ To update the version of a given package, use the following snippet
|
||||
|
||||
where:
|
||||
- `$PackageName` is the name of the package, e.g. Microsoft.UI.Xaml
|
||||
- `$OldVersionNumber` is the version number currently used, e.g. 2.4.0-prerelease.200506002
|
||||
- `$NewVersionNumber` is the version number you want to migrate to, e.g. 2.5.0-prerelease.200812002
|
||||
- `$OldVersionNumber` is the version number currently used, e.g. 2.5.0-prerelease.200609001
|
||||
- `$NewVersionNumber` is the version number you want to migrate to, e.g. 2.4.200117003-prerelease
|
||||
|
||||
Example usage:
|
||||
|
||||
`git grep -z -l Microsoft.UI.Xaml | xargs -0 sed -i -e 's/2.4.0-prerelease.200506002/2.5.0-prerelease.200812002/g'`
|
||||
`git grep -z -l Microsoft.UI.Xaml | xargs -0 sed -i -e 's/2.5.0-prerelease.200609001/2.4.200117003-prerelease/g'`
|
||||
|
||||
## Using .nupkg files instead of downloaded Nuget packages
|
||||
If you want to use .nupkg files instead of the downloaded Nuget package, you can do this with the following steps:
|
||||
|
||||
@@ -1 +1,211 @@
|
||||
⚠ This document has moved to [the Customize Settings section of the Windows Terminal documentation](https://docs.microsoft.com/windows/terminal/customize-settings/global-settings).
|
||||
# Settings.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. |
|
||||
| `copyFormatting` | Optional | Boolean, Array | `true` | When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied. |
|
||||
| `largePasteWarning` | Optional | Boolean | `true` | When set to `true`, trying to paste text with more than 5 KiB of characters will display a warning asking you whether to continue or not with the paste. |
|
||||
| `multiLinePasteWarning` | Optional | Boolean | `true` | When set to `true`, trying to paste text with a _new line_ character will display a warning asking you whether to continue or not with the paste. |
|
||||
| `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. |
|
||||
| `initialPosition` | Optional | String | `","` | The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If `launchMode` is set to `"maximized"`, the window will be maximized on the monitor specified by those coordinates. |
|
||||
| `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. |
|
||||
| `launchMode` | Optional | String | `default` | Defines whether the Terminal will launch as maximized or not. Possible values: `"default"`, `"maximized"` |
|
||||
| `theme` | _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. |
|
||||
| `snapToGridOnResize` | Optional | Boolean | `false` | When set to `true`, the window will snap to the nearest character boundary on resize. When `false`, the window will resize "smoothly" |
|
||||
| `tabWidthMode` | Optional | String | `equal` | Sets the width of the tabs. Possible values: <br><ul><li>`"equal"`: sizes each tab to the same width</li><li>`"titleLength"`: sizes each tab to the length of its title</li><li>`"compact"`: sizes each tab to the length of its title when focused, and shrinks to the size of only the icon when the tab is unfocused.</li></ul> |
|
||||
| `wordDelimiters` | Optional | String | <code> /\()"'-:,.;<>~!@#$%^&*|+=[]{}~?│</code><br>_(`│` is `U+2502 BOX DRAWINGS LIGHT VERTICAL`)_ | Determines the delimiters used in a double click selection. |
|
||||
| `confirmCloseAllTabs` | Optional | Boolean | `true` | When set to `true` closing a window with multiple tabs open WILL require confirmation. When set to `false` closing a window with multiple tabs open WILL NOT require confirmation. |
|
||||
| `startOnUserLogin` | Optional | Boolean | `false` | When set to `true` enables the launch of Windows Terminal at startup. Setting to `false` will disable the startup task entry. Note: if the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect. |
|
||||
| `disabledProfileSources` | Optional | Array[String] | `[]` | Disables all the dynamic profile generators in this list, preventing them from adding their profiles to the list of profiles on startup. This array can contain any combination of `Windows.Terminal.Wsl`, `Windows.Terminal.Azure`, or `Windows.Terminal.PowershellCore`. For more information, see [UsingJsonSettings.md](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingJsonSettings.md#dynamic-profiles) |
|
||||
| `experimental.rendering.forceFullRepaint` | Optional | Boolean | `false` | When set to true, we will redraw the entire screen each frame. When set to false, we will render only the updates to the screen between frames. |
|
||||
| `experimental.rendering.software` | Optional | Boolean | `false` | When set to true, we will use the software renderer (a.k.a. WARP) instead of the hardware one. |
|
||||
|
||||
## 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 overridden by using `tabTitle`. |
|
||||
| `acrylicOpacity` | Optional | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
|
||||
| `antialiasingMode` | Optional | String | `"grayscale"` | Controls how text is antialiased in the renderer. Possible values are "grayscale", "cleartype" and "aliased". Note that changing this setting will require starting a new terminal instance. |
|
||||
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `backgroundImage` | Optional | String | | Sets the file location of the Image to draw over the window background. |
|
||||
| `backgroundImageAlignment` | Optional | String | `center` | Sets how the background image aligns to the boundaries of the window. Possible values: `"center"`, `"left"`, `"top"`, `"right"`, `"bottom"`, `"topLeft"`, `"topRight"`, `"bottomLeft"`, `"bottomRight"` |
|
||||
| `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 | String | `graceful` | Sets how the profile reacts to termination or failure to launch. Possible values: `"graceful"` (close when `exit` is typed or the process exits normally), `"always"` (always close) and `"never"` (never close). `true` and `false` are accepted as synonyms for `"graceful"` and `"never"` respectively. |
|
||||
| `colorScheme` | Optional | String | `Campbell` | Name of the terminal color scheme to use. Color schemes are defined under `schemes`. |
|
||||
| `commandline` | Optional | String | | Executable used in the profile. |
|
||||
| `cursorColor` | Optional | String | | Sets the cursor color of the profile. Overrides `cursorColor` set in color scheme if `colorscheme` is set. 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 | `Cascadia Mono` | 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. |
|
||||
| `fontWeight` | Optional | String | `normal` | Sets the weight (lightness or heaviness of the strokes) for the given font. Possible values: `"thin"`, `"extra-light"`, `"light"`, `"semi-light"`, `"normal"`, `"medium"`, `"semi-bold"`, `"bold"`, `"extra-bold"`, `"black"`, `"extra-black"`, or the corresponding numeric representation of OpenType font weight. |
|
||||
| `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 dynamically 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 | `"visible"` | 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. |
|
||||
| `altGrAliasing` | Optional | Boolean | `true` | By default Windows treats Ctrl+Alt as an alias for AltGr. When altGrAliasing is set to false, this behavior will be disabled. |
|
||||
| `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 | `false` | 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. The transparency only applies to focused windows due to OS limitation. |
|
||||
| `experimental.retroTerminalEffect` | Optional | Boolean | `false` | When set to `true`, enable retro terminal effects. This is an experimental feature, and its continued existence is not guaranteed. |
|
||||
|
||||
## 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. |
|
||||
| `cursorColor` | Optional | String | Sets the cursor 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] or String | Defines the key combinations used to call the command. |
|
||||
| `action` | Optional | String | Adds additional functionality to certain commands. |
|
||||
|
||||
### Implemented Commands and Actions
|
||||
|
||||
Commands listed below are per the implementation in [`src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp`](https://github.com/microsoft/terminal/blob/master/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp).
|
||||
|
||||
Keybindings can be structured in the following manners:
|
||||
|
||||
For commands without arguments:
|
||||
<br>
|
||||
`{ "command": "commandName", "keys": [ "modifiers+key" ] }`
|
||||
|
||||
For commands with arguments:
|
||||
<br>
|
||||
`{ "command": { "action": "commandName", "argument": "value" }, "keys": ["modifiers+key"] }`
|
||||
|
||||
| Command | Command Description | Action (*=required) | Action Arguments | Argument Descriptions |
|
||||
| ------- | ------------------- | ------ | ---------------- | ----------------- |
|
||||
| `adjustFontSize` | Change the text size by a specified point amount. | `delta` | integer | Amount of size change per command invocation. |
|
||||
| `closePane` | Close the active pane. | | | |
|
||||
| `closeTab` | Close the current tab. | | | |
|
||||
| `closeWindow` | Close the current window and all tabs within it. | | | |
|
||||
| `copy` | Copy the selected terminal content to your Windows Clipboard. | 1. `singleLine`<br>2. `copyFormatting` | 1. boolean<br>2. boolean, array | 1. When `true`, the copied content will be copied as a single line. When `false`, newlines persist from the selected text.<br>2. When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied. Not setting this value inherits the behavior of the `copyFormatting` global setting. |
|
||||
| `duplicateTab` | Make a copy and open the current tab. | | | |
|
||||
| `find` | Open the search dialog box. | | | |
|
||||
| `moveFocus` | Focus on a different pane depending on direction. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the focus will move. |
|
||||
| `newTab` | Create a new tab. Without any arguments, this will open the default profile in a new tab. | 1. `commandLine`<br>2. `startingDirectory`<br>3. `tabTitle`<br>4. `index`<br>5. `profile` | 1. string<br>2. string<br>3. string<br>4. integer<br>5. string | 1. Executable run within the tab.<br>2. Directory in which the tab will open.<br>3. Title of the new tab.<br>4. Profile that will open based on its position in the dropdown (starting at 0).<br>5. Profile that will open based on its GUID or name. |
|
||||
| `nextTab` | Open the tab to the right of the current one. | | | |
|
||||
| `openNewTabDropdown` | Open the dropdown menu. | | | |
|
||||
| `openSettings` | Open the settings file. | | | |
|
||||
| `paste` | Insert the content that was copied onto the clipboard. | | | |
|
||||
| `prevTab` | Open the tab to the left of the current one. | | | |
|
||||
| `resetFontSize` | Reset the text size to the default value. | | | |
|
||||
| `resizePane` | Change the size of the active pane. | `direction`* | `left`, `right`, `up`, `down` | Direction in which the pane will be resized. |
|
||||
| `scrollDown` | Move the screen down. | | | |
|
||||
| `scrollUp` | Move the screen up. | | | |
|
||||
| `scrollUpPage` | Move the screen up a whole page. | | | |
|
||||
| `scrollDownPage` | Move the screen down a whole page. | | | |
|
||||
| `sendInput` | Sends some text input to the shell. | `input` | string | The text input to feed into the shell.<br>ANSI escape sequences may be used. Escape codes like `\x1b` must be written as `\u001b`.<br>For instance the input `"text\n"` will write "text" followed by a newline. `"\u001b[D"` will behave as if the left arrow button had been pressed. |
|
||||
| `splitPane` | Halve the size of the active pane and open another. Without any arguments, this will open the default profile in the new pane. | 1. `split`*<br>2. `commandLine`<br>3. `startingDirectory`<br>4. `tabTitle`<br>5. `index`<br>6. `profile`<br>7. `splitMode` | 1. `vertical`, `horizontal`, `auto`<br>2. string<br>3. string<br>4. string<br>5. integer<br>6. string<br>7. string | 1. How the pane will split. `auto` will split in the direction that provides the most surface area.<br>2. Executable run within the pane.<br>3. Directory in which the pane will open.<br>4. Title of the tab when the new pane is focused.<br>5. Profile that will open based on its position in the dropdown (starting at 0).<br>6. Profile that will open based on its GUID or name.<br>7. Controls how the pane splits. Only accepts `duplicate` which will duplicate the focused pane's profile into a new pane. |
|
||||
| `switchToTab` | Open a specific tab depending on index. | `index`* | integer | Tab that will open based on its position in the tab bar (starting at 0). |
|
||||
| `toggleFullscreen` | Switch between fullscreen and default window sizes. | | | |
|
||||
| `unbound` | Unbind the associated keys from any command. | | | |
|
||||
|
||||
### Accepted Modifiers and Keys
|
||||
|
||||
#### Modifiers
|
||||
`ctrl+`, `shift+`, `alt+`
|
||||
|
||||
#### Keys
|
||||
|
||||
| Type | Keys |
|
||||
| ---- | ---- |
|
||||
| Function and Alphanumeric Keys | `f1-f24`, `a-z`, `0-9` |
|
||||
| Symbols | ``` ` ```, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/` |
|
||||
| Arrow Keys | `down`, `left`, `right`, `up`, `pagedown`, `pageup`, `pgdn`, `pgup`, `end`, `home`, `plus`, `app`, `menu` |
|
||||
| Action Keys | `tab`, `enter`, `esc`, `escape`, `space`, `backspace`, `delete`, `insert` |
|
||||
| Numpad Keys | `numpad_0-numpad_9`, `numpad0-numpad9`, `numpad_add`, `numpad_plus`, `numpad_decimal`, `numpad_period`, `numpad_divide`, `numpad_minus`, `numpad_subtract`, `numpad_multiply` |
|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
|
||||
@@ -26,25 +26,11 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"BellStyle": {
|
||||
"enum": [
|
||||
"none",
|
||||
"audible"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"Icon": {
|
||||
"description": "Image file location or an emoji to be used as an icon. Displays within the tab, the dropdown menu, and jumplist.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"ShortcutActionName": {
|
||||
"enum": [
|
||||
"adjustFontSize",
|
||||
@@ -81,7 +67,6 @@
|
||||
"toggleAlwaysOnTop",
|
||||
"toggleFocusMode",
|
||||
"toggleFullscreen",
|
||||
"togglePaneZoom",
|
||||
"toggleRetroEffect",
|
||||
"wt",
|
||||
"unbound"
|
||||
@@ -479,14 +464,6 @@
|
||||
"type": "array"
|
||||
}
|
||||
]
|
||||
},
|
||||
"icon": { "$ref": "#/definitions/Icon" },
|
||||
"name": {
|
||||
"description": "The name that will appear in the command palette. If one isn't provided, the terminal will attempt to automatically generate a name.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -519,11 +496,6 @@
|
||||
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",
|
||||
"$ref": "#/definitions/CopyFormat"
|
||||
},
|
||||
"disableAnimations": {
|
||||
"default": false,
|
||||
"description": "When set to `true`, visual animations will be disabled across the application.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"largePasteWarning": {
|
||||
"default": true,
|
||||
"description": "When set to true, trying to paste text with more than 5 KiB of characters will display a warning asking you whether to continue or not with the paste.",
|
||||
@@ -555,18 +527,18 @@
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
|
||||
"description": "The number of columns displayed in the window upon first load.",
|
||||
"maximum": 999,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"initialPosition": {
|
||||
"$ref": "#/definitions/Coordinates",
|
||||
"description": "The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), the window will be maximized on the monitor specified by those coordinates."
|
||||
"description": "The position of the top left corner of the window upon first load. On a system with multiple displays, these coordinates are relative to the top left of the primary display. If \"launchMode\" is set to maximized, the window will be maximized on the monitor specified by those coordinates."
|
||||
},
|
||||
"initialRows": {
|
||||
"default": 30,
|
||||
"description": "The number of rows displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
|
||||
"description": "The number of rows displayed in the window upon first load.",
|
||||
"maximum": 999,
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
@@ -578,13 +550,11 @@
|
||||
},
|
||||
"launchMode": {
|
||||
"default": "default",
|
||||
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window. Setting this to \"focus\" is equivalent to launching the terminal in the \"default\" mode, but with the focus mode enabled. Similar, setting this to \"maximizedFocus\" will result in launching the terminal in a maximized window with the focus mode enabled.",
|
||||
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window.",
|
||||
"enum": [
|
||||
"fullscreen",
|
||||
"maximized",
|
||||
"default",
|
||||
"focus",
|
||||
"maximizedFocus"
|
||||
"default"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -724,11 +694,6 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"bellStyle": {
|
||||
"default": "audible",
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"audible\", the Terminal will play a sound. When set to \"none\", nothing will happen.",
|
||||
"$ref": "#/definitions/BellStyle"
|
||||
},
|
||||
"closeOnExit": {
|
||||
"default": "graceful",
|
||||
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
|
||||
@@ -844,7 +809,10 @@
|
||||
"minimum": -1,
|
||||
"type": "integer"
|
||||
},
|
||||
"icon":{ "$ref": "#/definitions/Icon" },
|
||||
"icon": {
|
||||
"description": "Image file location of the icon used in the profile. Displays within the tab and the dropdown menu.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"name": {
|
||||
"description": "Name of the profile. Displays in the dropdown menu.",
|
||||
"minLength": 1,
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# Creating a New Project
|
||||
|
||||
## Creating a new WinRT Component DLL and referencing it in another project
|
||||
|
||||
When creating a new DLL, it was really helpful to reference an existing DLL's `.vcxproj` like `TerminalControl.vcxproj`. While you should mostly try to copy what the existing `.vcxproj` has, here's a handful of things to double check for as you go along.
|
||||
|
||||
- [ ] Make sure to `<Import>` our pre props at the _top_ of the vcxproj, and our post props at the _bottom_ of the vcxproj.
|
||||
```
|
||||
<!-- pre props -->
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
<!-- everything else -->
|
||||
|
||||
<!-- post props -->
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
```
|
||||
- [ ] Add a `<ProjectReference>` to your new `.vcxproj` in both `WindowsTerminal.vcxproj` and `TerminalApp.vcxproj`
|
||||
- [ ] Add a `<Reference>` to `TerminalAppLib.vcxproj` similar to this:
|
||||
```
|
||||
<Reference Include="Microsoft.Terminal.NewDLL">
|
||||
<HintPath>$(_BinRoot)TerminalNewDLL\Microsoft.Terminal.NewDLL.winmd</HintPath>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
<Private>false</Private>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
</Reference>
|
||||
```
|
||||
- [ ] Make sure the project has a `.def` file with the following lines. The `WINRT_GetActivationFactory` part is important to expose the new DLL's activation factory so that other projects can successfully call the DLL's `GetActivationFactory` to get the DLL's classes.
|
||||
```
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
```
|
||||
- For a bit more context on this whole process, the `AppXManifest.xml` file defines which classes belong to which DLLs. If your project wants class `X.Y.Z`, it can look it up in the manifest's definitions and see that it came from `X.Y.dll`. Then it'll load up the DLL, and call a particular function called `GetActivationFactory(L"X.Y.Z")` to get the class it wants. So, the definitions in `AppXManifest` are _required_ for this activation to work properly, and I found myself double checking the file to see that the definitions I expect are there.
|
||||
- _Note_: If your new library eventually rolls up as a reference to our Centennial Packaging project `CascadiaPackage`, you don't have to worry about manually adding your definitions to the `AppXManifest.xml` because the Centennial Packaging project automatically enumerates the reference tree of WinMDs and stitches that information into the `AppXManifest.xml`. However, if your new project does _not_ ultimately roll up to a packaging project that will automatically put the references into `AppXManifest`, you will have to add them in manually.
|
||||
|
||||
### Troubleshooting
|
||||
- If you hit an error that looks like this:
|
||||
```
|
||||
X found processing metadata file ..\blah1\Microsoft.UI.Xaml.winmd, type already exists in file ..\blah\NewDLLProject\Microsoft.UI.Xaml.winmd.
|
||||
```
|
||||
The `Microsoft.UI.Xaml.winmd` is showing up in the output folder when it shouldn't. Try adding this block at the top of your `.vcxproj`
|
||||
```
|
||||
<ItemDefinitionGroup>
|
||||
<Reference>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemDefinitionGroup>
|
||||
```
|
||||
This will make all references non-private, meaning "don't copy it into my folder" by default.
|
||||
|
||||
- If you hit a `Class not Registered` error, this might be because a class isn't getting registered in the app manifest. You can go check `src/cascadia/CascadiaPackage/bin/x64/Debug/AppX/AppXManifest.xml` to see if there exist entries to the classes of your newly created DLL. If the references aren't there, double check that you've added `<ProjectReference>` blocks to both `WindowsTerminal.vcxproj` and `TerminalApp.vcxproj`.
|
||||
|
||||
- If you hit an extremely vague error along the lines of `Error in the DLL`, and right before that line you notice that your new DLL is loaded and unloaded right after each other, double check that your new DLL's definitions show up in the `AppXManifest.xml` file. If your new DLL is included as a reference to a project that rolls up to `CascadiaPackage`, double check that you've created a `.def` file for the project. Otherwise if your new project _does not_ roll up to a package that populates the `AppXManifest` references for you, you'll have to add those references yourself.
|
||||
BIN
doc/images/custom-icon-and-background-image.jpg
Normal file
|
After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 274 KiB |
@@ -1,112 +0,0 @@
|
||||
---
|
||||
author: Kayla Cinnamon @cinnamon-msft
|
||||
created on: 2020-07-13
|
||||
last updated: 2020-08-11
|
||||
issue id: #1564
|
||||
---
|
||||
|
||||
# Settings UI Design
|
||||
|
||||
## Abstract
|
||||
|
||||
This design document describes how each page of the settings UI will be laid out along with design mockups to display how the UI will appear. The mock ups are for appearance purposes and some layouts and naming may be different in this doc. This doc should be considered the final say.
|
||||
|
||||
## UI Design
|
||||
|
||||
### Overall navigation with Startup page
|
||||
|
||||
This is the list of the top-level navigation items that will appear on the left nav bar:
|
||||
|
||||
- General
|
||||
- Startup
|
||||
- Interaction
|
||||
- Rendering
|
||||
- Appearance
|
||||
- Global
|
||||
- Color schemes
|
||||
- Themes*
|
||||
- Profiles
|
||||
- Defaults
|
||||
- Enumerate profiles
|
||||
- Add new
|
||||
- Keyboard
|
||||
- Mouse*
|
||||
- Command Palette*
|
||||
- Marketplace*
|
||||
|
||||
\* Themes, mouse, command palette, and marketplace will be added once they're implemented.
|
||||
|
||||

|
||||
|
||||
### Profile appearance page
|
||||
|
||||
This page requires special design because it includes the TerminalControl window to preview appearance changes. This preview window will appear on the following pages:
|
||||
|
||||
- Appearance - Color Schemes
|
||||
- Profiles - Appearance
|
||||
|
||||

|
||||
|
||||
### Keyboard page
|
||||
|
||||
The keyboard page will list the enabled key bindings and provide a way for users to add and remove them.
|
||||
|
||||

|
||||
|
||||
When someone hovers over one of the items in the table, the Edit and Delete buttons will appear. Below is what the modal looks like if they were to click Edit on a command that does not have any arguments/actions. In the future, we would want this text box to be able to listen for key combinations. This would add a "listen" button to the UI.
|
||||
|
||||

|
||||
|
||||
If the command they select has additional arguments/actions, the modal will dynamically size as arguments/actions are added.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Settings layout
|
||||
|
||||
Below is the list of all settings on their respective pages in the settings UI. The title row aligns with the navigation view on the left of the UI. Bolded headers in those columns align with top nav on the page.
|
||||
|
||||
| General - Startup | General - Interaction | General - Rendering | Appearance - Global | Appearance - Color Schemes | Profiles - Global | Profiles - Enumerate profiles | Profiles - Add new |
|
||||
| ---------------- | --------------------- | ------------------- | ------------------- | -------------------------- | ----------------- | ----------------------------- | ------------------ |
|
||||
| Default profile (dropdown) | Copy after selection is made (checkbox) | Software rendering (checkbox) | Theme (radio) | Name (text box) | **General** | **General** | **General** | **General** |
|
||||
| Launch on startup (checkbox) | Copy formatting (checkbox) | Screen redrawing (checkbox) | Show/Hide the title bar (checkbox) | Cursor color (color picker) | Command line (text box) | Scrollbar visibility (radio) | Scrollbar visibility (radio) |
|
||||
| Launch size (radio) | Word delimiters (text box) | | Show terminal title in title bar (checkbox) | Selection background (color picker) | Starting directory (browse button) | Command line (browse button) | Command line (browse button) |
|
||||
| Launch position (text box) | Window resize behavior (checkbox) | | Always show tabs (checkbox) | Background (color picker) | Icon (browse button) | Starting directory (browse button) | Starting directory (browse button) |
|
||||
| Columns on first launch (number picker) | | | Tab width mode (radio) | Foreground (color picker) | Tab title (text box) | Name (text box) | Name (text box) |
|
||||
| Rows on first launch (number picker) | | | Hide close all tabs popup (checkbox) | Black (color picker) | Scrollbar visibility (radio) | Icon (browse button) | Icon (browse button) |
|
||||
| Automatically create new profiles when new shells are installed (checkbox) | | | | Blue (color picker) | **Appearance** | Tab title (text box) | Tab title (text box) |
|
||||
| | | | | Cyan (color picker) | Font face (text box) | **Appearance** | **Appearance** |
|
||||
| | | | | Green (color picker) | Font size (number picker) | Retro terminal effects (checkbox) | Retro terminal effects (checkbox) |
|
||||
| | | | | Purple (color picker) | Font weight (dropdown) | Font face (text box) | Font face (text box) |
|
||||
| | | | | Red (color picker) | Padding (text box) | Font size (number picker) | Font size (number picker) |
|
||||
| | | | | White (color picker) | Cursor shape (radio) | Font weight (dropdown) | Font weight (dropdown) |
|
||||
| | | | | Yellow (color picker) | Cursor color (color picker) | Padding (text box) | Padding (text box) |
|
||||
| | | | | Bright black (color picker) | Cursor height (number picker) | Cursor shape (radio) | Cursor shape (radio) |
|
||||
| | | | | Bright blue (color picker) | Color scheme (dropdown) | Cursor color (color picker) | Cursor color (color picker) |
|
||||
| | | | | Bright cyan (color picker) | Foreground color (color picker) | Cursor height (number picker) | Cursor height (number picker) |
|
||||
| | | | | Bright green (color picker) | Background color (color picker) | Color scheme (dropdown) | Color scheme (dropdown) |
|
||||
| | | | | Bright purple (color picker) | Selection background color (color picker) | Foreground color (color picker) | Foreground color (color picker) |
|
||||
| | | | | Bright red (color picker) | Enable acrylic (checkbox) | Background color (color picker) | Background color (color picker) |
|
||||
| | | | | Bright white (color picker) | Acrylic opacity (number picker) | Selection background color (color picker) | Selection background color (color picker) |
|
||||
| | | | | Bright yellow (color picker) | Background image (browse button) | Enable acrylic (checkbox) | Enable acrylic (checkbox) |
|
||||
| | | | | | Background image stretch mode (radio) | Acrylic opacity (number picker) | Acrylic opacity (number picker) |
|
||||
| | | | | | Background image alignment (dropdown) | Background image (browse button) | Background image (browse button) |
|
||||
| | | | | | Background image opacity (number picker) | Background image stretch mode (radio) | Background image stretch mode (radio) |
|
||||
| | | | | | Retro terminal effects (checkbox) | Background image alignment (dropdown) | Background image alignment (dropdown) |
|
||||
| | | | | | **Advanced** | Background image opacity (number picker) | Background image opacity (number picker) |
|
||||
| | | | | | Hide profile from dropdown (checkbox) | **Advanced** | **Advanced** |
|
||||
| | | | | | Suppress title changes (checkbox) | GUID (text box) | GUID (text box) |
|
||||
| | | | | | Antialiasing text (radio) | Hide profile from dropdown (checkbox) | Hide profile from dropdown (checkbox) |
|
||||
| | | | | | AltGr aliasing (checkbox) | Suppress title changes (checkbox) | Suppress title changes (checkbox) |
|
||||
| | | | | | Scroll to input when typing (checkbox) | Antialiasing text (radio) | Antialiasing text (radio) |
|
||||
| | | | | | History size (number picker) | AltGr aliasing (checkbox) | AltGr aliasing (checkbox) |
|
||||
| | | | | | How the profile closes (radio) | Scroll to input when typing (checkbox) | Scroll to input when typing (checkbox) |
|
||||
| | | | | | | History size (number picker) | History size (number picker) |
|
||||
| | | | | | | How the profile closes (radio) | How the profile closes (radio) |
|
||||
|
||||
## Potential Issues
|
||||
|
||||
## Future considerations
|
||||
|
||||
## Resources
|
||||
|
Before Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 47 KiB |
@@ -1,122 +0,0 @@
|
||||
---
|
||||
author: Kayla Cinnamon @cinnamon-msft
|
||||
created on: 2020-06-29
|
||||
last updated: 2020-08-10
|
||||
issue id: #1564
|
||||
---
|
||||
|
||||
# Settings UI Implementation
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec describes the basic functionality of the settings UI, including disabling it, the navigation items, launch methods, and editing of settings. The specific layout of each page will defined in later design reviews.
|
||||
|
||||
## Inspiration
|
||||
|
||||
We have been wanting a settings UI since the dawn of Terminal time, so we need to define how it will interact with the application and how users should expect to interact with it.
|
||||
|
||||
## Solution Design
|
||||
|
||||
The settings UI will be the default experience. We will provide users an option to skip the settings UI and edit the raw JSON file.
|
||||
|
||||
### Ability to disable displaying the settings UI
|
||||
|
||||
Some users don't want a UI for the settings. We can update the `openSettings` key binding with a `settingsUI` option.
|
||||
|
||||
If people still like the UI but want to access the JSON file, we can provide an "Open the JSON file" button at the bottom of the navigation menu.
|
||||
|
||||
### Launch method: launch in a new tab
|
||||
|
||||
Clicking the settings button in the dropdown menu will open the settings UI in a new tab. This helps us take steps toward supporting non-terminal content in a tab. Users will be able to see their visual changes by using the preview window inside the settings UI on relevant pages.
|
||||
|
||||
#### We also considered: launch in a new window
|
||||
|
||||
Clicking the settings button in the dropdown menu will open the settings UI in a new window. This allows the user to edit their settings and see the Terminal live update with their changes.
|
||||
|
||||
In the Windows taskbar, the icon will appear as if Terminal has multiple windows open.
|
||||
|
||||
### Editing and saving settings: implement a save button
|
||||
|
||||
Users will only see their settings changes take place once they click "Save". Clicking "Save" will write to the settings.json file. This aligns with the functionality that exists today by editing the settings.json file in a text editor and saving it.
|
||||
|
||||
We will also be adding a TerminalControl inside the settings UI to preview what the changes will look like before actually saving them to the settings.json file.
|
||||
|
||||
#### We also considered: automatically save settings
|
||||
|
||||
As users edit fields in the settings UI, they are automatically saved and written to the JSON file. This allows the user to see their settings changes taking place in real time.
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
Layout of all of the settings per page can be found in the [design doc](./design.md).
|
||||
|
||||
### Top-level navigation: more descriptive navigation
|
||||
|
||||
The navigation menu is broken up into more digestible sections. This aligns more closely to other terminals. The following are the proposed navigation items:
|
||||
|
||||
- General
|
||||
- Startup
|
||||
- Interaction
|
||||
- Rendering
|
||||
- Appearance
|
||||
- Global
|
||||
- Color schemes
|
||||
- Themes*
|
||||
- Profiles
|
||||
- Defaults
|
||||
- Enumerate profiles
|
||||
- Add new
|
||||
- Keyboard
|
||||
- Mouse*
|
||||
- Command Palette*
|
||||
- Marketplace*
|
||||
|
||||
\* Themes, mouse, command palette, and marketplace will be added once they're implemented.
|
||||
|
||||

|
||||
|
||||
#### We also considered: align with JSON
|
||||
|
||||
The settings UI could have top-level navigation that aligns with the overall structure of the settings.json file. The following are the proposed navigation items:
|
||||
|
||||
- Globals
|
||||
- Profiles
|
||||
- Color schemes
|
||||
- Bindings
|
||||
|
||||
For Bindings, it would have key bindings, mouse bindings, and command palette inside it.
|
||||
|
||||

|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
This will have to undergo full accessibility testing because it is a new UI element. All items inside the settings UI should be accessible by a screen reader and the keyboard. Additionally, all of the settings UI will have to be localized.
|
||||
|
||||
### Security
|
||||
|
||||
This does not impact security.
|
||||
|
||||
### Reliability
|
||||
|
||||
This will not improve reliability.
|
||||
|
||||
### Compatibility
|
||||
|
||||
This will change the default experience to open the UI, rather than the JSON file in a text editor. This behavior can be reverted with the setting listed [above](#ability-to-disable-displaying-the-settings-ui).
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
This does not affect performance, power, nor efficiency.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
## Future considerations
|
||||
|
||||
- We will have to have design reviews for all of the content pages.
|
||||
- The `hidden` property will need special consideration. Ideally, all profiles will appear in the settings regardless if `hidden` is set to `true`.
|
||||
- We should have undo functionality. In a text editor, you can type `Ctrl+Z` however the settings UI is a bit more complex.
|
||||
- Once we have a marketplace for themes and extensions, this should be added to the top-level navigation.
|
||||
- As we add more features, the top-level navigation is subject to change in favor of improved usability.
|
||||
|
||||
## Resources
|
||||
@@ -1,251 +0,0 @@
|
||||
---
|
||||
author: Carlos Zamora @carlos-zamora
|
||||
created on: 2020-07-10
|
||||
last updated: 2020-07-10
|
||||
issue id: [#885](https://github.com/microsoft/terminal/issues/885)
|
||||
---
|
||||
|
||||
# Terminal Settings Model
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec proposes a major refactor and repurposing of the TerminalSettings project as the TerminalSettingsModel.
|
||||
TerminalSettingsModel would be responsible for exposing, serializing, and deserializing settings as WinRT objects
|
||||
for Windows Terminal. In doing so, Terminal's settings model is accessible as WinRT objects to existing components
|
||||
like TerminalApp, TerminalControl, and TerminalCore. Additionally, Terminal Settings can be used by the Settings UI or
|
||||
Shell Extensions to modify or reference Terminal's settings respectively.
|
||||
|
||||
## Inspiration
|
||||
|
||||
The main driver for this change is the Settings UI. The Settings UI will need to read and modify Terminal's settings
|
||||
objects. At the time of writing this spec, the Terminal's settings are serialized as objects in the TerminalApp project.
|
||||
To access these objects via XAML, the Settings UI needs them to be WinRT objects. Additional features that need the
|
||||
settings objects to be WinRT objects include future shell extensions, like jumplist.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Terminal Settings Model: Objects and Projections
|
||||
|
||||
The following TerminalApp objects will become WinRT objects and will be moved to the TerminalSettingsModel project
|
||||
(formerly TerminalSettings):
|
||||
- ColorScheme
|
||||
- Profile
|
||||
- GlobalAppSettings
|
||||
- CascadiaSettings
|
||||
|
||||
The TerminalSettingsModel project will have a root namespace of `Microsoft.Terminal.Settings.Model`.
|
||||
|
||||
Adjacent to the introduction of these settings objects, `IControlSettings` and `ICoreSettings` will be moved
|
||||
to the `Microsoft.Terminal.TerminalControl` namespace. This allows for a better consumption of the
|
||||
settings model that is covered later in the (Consumption section)[#terminal-settings-model:-consumption].
|
||||
|
||||
#### Moving/Splitting the Action Model
|
||||
|
||||
Windows Terminal represents actions via several objects:
|
||||
- `AppKeyBindings`: a map of all the defined keybindings and their corresponding actions
|
||||
- `ActionAndArgs`: a (de)serializable action (this holds more objects inside of it, but we won't focus on that for now)
|
||||
- `ShortcutActionDispatch`: responsible for dispatching events pertinent to a given ActionAndArgs object
|
||||
`TerminalApp`'s `TerminalPage` handles any events dispatched by the `ShortcutActionDispatch`.
|
||||
|
||||
With the introduction of the TerminalSettingsModel, we will split `AppKeyBindings` using a `KeyMapping` class.
|
||||
This separation will look something like the following:
|
||||
```c++
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.TerminalControl.IKeyBindings
|
||||
{
|
||||
AppKeyBindings();
|
||||
|
||||
// NOTE: It may be possible to move both of these to the constructor instead
|
||||
void SetDispatch(ShortcutActionDispatch dispatch);
|
||||
void SetKeyMap(KeyMapping keymap);
|
||||
}
|
||||
}
|
||||
|
||||
namespace TerminalSettingsModel
|
||||
{
|
||||
[default_interface] runtimeclass KeyMapping
|
||||
{
|
||||
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.TerminalControl.KeyChord chord);
|
||||
void ClearKeyBinding(Microsoft.Terminal.TerminalControl.KeyChord chord);
|
||||
|
||||
Microsoft.Terminal.TerminalControl.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
Microsoft.Terminal.TerminalControl.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
|
||||
}
|
||||
}
|
||||
```
|
||||
This separation leaves `AppKeyBindings` with the responsibility of detecting and dispatching actions, whereas
|
||||
`KeyMapping` handles the (de)serialization and navigation of the key bindings.
|
||||
|
||||
|
||||
### Terminal Settings Model: Serialization and Deserialization
|
||||
|
||||
Introducing these `Microsoft.Terminal.Settings.Model` WinRT objects also allow the serialization and deserialization
|
||||
logic from TerminalApp to be moved to TerminalSettings. `JsonUtils` introduces several quick and easy methods
|
||||
for setting serialization. This will be moved into the `Microsoft.Terminal.Settings.Model` namespace too.
|
||||
|
||||
Deserialization will be an extension of the existing `JsonUtils` `ConversionTrait` struct template. `ConversionTrait`
|
||||
already includes `FromJson` and `CanConvert`. Serialization would be handled by a `ToJson` function.
|
||||
|
||||
|
||||
### Terminal Settings Model: Warnings and Serialization Errors
|
||||
|
||||
Today, if the deserialization of `CascadiaSettings` encounters any errors, an exception is thrown and caught/handled
|
||||
by falling back to a simple `CascadiaSettings` object. However, WinRT does not support exceptions.
|
||||
|
||||
To get around this issue, when `CascadiaSettings` encounters a serialization error, it must internally record
|
||||
any pertinent information for that error, and return the simple `CascadiaSettings` as if nothing happened.
|
||||
The consumer must then call `CascadiaSettings::GetErrors()` and `CascadiaSettings::GetWarnings()` to properly
|
||||
understand whether an error ocurred and how to present that to the user.
|
||||
|
||||
|
||||
#### TerminalApp: Loading and Reloading Changes
|
||||
|
||||
TerminalApp will construct and reference a `CascadiaSettings settings` as follows:
|
||||
- TerminalApp will have a global reference to the "settings.json" filepath
|
||||
- construct an `CascadiaSettings` using `CascadiaSettings("settings.json")`. This builds an `CascadiaSettings`
|
||||
from the "defaults.json" file data (which is already compiled as a string literal)
|
||||
and layers the settings.json data on top of it.
|
||||
- check for errors/warnings, and handle them appropriately
|
||||
|
||||
This will be different from the current model which has the settings.json path hardcoded, and is simplified
|
||||
to a `LoadAll()` call wrapped in error handlers.
|
||||
|
||||
**NOTE:** This model allows us to layer even more settings files on top of the existing Terminal Settings
|
||||
Model, if so desired. This could be helpful when importing additional settings files from an external location
|
||||
such as a marketplace.
|
||||
|
||||
When TerminalApp detects a change to settings.json, it'll repeat the steps above. We could cache the result from
|
||||
constructing an `CascadiaSettings` from "defaults.json" data to improve performance.
|
||||
|
||||
|
||||
#### TerminalControl: Acquiring and Applying the Settings
|
||||
|
||||
At the time of writing this spec, TerminalApp constructs `TerminalControl.TerminalSettings` WinRT objects
|
||||
to expose `IControlSettings` and `ICoreSettings` to any hosted terminals. In moving `IControlSettings`
|
||||
and `ICoreSettings` down to the TerminalControl layer, TerminalApp can now have better control over
|
||||
how to expose relevant settings to a TerminalControl instance.
|
||||
|
||||
`TerminalSettings` (which implements `IControlSettings` and `ICoreSettings`) will be moved to
|
||||
TerminalApp and act as a bridge connecting `CascadiaSettings` to the TermControl. It will operate
|
||||
very similarly as it does today. On construction of the TermControl or hot-reload,
|
||||
`TerminalSettings` will be constructed by copying the relevant values of `CascadiaSettings`.
|
||||
Then, it will be passed to TermControl (and TermCore by extension).
|
||||
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
N/A
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
N/A
|
||||
|
||||
### Security
|
||||
|
||||
N/A
|
||||
|
||||
### Reliability
|
||||
|
||||
N/A
|
||||
|
||||
### Compatibility
|
||||
|
||||
N/A
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
N/A
|
||||
|
||||
## Future considerations
|
||||
|
||||
### TerminalSettings: passing by reference
|
||||
|
||||
`TermApp` synthesizes a `TerminalSettings` by copying the relevant values of `CascadiaSettings`,
|
||||
then giving it to a Terminal Control. Some visual keybindings and interactions like ctrl+scroll
|
||||
and ctrl+shift+scroll to change the font size and acrylic opacity operate by directly modifying
|
||||
the value of the instantiated `TerminalSettings`. However, when a settings reload occurs,
|
||||
these instanced changes are lost.
|
||||
|
||||
`TerminalSettings` can be used as a WinRT object that references (instead of copies) the relevant
|
||||
values of `CascadiaSettings`. This would prevent those instanced changes from being lost on a settings
|
||||
reload.
|
||||
|
||||
Since previewing commands like `setColorScheme` would require a clone of the existing `TerminalSettings`,
|
||||
a `Clone` API can be added on `TerminalSettings` to accomplish that. When passing by value,
|
||||
`TerminalSettings` can just overwrite the existing property (i.e.: color scheme). When passing
|
||||
by reference, a slightly more complex mechanism is required to override the value.
|
||||
|
||||
Now, instead of overwriting the value, we need to override the reference to a constant value
|
||||
(i.e.: `snapOnInput=true`) or a referenced value (i.e.: `colorScheme`).
|
||||
|
||||
### Layering Additional Settings
|
||||
As we begin to introduce more sources that affect the settings (via extensions or themes),
|
||||
we can introduce a `LayerSettings(String path)`. This layers the new settings file
|
||||
onto the existing `CascadiaSettings`. This is already done internally, we would just expose
|
||||
it via C++/WinRT.
|
||||
|
||||
```c++
|
||||
runtimeclass CascadiaSettings
|
||||
{
|
||||
// Load a settings file, and layer those changes on top of the existing CascadiaSettings
|
||||
void LayerSettings(String path);
|
||||
}
|
||||
```
|
||||
|
||||
### Settings UI: Modifying and Applying the Settings (DRAFT)
|
||||
|
||||
```c++
|
||||
runtimeclass CascadiaSettings
|
||||
{
|
||||
// Create a copy of the existing CascadiaSettings
|
||||
CascadiaSettings Clone();
|
||||
|
||||
// Compares object to "source" and applies changes to
|
||||
// the settings file at "outPath"
|
||||
void Save(String outPath);
|
||||
}
|
||||
```
|
||||
|
||||
The Settings UI will also have a reference to the `CascadiaSettings settings` from TerminalApp
|
||||
as `settingsSource`. When the Settings UI is opened up, the Settings UI will also have its own `CascadiaSettings settingsClone`
|
||||
that is a clone of TerminalApp's `CascadiaSettings`.
|
||||
```c++
|
||||
settingsClone = settingsSource.Clone()
|
||||
```
|
||||
|
||||
As the user navigates the Settings UI, the relevant contents of `settingsClone` will be retrieved and presented.
|
||||
As the user makes changes to the Settings UI, XAML will update `settingsClone` using XAML data binding.
|
||||
When the user saves/applies the changes in the XAML, `settingsClone.Save("settings.json")` is called;
|
||||
this compares the changes between `settingsClone` and `settingsSource`, then injects the changes (if any) to `settings.json`.
|
||||
|
||||
As mentioned earlier, TerminalApp detects a change to "settings.json" to update its `CascadiaSettings`.
|
||||
Since the above triggers a change to `settings.json`, TerminalApp will also update itself. When
|
||||
something like this occurs, `settingsSource` will automatically be updated too.
|
||||
|
||||
In the case that a user is simultaneously updating the settings file directly and the Settings UI,
|
||||
`settingsSource` and `settingsClone` can be compared to ensure that the Settings UI, the TerminalApp,
|
||||
and the settings files are all in sync.
|
||||
|
||||
**NOTE:** In the event that the user would want to export their current configuration, `Save`
|
||||
can be used to export the changes to a new file.
|
||||
|
||||
### Reserialization (DRAFT)
|
||||
|
||||
After deserializing the settings, injecting the new json into settings.json
|
||||
should not remove the existing comments or formatting.
|
||||
|
||||
The reserialization process takes place right after comparing the `settingsSource` and `settingsClone` objects.
|
||||
For each setting found in the diff, we go to the relevant part of the JSON and see if the key is already there.
|
||||
If it is, we update the value to be the one from `settingsClone`. Otherwise, we append the key/value pair
|
||||
at the end of the section (much like we do with dynamic profiles in `profiles`).
|
||||
|
||||
## Resources
|
||||
|
||||
- [Preview Commands](https://github.com/microsoft/terminal/issues/6689)
|
||||
- [New JSON Utils](https://github.com/microsoft/terminal/pull/6590)
|
||||
- [Spec: Settings UI](https://github.com/microsoft/terminal/pull/6720)
|
||||
@@ -22,14 +22,15 @@ Below is the schedule for when milestones will be included in release builds of
|
||||
| Milestone End Date | Milestone Name | Preview Release Blog Post |
|
||||
| ------------------ | -------------- | ------------------------- |
|
||||
| 2020-06-18 | [1.1] in Windows Terminal Preview | [Windows Terminal Preview 1.1 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-1-release/) |
|
||||
| 2020-07-31 | [1.2] in Windows Terminal Preview<br>[1.1] in Windows Terminal | [Windows Terminal Preview 1.2 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-2-release/) |
|
||||
| 2020-08-31 | [1.3] in Windows Terminal Preview<br>[1.2] in Windows Terminal | [Windows Terminal Preview 1.3 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-3-release/) |
|
||||
| 2020-09-30 | [1.4] in Windows Terminal Preview<br>[1.3] in Windows Terminal | [Windows Terminal Preview 1.4 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-4-release/) |
|
||||
| 2020-11-30 | [1.5] in Windows Terminal Preview<br>[1.4] in Windows Terminal | |
|
||||
| 2020-12-31 | 1.6 in Windows Terminal Preview<br>[1.5] in Windows Terminal | |
|
||||
| 2021-01-31 | 1.7 in Windows Terminal Preview<br>1.6 in Windows Terminal | |
|
||||
| 2021-02-28 | 1.8 in Windows Terminal Preview<br>1.8 in Windows Terminal | |
|
||||
| 2021-03-31 | 1.9 in Windows Terminal Preview<br>1.9 in Windows Terminal | |
|
||||
| 2020-07-31 | [1.2] in Windows Terminal Preview<br>[1.1] in Windows Terminal | |
|
||||
| 2020-08-31 | 1.3 in Windows Terminal Preview<br>[1.2] in Windows Terminal | |
|
||||
| 2020-09-30 | 1.4 in Windows Terminal Preview<br>1.3 in Windows Terminal | |
|
||||
| 2020-10-31 | 1.5 in Windows Terminal Preview<br>1.4 in Windows Terminal | |
|
||||
| 2020-11-30 | 1.6 in Windows Terminal Preview<br>1.5 in Windows Terminal | |
|
||||
| 2020-12-31 | 1.7 in Windows Terminal Preview<br>1.6 in Windows Terminal | |
|
||||
| 2021-01-31 | 1.8 in Windows Terminal Preview<br>1.7 in Windows Terminal | |
|
||||
| 2021-02-28 | 1.9 in Windows Terminal Preview<br>1.8 in Windows Terminal | |
|
||||
| 2021-03-31 | 1.10 in Windows Terminal Preview<br>1.9 in Windows Terminal | |
|
||||
| 2021-04-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-05-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
|
||||
@@ -49,11 +50,11 @@ The following are a list of the key scenarios we're aiming to deliver for Termin
|
||||
|
||||
| Priority\* | Scenario | Description/Notes |
|
||||
| ---------- | -------- | ----------------- |
|
||||
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564]<br>Specs: [#6720], [#6904]<br>Implementation: [#7283], [#7370] |
|
||||
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193]<br>Implementation: [#6635] |
|
||||
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564] |
|
||||
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193] |
|
||||
| 1 | Tab tear-off | The ability to tear a tab out of the current window and spawn a new window or attach it to a separate window.<br><br>Issue: [#1256]<br>Spec: [#2080] |
|
||||
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574]<br>Implementation: [#7251] |
|
||||
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080], [#7414] |
|
||||
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574] |
|
||||
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080] |
|
||||
| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme<br><br>Issue: [#3327]<br>Spec: [#5772] |
|
||||
| 1 | Open tab as admin/other user | Open tab in existing Windows Terminal instance as admin (if Terminal was run unelevated) or as another user.<br><br>Issue: [#5000] |
|
||||
| 1 | Traditional opacity | Have a transparent background without the acrylic blur.<br><br>Issue: [#603] |
|
||||
@@ -61,7 +62,7 @@ The following are a list of the key scenarios we're aiming to deliver for Termin
|
||||
| 2 | Infinite scrollback | Have an infinite history for the text buffer.<br><br>Issue: [#1410] |
|
||||
| 2 | Pane management | All issues listed out in the original issue. Some features include pane resizing with mouse, pane zooming, and opening a pane by prompting which profile to use.<br><br>Issue: [#1000] |
|
||||
| 2 | Theme marketplace | Marketplace for creation and distribution of themes.<br>Dependent on overall theming |
|
||||
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576]<br>Implementation: [#7515] |
|
||||
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576] |
|
||||
| 2 | Open with multiple tabs | A setting that allows Windows Terminal to launch with a specific tab configuration (not using only command line arguments).<br><br>Issue: [#756] |
|
||||
| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.<br><br>Issue: [#1060]<br>Implementation: [#6100] |
|
||||
| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.<br><br>Issues: [#961], [#960], [#766] |
|
||||
@@ -79,26 +80,16 @@ Feature Notes:
|
||||
|
||||
[1.1]: https://github.com/microsoft/terminal/milestone/24
|
||||
[1.2]: https://github.com/microsoft/terminal/milestone/25
|
||||
[1.3]: https://github.com/microsoft/terminal/milestone/26
|
||||
[1.4]: https://github.com/microsoft/terminal/milestone/28
|
||||
[1.5]: https://github.com/microsoft/terminal/milestone/30
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#6720]: https://github.com/microsoft/terminal/pull/6720
|
||||
[#6904]: https://github.com/microsoft/terminal/pull/6904
|
||||
[#7283]: https://github.com/microsoft/terminal/pull/7283
|
||||
[#7370]: https://github.com/microsoft/terminal/pull/7370
|
||||
[#5400]: https://github.com/microsoft/terminal/issues/5400
|
||||
[#2046]: https://github.com/microsoft/terminal/issues/2046
|
||||
[#2193]: https://github.com/microsoft/terminal/pull/2193
|
||||
[#6635]: https://github.com/microsoft/terminal/pull/6635
|
||||
[#1256]: https://github.com/microsoft/terminal/issues/1256
|
||||
[#2080]: https://github.com/microsoft/terminal/pull/2080
|
||||
[#574]: https://github.com/microsoft/terminal/issues/574
|
||||
[#7251]: https://github.com/microsoft/terminal/pull/7251
|
||||
[#492]: https://github.com/microsoft/terminal/issues/492
|
||||
[#2080]: https://github.com/microsoft/terminal/pull/2080
|
||||
[#7414]: https://github.com/microsoft/terminal/pull/7414
|
||||
[#3327]: https://github.com/microsoft/terminal/issues/3327
|
||||
[#5772]: https://github.com/microsoft/terminal/pull/5772
|
||||
[#5000]: https://github.com/microsoft/terminal/issues/5000
|
||||
@@ -109,7 +100,6 @@ Feature Notes:
|
||||
[#1410]: https://github.com/microsoft/terminal/issues/1410
|
||||
[#1000]: https://github.com/microsoft/terminal/issues/1000
|
||||
[#576]: https://github.com/microsoft/terminal/issues/576
|
||||
[#7515]: https://github.com/microsoft/terminal/pull/7515
|
||||
[#756]: https://github.com/microsoft/terminal/issues/756
|
||||
[#1060]: https://github.com/microsoft/terminal/issues/1060
|
||||
[#6100]: https://github.com/microsoft/terminal/pull/6100
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
# Adding profiles for third-party tools
|
||||
|
||||
This doc will hopefully provide a useful guide for adding profiles for common third-party tools to your
|
||||
[settings.json](https://docs.microsoft.com/en-us/windows/terminal/customize-settings/profile-settings) file.
|
||||
This doc will hopefully provide a useful guide for adding profiles for common
|
||||
third-party tools to your
|
||||
[settings.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
|
||||
|
||||
@@ -9,10 +15,10 @@ 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%"
|
||||
"commandline" : "cmd.exe /k \"%USERPROFILE%\\Anaconda3\\Scripts\\activate.bat %USERPROFILE%\\Anaconda3\"",
|
||||
"icon" : "%USERPROFILE%/Anaconda3/Menu/anaconda-navigator.ico",
|
||||
"name" : "Anaconda3",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -22,23 +28,23 @@ Assuming that you've installed cmder into `%CMDER_ROOT%`:
|
||||
|
||||
```json
|
||||
{
|
||||
"commandline": "cmd.exe /k \"%CMDER_ROOT%\\vendor\\init.bat\"",
|
||||
"name": "cmder",
|
||||
"icon": "%CMDER_ROOT%\\icons\\cmder.ico",
|
||||
"startingDirectory": "%USERPROFILE%"
|
||||
"commandline" : "cmd.exe /k \"%CMDER_ROOT%\\vendor\\init.bat\"",
|
||||
"name" : "cmder",
|
||||
"icon" : "%CMDER_ROOT%/icons/cmder.ico",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
|
||||
## Cygwin
|
||||
|
||||
Assuming that you've installed Cygwin into `C:\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"
|
||||
"name" : "Cygwin",
|
||||
"commandline" : "C:/Cygwin/bin/bash --login -i",
|
||||
"icon" : "C:/Cygwin/Cygwin.ico",
|
||||
"startingDirectory" : "C:/Cygwin/bin"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -52,49 +58,49 @@ Assuming that you've installed Far into `c:\Program Files\Far Manager`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Far",
|
||||
"commandline": "\"c:\\program files\\far manager\\far.exe\"",
|
||||
"startingDirectory": "%USERPROFILE%",
|
||||
"useAcrylic": false
|
||||
"name" : "Far",
|
||||
"commandline" : "\"c:\\program files\\far manager\\far.exe\"",
|
||||
"startingDirectory" : "%USERPROFILE%",
|
||||
"useAcrylic" : false
|
||||
},
|
||||
```
|
||||
|
||||
## Git Bash
|
||||
|
||||
Assuming that you've installed Git Bash into `C:\\Program Files\\Git`:
|
||||
Assuming that you've installed Git Bash into `C:/Program Files/Git`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Git Bash",
|
||||
"commandline": "C:\\Program Files\\Git\\bin\\bash.exe -li",
|
||||
"icon": "C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico",
|
||||
"startingDirectory": "%USERPROFILE%"
|
||||
"name" : "Git Bash",
|
||||
"commandline" : "C:/Program Files/Git/bin/bash.exe -li",
|
||||
"icon" : "C:/Program Files/Git/mingw64/share/git/git-for-windows.ico",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
````
|
||||
|
||||
## Git Bash (WOW64)
|
||||
|
||||
Assuming that you've installed Git Bash into `C:\\Program Files (x86)\\Git`:
|
||||
Assuming that you've installed Git Bash into `C:/Program Files (x86)/Git`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Git Bash",
|
||||
"commandline": "%ProgramFiles(x86)%\\Git\\bin\\bash.exe -li",
|
||||
"icon": "%ProgramFiles(x86)%\\Git\\mingw32\\share\\git\\git-for-windows.ico",
|
||||
"startingDirectory": "%USERPROFILE%"
|
||||
"name" : "Git Bash",
|
||||
"commandline" : "%ProgramFiles(x86)%/Git/bin/bash.exe -li",
|
||||
"icon" : "%ProgramFiles(x86)%/Git/mingw32/share/git/git-for-windows.ico",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
|
||||
## MSYS2
|
||||
|
||||
Assuming that you've installed MSYS2 into `C:\\msys64`:
|
||||
Assuming that you've installed MSYS2 into `C:/msys64`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "MSYS2",
|
||||
"commandline": "C:\\msys64\\msys2_shell.cmd -defterm -no-start -mingw64",
|
||||
"icon": "C:\\msys64\\msys2.ico",
|
||||
"startingDirectory": "C:\\msys64\\home\\user"
|
||||
"name" : "MSYS2",
|
||||
"commandline" : "C:/msys64/msys2_shell.cmd -defterm -no-start -mingw64",
|
||||
"icon": "C:/msys64/msys2.ico",
|
||||
"startingDirectory" : "C:/msys64/home/user"
|
||||
}
|
||||
````
|
||||
|
||||
@@ -104,9 +110,9 @@ Assuming that you've installed VS 2019 Professional:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Developer Command Prompt for VS 2019",
|
||||
"commandline": "cmd.exe /k \"C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/Common7/Tools/VsDevCmd.bat\"",
|
||||
"startingDirectory": "%USERPROFILE%"
|
||||
"name" : "Developer Command Prompt for VS 2019",
|
||||
"commandline" : "cmd.exe /k \"C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/Common7/Tools/VsDevCmd.bat\"",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1 +1,201 @@
|
||||
⚠ This document has moved to [Using command-line arguments for Windows Terminal](https://docs.microsoft.com/windows/terminal/command-line-arguments).
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2020-01-16
|
||||
last updated: 2020-01-17
|
||||
---
|
||||
|
||||
# Using the `wt.exe` Commandline
|
||||
|
||||
As of [#4023], the Windows Terminal now supports accepting arguments on the
|
||||
commandline, to enable launching the Terminal in a non-default configuration.
|
||||
This document serves as a reference for all the parameters you can currently
|
||||
pass, and gives some examples of how to use the `wt` commandline.
|
||||
|
||||
> NOTE: If you're running the Terminal built straight from the repo, you'll need
|
||||
> to use `wtd.exe` and `wtd` instead of `wt.exe` and `wt`.
|
||||
|
||||
1. [Commandline Reference](#Reference)
|
||||
1. [Commandline Examples](#Examples)
|
||||
|
||||
## Reference
|
||||
|
||||
### Options
|
||||
|
||||
#### `--help,-h,-?,/?,`
|
||||
|
||||
Display the help message.
|
||||
|
||||
## Subcommands
|
||||
|
||||
#### `new-tab`
|
||||
|
||||
`new-tab [terminal_parameters]`
|
||||
|
||||
Opens a new tab with the given customizations. On its _first_ invocation, also
|
||||
opens a new window. Subsequent `new-tab` commands will all open new tabs in the
|
||||
same window. <sup>[[1](#footnote-1)]</sup>
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
|
||||
|
||||
#### `split-pane`
|
||||
|
||||
`split-pane [-H,--horizontal|-V,--vertical] [terminal_parameters]`
|
||||
|
||||
Creates a new pane in the currently focused tab by splitting the given pane
|
||||
vertically or horizontally. <sup>[[1](#footnote-1)]</sup>
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `-H,--horizontal`, `-V,--vertical`: Used to indicate which direction to split
|
||||
the pane. `-V` is "vertically" (think `[|]`), and `-H` is "horizontally"
|
||||
(think `[-]`). If omitted, defaults to "auto", which splits the current pane
|
||||
in whatever the larger dimension is. If both `-H` and `-V` are provided,
|
||||
defaults to vertical.
|
||||
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
|
||||
|
||||
#### `focus-tab`
|
||||
|
||||
`focus-tab [--target,-t tab-index]|[--next,-n]|[--previous,-p]`
|
||||
|
||||
Moves focus to a given tab.
|
||||
|
||||
**Parameters**:
|
||||
|
||||
* `--target,-t tab-index`: moves focus to the tab at index `tab-index`. If
|
||||
omitted, defaults to `0` (the first tab). Will display an error if combined
|
||||
with either of `--next` or `--previous`.
|
||||
* `-n,--next`: Move focus to the next tab. Will display an error if combined
|
||||
with either of `--previous` or `--target`.
|
||||
* `-p,--previous`: Move focus to the previous tab. Will display an error if
|
||||
combined with either of `--next` or `--target`.
|
||||
|
||||
#### `[terminal_parameters]`
|
||||
|
||||
Some of the preceding commands are used to create a new terminal instance.
|
||||
These commands are listed above as accepting `[terminal_parameters]` as a
|
||||
parameter. For these commands, `[terminal_parameters]` can be any of the
|
||||
following:
|
||||
|
||||
`[--profile,-p profile-name] [--startingDirectory,-d starting-directory] [commandline]`
|
||||
|
||||
* `--profile,-p profile-name`: Use the given profile to open the new tab/pane,
|
||||
where `profile-name` is the `name` or `guid` of a profile. If `profile-name`
|
||||
does not match _any_ profiles, uses the default.
|
||||
* `--startingDirectory,-d starting-directory`: Overrides the value of
|
||||
`startingDirectory` of the specified profile, to start in `starting-directory`
|
||||
instead.
|
||||
* `commandline`: A commandline to replace the default commandline of the
|
||||
selected profile. If the user wants to use a `;` in this commandline, it
|
||||
should be escaped as `\;`.
|
||||
|
||||
### Notes
|
||||
|
||||
* <span id="footnote-1"></span> [1]: If you try to run a `wt` commandline while running in a Windows Terminal window, the commandline will _always_ create a new window by default. Being able to run `wt` commandlines in the _current_ window is planned in the future - for more information, refer to [#4472].
|
||||
|
||||
## Examples
|
||||
|
||||
### Open Windows Terminal in the current directory
|
||||
|
||||
```powershell
|
||||
wt -d .
|
||||
```
|
||||
|
||||
This will launch a new Windows Terminal window in the current working directory.
|
||||
It will use your default profile, but instead of using the `startingDirectory`
|
||||
property from that it will use the current path. This is especially useful for
|
||||
launching the Windows Terminal in a directory you currently have open in an
|
||||
`explorer.exe` window.
|
||||
|
||||
### Opening with multiple panes
|
||||
|
||||
If you want to open with multiple panes in the same tab all at once, you can use
|
||||
the `split-pane` command to create new panes.
|
||||
|
||||
Consider the following commandline:
|
||||
|
||||
```powershell
|
||||
wt ; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe
|
||||
```
|
||||
|
||||
This creates a new Windows Terminal window with one tab, and 3 panes:
|
||||
|
||||
* `wt`: Creates the new tab with the default profile
|
||||
* `split-pane -p "Windows PowerShell"`: This will create a new pane, split from
|
||||
the parent with the default profile. This pane will open with the "Windows
|
||||
PowerShell" profile
|
||||
* `split-pane -H wsl.exe`: This will create a third pane, split _horizontally_
|
||||
from the "Windows PowerShell" pane. It will be running the default profile,
|
||||
and will use `wsl.exe` as the commandline (instead of the default profile's
|
||||
`commandline`).
|
||||
|
||||
|
||||
### Using multiple commands from PowerShell
|
||||
|
||||
The Windows Terminal uses the semicolon character `;` as a delimiter for
|
||||
separating subcommands in the `wt` commandline. Unfortunately, `powershell` also
|
||||
uses `;` as a command separator. To work around this you can use the following
|
||||
tricks to help run multiple wt sub commands from powershell. In all the
|
||||
following examples, we'll be creating a new Terminal window with three panes -
|
||||
one running `cmd`, one with `powershell`, and a last one running `wsl`.
|
||||
|
||||
In each of the following examples, we're using the `Start-Process` command to
|
||||
run `wt`. For more information on why we're using `Start-Process`, see ["Using
|
||||
`start`"](#using-start) below.
|
||||
|
||||
#### Single quoted parameters (if you aren't calculating anything):
|
||||
|
||||
In this example, we'll wrap all the parameters to `wt` in single quotes (`'`)
|
||||
|
||||
```PowerShell
|
||||
start wt 'new-tab "cmd"; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe'
|
||||
```
|
||||
|
||||
#### Escaped quotes (if you need variables):
|
||||
|
||||
If you'd like to pass a value contained in a variable to the `wt` commandline,
|
||||
instead use the following syntax:
|
||||
|
||||
```PowerShell
|
||||
$ThirdPane = "wsl.exe"
|
||||
start wt "new-tab cmd; split-pane -p `"Windows PowerShell`" ; split-pane -H $ThirdPane"
|
||||
```
|
||||
|
||||
Note the usage of `` ` `` to escape the double-quotes (`"`) around "Windows
|
||||
Powershell" in the `-p` parameter to the `split-pane` sub-command.
|
||||
|
||||
#### Using `start`
|
||||
|
||||
All the above examples explicitly used `start` to launch the Terminal.
|
||||
|
||||
In the following examples, we're going to not use `start` to run the
|
||||
commandline. Instead, we'll try two other methods of escaping the commandline:
|
||||
* Only escaping the semicolons so that `powershell` will ignore them and pass
|
||||
them straight to `wt`.
|
||||
* Using `--%`, so powershell will treat the rest of the commandline as arguments
|
||||
to the application.
|
||||
|
||||
```PowerShell
|
||||
wt new-tab "cmd" `; split-pane -p "Windows PowerShell" `; split-pane -H wsl.exe
|
||||
```
|
||||
|
||||
```Powershell
|
||||
wt --% new-tab cmd ; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe
|
||||
```
|
||||
|
||||
In both these examples, the newly created Windows Terminal window will create
|
||||
the window by correctly parsing all the provided commandline arguments.
|
||||
|
||||
However, these methods are _not_ recommended currently, as Powershell will wait
|
||||
for the newly-created Terminal window to be closed before returning control to
|
||||
Powershell. By default, Powershell will always wait for Windows Store
|
||||
applications (like the Windows Terminal) to close before returning to the
|
||||
prompt. Note that this is different than the behavior of `cmd`, which will return
|
||||
to the prompt immediately. See
|
||||
[Powershell/PowerShell#9970](https://github.com/PowerShell/PowerShell/issues/9970)
|
||||
for more details on this bug.
|
||||
|
||||
|
||||
[#4023]: https://github.com/microsoft/terminal/pull/4023
|
||||
[#4472]: https://github.com/microsoft/terminal/issues/4472
|
||||
|
||||
@@ -1 +1,483 @@
|
||||
⚠ This document has moved to [the Customize Settings section of the Windows Terminal documentation](https://docs.microsoft.com/windows/terminal/customize-settings/global-settings).
|
||||
# Editing Windows Terminal JSON Settings
|
||||
|
||||
One way (currently the only way) to configure Windows Terminal is by editing the
|
||||
`settings.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\settings.json`.
|
||||
|
||||
As of [#2515](https://github.com/microsoft/terminal/pull/2515), the settings are
|
||||
split into _two_ files: a hardcoded `defaults.json`, and `settings.json`, which
|
||||
contains the user settings. Users should only be concerned with the contents of
|
||||
the `settings.json`, which contains their customizations. The `defaults.json`
|
||||
file is only provided as a reference of what the default settings are. For more
|
||||
details on how these two files work, see [Settings
|
||||
Layering](#settings-layering). To view the default settings file, click on the
|
||||
"Settings" button while holding the <kbd>Alt</kbd> key.
|
||||
|
||||
Details of specific settings can be found [here](../cascadia/SettingsSchema.md).
|
||||
A general introduction is provided below.
|
||||
|
||||
The settings are grouped under four headings:
|
||||
|
||||
1. Global: Settings that apply to the whole application e.g. Default profile, initial size etc.
|
||||
2. Key Bindings: Actually a sub field of the global settings, but worth discussing separately
|
||||
3. Profiles: A group of settings to be applied to a tab when it is opened using that profile. E.g. shell to use, cursor shape etc.
|
||||
4. Schemes: Sets of colors for background, text etc. that can be used by profiles
|
||||
|
||||
## Global Settings
|
||||
|
||||
These settings define startup defaults, and application-wide settings that might
|
||||
not affect a particular terminal instance.
|
||||
|
||||
* Theme
|
||||
* Title Bar options
|
||||
* Initial size
|
||||
* Default profile used when the Windows Terminal is started
|
||||
|
||||
Example settings include
|
||||
|
||||
```json
|
||||
{
|
||||
"defaultProfile" : "{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}",
|
||||
"initialCols" : 120,
|
||||
"initialRows" : 50,
|
||||
"theme" : "system",
|
||||
"keybindings" : []
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
These global properties should exist in the root json object.
|
||||
|
||||
## Key Bindings
|
||||
|
||||
This is an array of key chords and shortcuts to invoke various commands.
|
||||
Each command can have more than one key binding.
|
||||
|
||||
> 👉 **Note**: Key bindings is a subfield of the global settings and
|
||||
> key bindings apply to all profiles in the same manner.
|
||||
|
||||
For example, here's a sample of the default keybindings:
|
||||
|
||||
```json
|
||||
{
|
||||
"keybindings":
|
||||
[
|
||||
{ "command": "closePane", "keys": ["ctrl+shift+w"] },
|
||||
{ "command": "copy", "keys": ["ctrl+shift+c"] },
|
||||
{ "command": "newTab", "keys": ["ctrl+shift+t"] },
|
||||
// etc.
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
You can also use a single key chord string as the value of `"keys"`.
|
||||
It will be treated as a chord of length one.
|
||||
This will allow you to simplify the above snippet as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"keybindings":
|
||||
[
|
||||
{ "command": "closePane", "keys": "ctrl+shift+w" },
|
||||
{ "command": "copy", "keys": "ctrl+shift+c" },
|
||||
{ "command": "newTab", "keys": "ctrl+shift+t" },
|
||||
// etc.
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
A list of default key bindings is available [here](https://github.com/microsoft/terminal/blob/master/src/cascadia/TerminalApp/defaults.json#L204).
|
||||
|
||||
### Unbinding keys
|
||||
|
||||
If you ever come across a key binding that you're unhappy with, it's possible to
|
||||
easily change the keybindings. For example, vim uses <kbd>Ctrl+^</kbd> as a
|
||||
binding for "switch to previous buffer", which conflicts with the Terminal's
|
||||
default keybinding for "open a new tab with the sixth profile". If you'd like to
|
||||
unbind that keybinding, and allow the keystroke to fall through to vim, you can
|
||||
add the following to your keybindings:
|
||||
|
||||
```json
|
||||
{
|
||||
"command" : null, "keys" : ["ctrl+shift+6"]
|
||||
},
|
||||
```
|
||||
|
||||
This will _unbind_ <kbd>Ctrl+Shift+6</kbd>, allowing vim to use the keystroke
|
||||
instead of the terminal.
|
||||
|
||||
### Binding multiple keys
|
||||
|
||||
You can have multiple key chords bound to the same action. To do this, simply
|
||||
add multiple bindings for the same action. For example:
|
||||
|
||||
```json
|
||||
"keybindings" :
|
||||
[
|
||||
{ "command": "copy", "keys": "ctrl+shift+c" },
|
||||
{ "command": "copy", "keys": "ctrl+c" },
|
||||
{ "command": "copy", "keys": "enter" }
|
||||
]
|
||||
```
|
||||
|
||||
In this snippet, all three of <kbd>ctrl+shift+c</kbd>, <kbd>ctrl+c</kbd> and <kbd>enter</kbd> are bound to `copy`.
|
||||
|
||||
## Profiles
|
||||
|
||||
A profile contains the settings applied when a new WT tab is opened. Each
|
||||
profile is identified by a GUID and contains a number of other fields.
|
||||
|
||||
> 👉 **Note**: The `guid` property is the unique identifier for a profile. If
|
||||
> multiple profiles all have the same `guid` value, you may see unexpected
|
||||
> behavior.
|
||||
|
||||
* Which command to execute on startup - this can include arguments.
|
||||
* Starting directory
|
||||
* Which color scheme to use (see Schemes below)
|
||||
* Font face and size
|
||||
* Various settings to control appearance. E.g. Opacity, icon, cursor appearance, display name etc.
|
||||
* Other behavioral settings. E.g. Close on exit, snap on input, .....
|
||||
|
||||
Example settings include
|
||||
|
||||
```json
|
||||
"closeOnExit" : true,
|
||||
"colorScheme" : "Campbell",
|
||||
"commandline" : "wsl.exe -d Debian",
|
||||
"cursorColor" : "#FFFFFF",
|
||||
"cursorShape" : "bar",
|
||||
"fontFace" : "Hack",
|
||||
"fontSize" : 9,
|
||||
"guid" : "{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}",
|
||||
"name" : "Debian",
|
||||
"startingDirectory" : "%USERPROFILE%\\wslhome"
|
||||
....
|
||||
```
|
||||
|
||||
> 👉 **Note**: To use backslashes in any path field, you'll need to escape them following JSON escaping rules (like shown above). As an alternative, you can use forward slashes ("%USERPROFILE%/wslhome").
|
||||
|
||||
The profile GUID is used to reference the default profile in the global settings.
|
||||
|
||||
The values for background image stretch mode are documented [here](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.media.stretch).
|
||||
|
||||
### Hiding a profile
|
||||
|
||||
If you want to remove a profile from the list of profiles in the new tab
|
||||
dropdown, but keep the profile around in your `settings.json` file, you can add
|
||||
the property `"hidden": true` to the profile's json. This can also be used to
|
||||
remove the default `cmd` and PowerShell profiles, if the user does not wish to
|
||||
see them.
|
||||
|
||||
## Color Schemes
|
||||
|
||||
Each scheme defines the color values to be used for various terminal escape sequences.
|
||||
Each schema is identified by the name field. Examples include
|
||||
|
||||
```json
|
||||
"name" : "Campbell",
|
||||
"background" : "#0C0C0C",
|
||||
"black" : "#0C0C0C",
|
||||
"blue" : "#0037DA",
|
||||
"foreground" : "#F2F2F2",
|
||||
"green" : "#13A10E",
|
||||
"red" : "#C50F1F",
|
||||
"white" : "#CCCCCC",
|
||||
"yellow" : "#C19C00"
|
||||
...
|
||||
```
|
||||
|
||||
The schema name can then be referenced in one or more profiles.
|
||||
|
||||
## Settings layering
|
||||
|
||||
The runtime settings are actually constructed from _three_ sources:
|
||||
|
||||
* The default settings, which are hardcoded into the application, and available
|
||||
in `defaults.json`. This includes the default keybindings, color schemes, and
|
||||
profiles for both Windows PowerShell and Command Prompt (`cmd.exe`).
|
||||
* Dynamic Profiles, which are generated at runtime. These include Powershell
|
||||
Core, the Azure Cloud Shell connector, and profiles for and WSL distros.
|
||||
* The user settings from `settings.json`.
|
||||
|
||||
Settings from each of these sources are "layered" upon the settings from
|
||||
previous sources. In this manner, the user settings in `settings.json` can
|
||||
contain _only the changes from the default settings_. For example, if a user
|
||||
would like to only change the color scheme of the default `cmd` profile to
|
||||
"Solarized Dark", you could change your cmd profile to the following:
|
||||
|
||||
```js
|
||||
{
|
||||
// Make changes here to the cmd.exe profile
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"colorScheme": "Solarized Dark"
|
||||
}
|
||||
```
|
||||
|
||||
Here, we know we're changing the `cmd` profile, because the `guid`
|
||||
`"{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"` is `cmd`'s unique GUID. Any profiles
|
||||
with that GUID will all be treated as the same object. Any changes in that
|
||||
profile will overwrite those from the defaults.
|
||||
|
||||
Similarly, you can overwrite settings from a color scheme by defining a color
|
||||
scheme in `settings.json` with the same name as a default color scheme.
|
||||
|
||||
If you'd like to unbind a keystroke that's bound to an action in the default
|
||||
keybindings, you can set the `"command"` to `"unbound"` or `null`. This will
|
||||
allow the keystroke to fallthrough to the commandline application instead of
|
||||
performing the default action.
|
||||
|
||||
### Dynamic Profiles
|
||||
|
||||
When dynamic profiles are created at runtime, they'll be added to the
|
||||
`settings.json` file. You can identify these profiles by the presence of a
|
||||
`"source"` property. These profiles are tied to their source - if you uninstall
|
||||
a linux distro, then the profile will remain in your `settings.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:
|
||||
|
||||
```json
|
||||
|
||||
"disabledProfileSources": ["Windows.Terminal.WSL"],
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
> 👉 **NOTE**: On launch, if a dynamic profile generator is enabled, it will
|
||||
> always add new profiles it detects to your list of profiles. If you delete a
|
||||
> dynamically generated profile from your list of profiles, it will just get
|
||||
> re-added the next time the Terminal is launched! To remove a dynamic profile
|
||||
> from your list of profiles, make sure to set `"hidden": true` in the profile.
|
||||
|
||||
### Default settings
|
||||
|
||||
In [#2325](https://github.com/microsoft/terminal/issues/2325), we introduced the
|
||||
concept of "Default Profile Settings". These are settings that will apply to all
|
||||
of your profiles by default. Profiles can still override these settings
|
||||
individually. With default profile settings, you can easily make changes to all
|
||||
your profiles at once. For example, given the following settings:
|
||||
|
||||
```json
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"profiles":
|
||||
[
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe",
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 14
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe",
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 14
|
||||
},
|
||||
{
|
||||
"commandline" : "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
|
||||
"name" : "cmder",
|
||||
"startingDirectory" : "%USERPROFILE%",
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 14
|
||||
}
|
||||
],
|
||||
```
|
||||
|
||||
All three of these profiles are using "Cascadia Code" as their `"fontFace"`, and
|
||||
14 as their `fontSize`. With default profile settings, you can easily set these
|
||||
properties for all your profiles, like so:
|
||||
|
||||
```json
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"profiles": {
|
||||
"defaults":
|
||||
{
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 14
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe"
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe"
|
||||
},
|
||||
{
|
||||
"commandline" : "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
|
||||
"name" : "cmder",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
Note that the `profiles` property has changed in this example from a _list_ of
|
||||
profiles, to an _object_ with two properties:
|
||||
|
||||
* a `list` that contains the list of all the profiles
|
||||
* the new `defaults` object, which contains all the settings that should apply to
|
||||
every profile.
|
||||
|
||||
What if I wanted a profile to have a different value for a property other than
|
||||
the default? Simply set the property in the profile's entry to override the
|
||||
value from `defaults`. Let's say you want the `cmd` profile to have _"Consolas"_
|
||||
as the font, but the rest of your profiles to still have _"Cascadia Code"_. You
|
||||
could achieve that with the following:
|
||||
|
||||
```json
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"profiles": {
|
||||
"defaults":
|
||||
{
|
||||
"fontFace": "Cascadia Code",
|
||||
"fontSize": 14
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"name": "Windows PowerShell",
|
||||
"commandline": "powershell.exe"
|
||||
},
|
||||
{
|
||||
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
|
||||
"name": "cmd",
|
||||
"commandline": "cmd.exe",
|
||||
"fontFace": "Consolas"
|
||||
},
|
||||
{
|
||||
"commandline" : "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
|
||||
"name" : "cmder",
|
||||
"startingDirectory" : "%USERPROFILE%"
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
|
||||
`"fontFace"` from the `defaults`.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Add a custom background to the WSL Debian terminal profile
|
||||
|
||||
1. Download the [Debian JPG logo](https://www.debian.org/logos/openlogo-100.jpg)
|
||||
2. Put the image in the
|
||||
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\LocalState\`
|
||||
directory (same directory as your `settings.json` file).
|
||||
|
||||
__NOTE__: You can put the image anywhere you like, the above suggestion happens to be convenient.
|
||||
3. Open your WT json properties file.
|
||||
4. Under the Debian Linux profile, add the following fields:
|
||||
|
||||
```json
|
||||
"backgroundImage": "ms-appdata:///Local/openlogo-100.jpg",
|
||||
"backgroundImageOpacity": 1,
|
||||
"backgroundImageStretchMode" : "none",
|
||||
"backgroundImageAlignment" : "topRight",
|
||||
```
|
||||
|
||||
5. Make sure that `useAcrylic` is `false`.
|
||||
6. Save the file.
|
||||
7. Jump over to WT and verify your changes.
|
||||
|
||||
Notes:
|
||||
|
||||
1. You will need to experiment with different color settings
|
||||
and schemes to make your terminal text visible on top of your image
|
||||
2. If you store the image in the UWP directory (the same directory as your settings.json file),
|
||||
then you should use the URI style path name given in the above example.
|
||||
More information about UWP URI schemes [here](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes).
|
||||
3. Instead of using a UWP URI you can use a:
|
||||
1. URL such as
|
||||
`http://open.esa.int/files/2017/03/Mayer_and_Bond_craters_seen_by_SMART-1-350x346.jpg`
|
||||
2. Local file location such as `C:\Users\Public\Pictures\openlogo.jpg`
|
||||
|
||||
### Adding Copy and Paste Keybindings
|
||||
|
||||
As of [#1093](https://github.com/microsoft/terminal/pull/1093) (first available
|
||||
in Windows Terminal v0.3), the Windows Terminal now supports copy and paste
|
||||
keyboard shortcuts. However, if you installed and ran the terminal before that,
|
||||
you won't automatically get the new keybindings added to your settings. If you'd
|
||||
like to add shortcuts for copy and paste, you can do so by inserting the
|
||||
following objects into your `globals.keybindings` array:
|
||||
|
||||
```json
|
||||
{ "command": "copy", "keys": ["ctrl+shift+c"] },
|
||||
{ "command": "paste", "keys": ["ctrl+shift+v"] }
|
||||
```
|
||||
|
||||
> 👉 **Note**: you can also add a keybinding for the `copy` command with the argument `"trimWhitespace": true`. 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.
|
||||
|
||||
You can set the keybindings to whatever you'd like. If you prefer
|
||||
<kbd>ctrl+c</kbd> to copy, then set the `keys` to `"ctrl+c"`.
|
||||
|
||||
You can even set multiple keybindings for a single action if you'd like. For example:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"command" : "paste",
|
||||
"keys" :
|
||||
[
|
||||
"ctrl+shift+v"
|
||||
]
|
||||
},
|
||||
{
|
||||
"command" : "paste",
|
||||
"keys" :
|
||||
[
|
||||
"shift+insert"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
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>",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1 +1,91 @@
|
||||
⚠ Our user-facing documentation has moved to the [Windows Terminal documentation page](https://docs.microsoft.com/windows/terminal/).
|
||||
# Windows Terminal User Documentation
|
||||
|
||||
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
|
||||
|
||||
### From Source Code
|
||||
|
||||
To compile Windows Terminal yourself using the source code, follow the instructions in the [README](/README.md#developer-guidance).
|
||||
|
||||
### From the Microsoft Store
|
||||
|
||||
1. Make sure you have upgraded to the current Windows 10 release (at least build `1903`). To determine your build number, see [winver](https://docs.microsoft.com/en-us/windows/client-management/windows-version-search).
|
||||
2. Open the Windows Terminal listing in the [Microsoft Store](https://aka.ms/install-terminal).
|
||||
3. Review the minimum system requirements to confirm you can successfully install Windows Terminal.
|
||||
4. Click `Get` to begin the installation process.
|
||||
|
||||
## 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`.
|
||||
|
||||
NOTE: The default shell is PowerShell; you can change this using the _Running a Different Shell_ procedure.
|
||||
|
||||
### Command line options
|
||||
|
||||
Windows Terminal has implemented a rich set of command-line options in part as response to issue [#607](https://github.com/microsoft/terminal/issues/607). See [UsingCommandlineArguments.md](https://github.com/microsoft/terminal/blob/master/doc/user-docs/UsingCommandlineArguments.md) for details.
|
||||
|
||||
## 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>).
|
||||
|
||||
## Running a Different Shell
|
||||
|
||||
Note: This section assumes you already have _Windows Subsystem for Linux_ (WSL) installed. For more information, see [the installation guide](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
|
||||
|
||||
Windows Terminal uses PowerShell as its default shell. You can also use Windows Terminal to launch other shells, such as `cmd.exe` or WSL's `bash`:
|
||||
|
||||
1. In the tab bar, click the `⌵` button to view the available shells.
|
||||
2. Choose your shell from the dropdown list. The new shell session will open in a new tab.
|
||||
|
||||
To customize the shell list, see the _Configuring Windows Terminal_ section below.
|
||||
|
||||
## Starting a new PowerShell tab with admin privilege
|
||||
|
||||
There is no current plan to support this feature for security reasons. See issue [#632](https://github.com/microsoft/terminal/issues/632)
|
||||
|
||||
## Selecting and Copying Text in Windows Terminal
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
If there is not an active selection, a right-click will paste the text content from your clipboard to the terminal.
|
||||
|
||||
Copy and paste operations can also be keybound. For more information on how to bind keys, see [Using Json Settings](UsingJsonSettings.md#adding-copy-and-paste-keybindings).
|
||||
|
||||
> 👉 **Note**: If you have the `copyOnSelect` global setting enabled, a selection will persist and immediately copy the selected text to your clipboard. Right-clicking will always paste your clipboard data.
|
||||
|
||||
## Add a "Open Windows Terminal Here" to File Explorer
|
||||
|
||||
Not currently supported "out of the box" (See issue [#1060](https://github.com/microsoft/terminal/issues/1060)). However, you can open Windows Terminal in current directory by typing `wt -d .` in the Explorer address bar.
|
||||
|
||||
## Configuring Windows Terminal
|
||||
|
||||
All Windows Terminal settings are currently managed using the `settings.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState`.
|
||||
|
||||
To open the settings file from Windows Terminal:
|
||||
|
||||
1. Click the `⌵` button in the top bar.
|
||||
2. From the dropdown list, click `Settings`. You can also use a shortcut: <kbd>Ctrl</kbd>+<kbd>,</kbd>.
|
||||
3. Your default `json` editor will open the settings file.
|
||||
|
||||
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [settings.json documentation](../cascadia/SettingsSchema.md) section.
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
1. In PowerShell you can discover if the Windows Terminal is being used by checking for the existence of the environment variable `WT_SESSION`.
|
||||
|
||||
Under pwsh you can also use
|
||||
`(Get-Process -Id $pid).Parent.ProcessName -eq 'WindowsTerminal'`
|
||||
|
||||
(ref [https://twitter.com/r_keith_hill/status/1142871145852440576](https://twitter.com/r_keith_hill/status/1142871145852440576))
|
||||
|
||||
2. Terminal zoom can be changed by holding <kbd>Ctrl</kbd> and scrolling with mouse.
|
||||
3. Background opacity can be changed by holding <kbd>Ctrl</kbd>+<kbd>Shift</kbd> and scrolling with mouse. Note that acrylic transparency is limited by the OS only to focused windows.
|
||||
4. Open Windows Terminal in current directory by typing `wt -d .` in the address bar.
|
||||
5. Pin the Windows Terminal to the taskbar. Now it can be launched using the Windows shortcut <kbd>Win</kbd>+<kbd>Number</kbd> (e.g. <kbd>Win</kbd>+<kbd>1</kbd> or any other number based on the position in the taskbar!). Press <kbd>Win</kbd>+<kbd>Shift</kbd>+<kbd>Number</kbd> to always launch a new window.
|
||||
6. Please add more Tips and Tricks.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
### Notes for Future Maintainers
|
||||
|
||||
This manifest anchors our usage of rgb.txt from the X11 distribution.
|
||||
|
||||
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
|
||||
Please update the provenance information in that file when ingesting an updated version of the dependent library.
|
||||
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
|
||||
@@ -1,13 +0,0 @@
|
||||
{"Registrations":[
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://gitlab.freedesktop.org/xorg/app/rgb.git",
|
||||
"commitHash": "97820e748eb496a1f6d3fc3bf89688f0ce1f64f9"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
||||
@@ -175,23 +175,6 @@ size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const
|
||||
return runPos - _list.cbegin();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Finds the hyperlink IDs present in this row and returns them
|
||||
// Return value:
|
||||
// - An unordered set containing the hyperlink IDs present in this row
|
||||
std::unordered_set<uint16_t> ATTR_ROW::GetHyperlinks()
|
||||
{
|
||||
std::unordered_set<uint16_t> ids;
|
||||
for (const auto& run : _list)
|
||||
{
|
||||
if (run.GetAttributes().IsHyperlink())
|
||||
{
|
||||
ids.emplace(run.GetAttributes().GetHyperlinkId());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets the attributes (colors) of all character positions from the given position through the end of the row.
|
||||
// Arguments:
|
||||
|
||||
@@ -41,8 +41,6 @@ public:
|
||||
size_t FindAttrIndex(const size_t index,
|
||||
size_t* const pApplies) const;
|
||||
|
||||
std::unordered_set<uint16_t> GetHyperlinks();
|
||||
|
||||
bool SetAttrToEnd(const UINT iStart, const TextAttribute attr);
|
||||
void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept;
|
||||
|
||||
|
||||
@@ -88,18 +88,16 @@ bool TextAttribute::IsLegacy() const noexcept
|
||||
// - defaultFgColor: the default foreground color rgb value.
|
||||
// - defaultBgColor: the default background color rgb value.
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint.
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode,
|
||||
const bool blinkingIsFaint) const noexcept
|
||||
const bool reverseScreenMode) const noexcept
|
||||
{
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgColor);
|
||||
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
|
||||
if (IsFaint())
|
||||
{
|
||||
fg = (fg >> 1) & 0x7F7F7F; // Divide foreground color components by two.
|
||||
}
|
||||
@@ -114,17 +112,6 @@ std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<
|
||||
return { fg, bg };
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Tells us whether the text is a hyperlink or not
|
||||
// Return value:
|
||||
// - True if it is a hyperlink, false otherwise
|
||||
bool TextAttribute::IsHyperlink() const noexcept
|
||||
{
|
||||
// All non-hyperlink text have a default hyperlinkId of 0 while
|
||||
// all hyperlink text have a non-zero hyperlinkId
|
||||
return _hyperlinkId != 0;
|
||||
}
|
||||
|
||||
TextColor TextAttribute::GetForeground() const noexcept
|
||||
{
|
||||
return _foreground;
|
||||
@@ -135,15 +122,6 @@ TextColor TextAttribute::GetBackground() const noexcept
|
||||
return _background;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Retrieves the hyperlink ID of the text
|
||||
// Return value:
|
||||
// - The hyperlink ID
|
||||
uint16_t TextAttribute::GetHyperlinkId() const noexcept
|
||||
{
|
||||
return _hyperlinkId;
|
||||
}
|
||||
|
||||
void TextAttribute::SetForeground(const TextColor foreground) noexcept
|
||||
{
|
||||
_foreground = foreground;
|
||||
@@ -196,15 +174,6 @@ void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground)
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Sets the hyperlink ID of the text
|
||||
// Arguments:
|
||||
// - id - the id we wish to set
|
||||
void TextAttribute::SetHyperlinkId(uint16_t id) noexcept
|
||||
{
|
||||
_hyperlinkId = id;
|
||||
}
|
||||
|
||||
bool TextAttribute::IsLeadingByte() const noexcept
|
||||
{
|
||||
return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_LEADING_BYTE);
|
||||
@@ -367,14 +336,6 @@ void TextAttribute::SetDefaultBackground() noexcept
|
||||
_background = TextColor();
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Resets only the meta and extended attributes
|
||||
void TextAttribute::SetDefaultMetaAttrs() noexcept
|
||||
{
|
||||
_extendedAttrs = ExtendedAttributes::Normal;
|
||||
_wAttrLegacy = 0;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if this attribute indicates its background is the "default"
|
||||
// background. Its _rgbBackground will contain the actual value of the
|
||||
@@ -395,6 +356,6 @@ bool TextAttribute::BackgroundIsDefault() const noexcept
|
||||
// requires for most erasing and filling operations.
|
||||
void TextAttribute::SetStandardErase() noexcept
|
||||
{
|
||||
SetDefaultMetaAttrs();
|
||||
_hyperlinkId = 0;
|
||||
_extendedAttrs = ExtendedAttributes::Normal;
|
||||
_wAttrLegacy = 0;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ public:
|
||||
_wAttrLegacy{ 0 },
|
||||
_foreground{},
|
||||
_background{},
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -45,8 +44,7 @@ public:
|
||||
_wAttrLegacy{ gsl::narrow_cast<WORD>(wLegacyAttr & META_ATTRS) },
|
||||
_foreground{ s_LegacyIndexOrDefault(wLegacyAttr & FG_ATTRS, s_legacyDefaultForeground) },
|
||||
_background{ s_LegacyIndexOrDefault((wLegacyAttr & BG_ATTRS) >> 4, s_legacyDefaultBackground) },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
{
|
||||
// If we're given lead/trailing byte information with the legacy color, strip it.
|
||||
WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS);
|
||||
@@ -57,8 +55,7 @@ public:
|
||||
_wAttrLegacy{ 0 },
|
||||
_foreground{ rgbForeground },
|
||||
_background{ rgbBackground },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
_extendedAttrs{ ExtendedAttributes::Normal }
|
||||
{
|
||||
}
|
||||
|
||||
@@ -69,8 +66,7 @@ public:
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false,
|
||||
const bool blinkingIsFaint = false) const noexcept;
|
||||
const bool reverseScreenMode = false) const noexcept;
|
||||
|
||||
bool IsLeadingByte() const noexcept;
|
||||
bool IsTrailingByte() const noexcept;
|
||||
@@ -116,11 +112,8 @@ public:
|
||||
|
||||
ExtendedAttributes GetExtendedAttributes() const noexcept;
|
||||
|
||||
bool IsHyperlink() const noexcept;
|
||||
|
||||
TextColor GetForeground() const noexcept;
|
||||
TextColor GetBackground() const noexcept;
|
||||
uint16_t GetHyperlinkId() const noexcept;
|
||||
void SetForeground(const TextColor foreground) noexcept;
|
||||
void SetBackground(const TextColor background) noexcept;
|
||||
void SetForeground(const COLORREF rgbForeground) noexcept;
|
||||
@@ -130,11 +123,9 @@ public:
|
||||
void SetIndexedForeground256(const BYTE fgIndex) noexcept;
|
||||
void SetIndexedBackground256(const BYTE bgIndex) noexcept;
|
||||
void SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept;
|
||||
void SetHyperlinkId(uint16_t id) noexcept;
|
||||
|
||||
void SetDefaultForeground() noexcept;
|
||||
void SetDefaultBackground() noexcept;
|
||||
void SetDefaultMetaAttrs() noexcept;
|
||||
|
||||
bool BackgroundIsDefault() const noexcept;
|
||||
|
||||
@@ -152,14 +143,11 @@ public:
|
||||
return !IsAnyGridLineEnabled() && // grid lines have a visual representation
|
||||
// crossed out, doubly and singly underlined have a visual representation
|
||||
WI_AreAllFlagsClear(_extendedAttrs, ExtendedAttributes::CrossedOut | ExtendedAttributes::DoublyUnderlined | ExtendedAttributes::Underlined) &&
|
||||
// hyperlinks have a visual representation
|
||||
!IsHyperlink() &&
|
||||
// all other attributes do not have a visual representation
|
||||
(_wAttrLegacy & META_ATTRS) == (other._wAttrLegacy & META_ATTRS) &&
|
||||
((checkForeground && _foreground == other._foreground) ||
|
||||
(!checkForeground && _background == other._background)) &&
|
||||
_extendedAttrs == other._extendedAttrs &&
|
||||
IsHyperlink() == other.IsHyperlink();
|
||||
_extendedAttrs == other._extendedAttrs;
|
||||
}
|
||||
|
||||
constexpr bool IsAnyGridLineEnabled() const noexcept
|
||||
@@ -181,8 +169,6 @@ private:
|
||||
TextColor _background;
|
||||
ExtendedAttributes _extendedAttrs;
|
||||
|
||||
uint16_t _hyperlinkId;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
friend class TextAttributeTests;
|
||||
@@ -196,7 +182,7 @@ private:
|
||||
// 4 for _foreground
|
||||
// 4 for _background
|
||||
// 1 for _extendedAttrs
|
||||
static_assert(sizeof(TextAttribute) <= 13 * sizeof(BYTE), "We should only need 13B for an entire TextAttribute. We may need to increment this in the future as we add additional attributes");
|
||||
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
|
||||
{
|
||||
@@ -210,8 +196,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._hyperlinkId == b._hyperlinkId;
|
||||
a._extendedAttrs == b._extendedAttrs;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const TextAttribute& a, const TextAttribute& b) noexcept
|
||||
|
||||
@@ -34,8 +34,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
|
||||
_storage{},
|
||||
_unicodeStorage{},
|
||||
_renderTarget{ renderTarget },
|
||||
_size{},
|
||||
_currentHyperlinkId{ 1 }
|
||||
_size{}
|
||||
{
|
||||
// initialize ROWs
|
||||
for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i)
|
||||
@@ -552,10 +551,7 @@ bool TextBuffer::IncrementCircularBuffer(const bool inVtMode)
|
||||
// to the logical position 0 in the window (cursor coordinates and all other coordinates).
|
||||
_renderTarget.TriggerCircling();
|
||||
|
||||
// Prune hyperlinks to delete obsolete references
|
||||
_PruneHyperlinks();
|
||||
|
||||
// Second, clean out the old "first row" as it will become the "last row" of the buffer after the circle is performed.
|
||||
// First, clean out the old "first row" as it will become the "last row" of the buffer after the circle is performed.
|
||||
auto fillAttributes = _currentAttributes;
|
||||
if (inVtMode)
|
||||
{
|
||||
@@ -996,29 +992,19 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
|
||||
// so the words in the example include ["word ", "other "]
|
||||
// NOTE: the start anchor (this one) is inclusive, whereas the end anchor (GetWordEnd) is exclusive
|
||||
|
||||
#pragma warning(suppress : 26496)
|
||||
// GH#7664: Treat EndExclusive as EndInclusive so
|
||||
// that it actually points to a space in the buffer
|
||||
auto copy{ target };
|
||||
const auto bufferSize{ GetSize() };
|
||||
if (target == bufferSize.Origin())
|
||||
// can't expand left
|
||||
if (target.X == GetSize().Left())
|
||||
{
|
||||
// can't expand left
|
||||
return target;
|
||||
}
|
||||
else if (target == bufferSize.EndExclusive())
|
||||
{
|
||||
// treat EndExclusive as EndInclusive
|
||||
copy = { bufferSize.RightInclusive(), bufferSize.BottomInclusive() };
|
||||
}
|
||||
|
||||
if (accessibilityMode)
|
||||
{
|
||||
return _GetWordStartForAccessibility(copy, wordDelimiters);
|
||||
return _GetWordStartForAccessibility(target, wordDelimiters);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _GetWordStartForSelection(copy, wordDelimiters);
|
||||
return _GetWordStartForSelection(target, wordDelimiters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1118,16 +1104,9 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
|
||||
// so the words in the example include ["word ", "other "]
|
||||
// NOTE: the end anchor (this one) is exclusive, whereas the start anchor (GetWordStart) is inclusive
|
||||
|
||||
// Already at the end. Can't move forward.
|
||||
if (target == GetSize().EndExclusive())
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
if (accessibilityMode)
|
||||
{
|
||||
const auto lastCharPos{ GetLastNonSpaceCharacter() };
|
||||
return _GetWordEndForAccessibility(target, wordDelimiters, lastCharPos);
|
||||
return _GetWordEndForAccessibility(target, wordDelimiters);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1140,20 +1119,13 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
|
||||
// Arguments:
|
||||
// - target - a COORD on the word you are currently on
|
||||
// - wordDelimiters - what characters are we considering for the separation of words
|
||||
// - lastCharPos - the position of the last nonspace character in the text buffer (to improve performance)
|
||||
// Return Value:
|
||||
// - The COORD for the first character of the next readable "word". If no next word, return one past the end of the buffer
|
||||
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const
|
||||
const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
COORD result = target;
|
||||
|
||||
// Check if we're already on/past the last RegularChar
|
||||
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
|
||||
{
|
||||
return bufferSize.EndExclusive();
|
||||
}
|
||||
|
||||
// ignore right boundary. Continue through readable text found
|
||||
while (_GetDelimiterClassAt(result, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
@@ -1163,12 +1135,6 @@ const COORD TextBuffer::_GetWordEndForAccessibility(const COORD target, const st
|
||||
}
|
||||
}
|
||||
|
||||
// we are already on/past the last RegularChar
|
||||
if (bufferSize.CompareInBounds(result, lastCharPos, true) >= 0)
|
||||
{
|
||||
return bufferSize.EndExclusive();
|
||||
}
|
||||
|
||||
// make sure we expand to the beginning of the NEXT word
|
||||
while (_GetDelimiterClassAt(result, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
@@ -1219,46 +1185,6 @@ const COORD TextBuffer::_GetWordEndForSelection(const COORD target, const std::w
|
||||
return result;
|
||||
}
|
||||
|
||||
void TextBuffer::_PruneHyperlinks()
|
||||
{
|
||||
// Check the old first row for hyperlink references
|
||||
// If there are any, search the entire buffer for the same reference
|
||||
// If the buffer does not contain the same reference, we can remove that hyperlink from our map
|
||||
// This way, obsolete hyperlink references are cleared from our hyperlink map instead of hanging around
|
||||
// Get all the hyperlink references in the row we're erasing
|
||||
auto firstRowRefs = _storage.at(_firstRow).GetAttrRow().GetHyperlinks();
|
||||
if (!firstRowRefs.empty())
|
||||
{
|
||||
const auto total = TotalRowCount();
|
||||
// Loop through all the rows in the buffer except the first row -
|
||||
// we have found all hyperlink references in the first row and put them in refs,
|
||||
// now we need to search the rest of the buffer (i.e. all the rows except the first)
|
||||
// to see if those references are anywhere else
|
||||
for (size_t i = 1; i != total; ++i)
|
||||
{
|
||||
const auto nextRowRefs = GetRowByOffset(i).GetAttrRow().GetHyperlinks();
|
||||
for (auto id : nextRowRefs)
|
||||
{
|
||||
if (firstRowRefs.find(id) != firstRowRefs.end())
|
||||
{
|
||||
firstRowRefs.erase(id);
|
||||
}
|
||||
}
|
||||
if (firstRowRefs.empty())
|
||||
{
|
||||
// No more hyperlink references left to search for, terminate early
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now delete obsolete references from our map
|
||||
for (auto hyperlinkReference : firstRowRefs)
|
||||
{
|
||||
RemoveHyperlinkFromMap(hyperlinkReference);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the position of the first character of the next word. This is used for accessibility
|
||||
// Arguments:
|
||||
@@ -1270,16 +1196,38 @@ void TextBuffer::_PruneHyperlinks()
|
||||
// - pos - The COORD for the first character on the "word" (inclusive)
|
||||
bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const
|
||||
{
|
||||
// move to the beginning of the next word
|
||||
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
|
||||
// This is also the inclusive start of the next word.
|
||||
auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, lastCharPos) };
|
||||
auto copy = pos;
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
if (copy == GetSize().EndExclusive())
|
||||
// started on a word, continue until the end of the word
|
||||
while (_GetDelimiterClassAt(copy, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
if (!bufferSize.IncrementInBounds(copy))
|
||||
{
|
||||
// last char in buffer is a RegularChar
|
||||
// thus there is no next word
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// we are already on/past the last RegularChar
|
||||
if (bufferSize.CompareInBounds(copy, lastCharPos) >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// on whitespace, continue until the beginning of the next word
|
||||
while (_GetDelimiterClassAt(copy, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
if (!bufferSize.IncrementInBounds(copy))
|
||||
{
|
||||
// last char in buffer is a DelimiterChar or ControlChar
|
||||
// there is no next word
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// successful move, copy result out
|
||||
pos = copy;
|
||||
return true;
|
||||
}
|
||||
@@ -1294,17 +1242,33 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
|
||||
// - pos - The COORD for the first character on the "word" (inclusive)
|
||||
bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters) const
|
||||
{
|
||||
// move to the beginning of the current word
|
||||
auto copy{ GetWordStart(pos, wordDelimiters, true) };
|
||||
auto copy = pos;
|
||||
auto bufferSize = GetSize();
|
||||
|
||||
if (!GetSize().DecrementInBounds(copy, true))
|
||||
// started on whitespace/delimiter, continue until the end of the previous word
|
||||
while (_GetDelimiterClassAt(copy, wordDelimiters) != DelimiterClass::RegularChar)
|
||||
{
|
||||
// can't move behind current word
|
||||
return false;
|
||||
if (!bufferSize.DecrementInBounds(copy))
|
||||
{
|
||||
// first char in buffer is a DelimiterChar or ControlChar
|
||||
// there is no previous word
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// move to the beginning of the previous word
|
||||
pos = GetWordStart(copy, wordDelimiters, true);
|
||||
// on a word, continue until the beginning of the word
|
||||
while (_GetDelimiterClassAt(copy, wordDelimiters) == DelimiterClass::RegularChar)
|
||||
{
|
||||
if (!bufferSize.DecrementInBounds(copy))
|
||||
{
|
||||
// first char in buffer is a RegularChar
|
||||
// there is no previous word
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// successful move, copy result out
|
||||
pos = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1317,13 +1281,8 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
|
||||
const til::point TextBuffer::GetGlyphStart(const til::point pos) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
if (resultPos == bufferSize.EndExclusive())
|
||||
{
|
||||
bufferSize.DecrementInBounds(resultPos, true);
|
||||
}
|
||||
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
||||
{
|
||||
bufferSize.DecrementInBounds(resultPos, true);
|
||||
@@ -1364,15 +1323,9 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos) const
|
||||
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
const auto bufferSize = GetSize();
|
||||
|
||||
if (resultPos == GetSize().EndExclusive())
|
||||
{
|
||||
// we're already at the end
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to move. If we can't, we're done.
|
||||
const auto bufferSize = GetSize();
|
||||
const bool success = bufferSize.IncrementInBounds(resultPos, allowBottomExclusive);
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
|
||||
{
|
||||
@@ -1387,19 +1340,20 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowBottomExclusive) con
|
||||
// - Update pos to be the beginning of the previous glyph/character. This is used for accessibility
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// - allowBottomExclusive - allow the nonexistent end-of-buffer cell to be encountered
|
||||
// Return Value:
|
||||
// - true, if successfully updated pos. False, if we are unable to move (usually due to a buffer boundary)
|
||||
// - pos - The COORD for the first cell of the previous glyph (inclusive)
|
||||
bool TextBuffer::MoveToPreviousGlyph(til::point& pos) const
|
||||
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, bool allowBottomExclusive) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
|
||||
// try to move. If we can't, we're done.
|
||||
const auto bufferSize = GetSize();
|
||||
const bool success = bufferSize.DecrementInBounds(resultPos, true);
|
||||
const bool success = bufferSize.DecrementInBounds(resultPos, allowBottomExclusive);
|
||||
if (resultPos != bufferSize.EndExclusive() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
|
||||
{
|
||||
bufferSize.DecrementInBounds(resultPos, true);
|
||||
bufferSize.DecrementInBounds(resultPos, allowBottomExclusive);
|
||||
}
|
||||
|
||||
pos = resultPos;
|
||||
@@ -2188,7 +2142,6 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
|
||||
{
|
||||
// Finish copying remaining parameters from the old text buffer to the new one
|
||||
newBuffer.CopyProperties(oldBuffer);
|
||||
newBuffer.CopyHyperlinkMaps(oldBuffer);
|
||||
|
||||
// If we found where to put the cursor while placing characters into the buffer,
|
||||
// just put the cursor there. Otherwise we have to advance manually.
|
||||
@@ -2254,104 +2207,3 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds or updates a hyperlink in our hyperlink table
|
||||
// Arguments:
|
||||
// - The hyperlink URI, the hyperlink id (could be new or old)
|
||||
void TextBuffer::AddHyperlinkToMap(std::wstring_view uri, uint16_t id)
|
||||
{
|
||||
_hyperlinkMap[id] = uri;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves the URI associated with a particular hyperlink ID
|
||||
// Arguments:
|
||||
// - The hyperlink ID
|
||||
// Return Value:
|
||||
// - The URI
|
||||
std::wstring TextBuffer::GetHyperlinkUriFromId(uint16_t id) const
|
||||
{
|
||||
return _hyperlinkMap.at(id);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Provides the hyperlink ID to be assigned as a text attribute, based on the optional custom id provided
|
||||
// Arguments:
|
||||
// - The user-defined id
|
||||
// Return value:
|
||||
// - The internal hyperlink ID
|
||||
uint16_t TextBuffer::GetHyperlinkId(std::wstring_view params)
|
||||
{
|
||||
uint16_t id = 0;
|
||||
if (params.empty())
|
||||
{
|
||||
// no custom id specified, return our internal count
|
||||
id = _currentHyperlinkId;
|
||||
++_currentHyperlinkId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// assign _currentHyperlinkId if the custom id does not already exist
|
||||
const auto result = _hyperlinkCustomIdMap.emplace(params, _currentHyperlinkId);
|
||||
if (result.second)
|
||||
{
|
||||
// the custom id did not already exist
|
||||
++_currentHyperlinkId;
|
||||
}
|
||||
id = (*(result.first)).second;
|
||||
}
|
||||
// _currentHyperlinkId could overflow, make sure its not 0
|
||||
if (_currentHyperlinkId == 0)
|
||||
{
|
||||
++_currentHyperlinkId;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Removes a hyperlink from the hyperlink map and the associated
|
||||
// user defined id from the custom id map (if there is one)
|
||||
// Arguments:
|
||||
// - The ID of the hyperlink to be removed
|
||||
void TextBuffer::RemoveHyperlinkFromMap(uint16_t id)
|
||||
{
|
||||
_hyperlinkMap.erase(id);
|
||||
for (const auto& customIdPair : _hyperlinkCustomIdMap)
|
||||
{
|
||||
if (customIdPair.second == id)
|
||||
{
|
||||
_hyperlinkCustomIdMap.erase(customIdPair.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Obtains the custom ID, if there was one, associated with the
|
||||
// uint16_t id of a hyperlink
|
||||
// Arguments:
|
||||
// - The uint16_t id of the hyperlink
|
||||
// Return Value:
|
||||
// - The custom ID if there was one, empty string otherwise
|
||||
std::wstring TextBuffer::GetCustomIdFromId(uint16_t id) const
|
||||
{
|
||||
for (auto customIdPair : _hyperlinkCustomIdMap)
|
||||
{
|
||||
if (customIdPair.second == id)
|
||||
{
|
||||
return customIdPair.first;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Copies the hyperlink/customID maps of the old buffer into this one
|
||||
// Arguments:
|
||||
// - The other buffer
|
||||
void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other)
|
||||
{
|
||||
_hyperlinkMap = other._hyperlinkMap;
|
||||
_hyperlinkCustomIdMap = other._hyperlinkCustomIdMap;
|
||||
}
|
||||
|
||||
@@ -137,17 +137,10 @@ public:
|
||||
const til::point GetGlyphStart(const til::point pos) const;
|
||||
const til::point GetGlyphEnd(const til::point pos) const;
|
||||
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
||||
bool MoveToPreviousGlyph(til::point& pos) const;
|
||||
bool MoveToPreviousGlyph(til::point& pos, bool allowBottomExclusive = false) const;
|
||||
|
||||
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
|
||||
|
||||
void AddHyperlinkToMap(std::wstring_view uri, uint16_t id);
|
||||
std::wstring GetHyperlinkUriFromId(uint16_t id) const;
|
||||
uint16_t GetHyperlinkId(std::wstring_view params);
|
||||
void RemoveHyperlinkFromMap(uint16_t id);
|
||||
std::wstring GetCustomIdFromId(uint16_t id) const;
|
||||
void CopyHyperlinkMaps(const TextBuffer& OtherBuffer);
|
||||
|
||||
class TextAndColor
|
||||
{
|
||||
public:
|
||||
@@ -195,10 +188,6 @@ private:
|
||||
// storage location for glyphs that can't fit into the buffer normally
|
||||
UnicodeStorage _unicodeStorage;
|
||||
|
||||
std::unordered_map<uint16_t, std::wstring> _hyperlinkMap;
|
||||
std::unordered_map<std::wstring, uint16_t> _hyperlinkCustomIdMap;
|
||||
uint16_t _currentHyperlinkId;
|
||||
|
||||
void _RefreshRowIDs(std::optional<SHORT> newRowWidth);
|
||||
|
||||
Microsoft::Console::Render::IRenderTarget& _renderTarget;
|
||||
@@ -224,11 +213,9 @@ private:
|
||||
const DelimiterClass _GetDelimiterClassAt(const COORD pos, const std::wstring_view wordDelimiters) const;
|
||||
const COORD _GetWordStartForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||
const COORD _GetWordStartForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters, const COORD lastCharPos) const;
|
||||
const COORD _GetWordEndForAccessibility(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||
const COORD _GetWordEndForSelection(const COORD target, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
void _PruneHyperlinks();
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TextBufferTests;
|
||||
friend class UiaTextRangeTests;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<Link>ProfileIcons\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
<!-- Default Settings -->
|
||||
<Content Include="$(OpenConsoleDir)src\cascadia\TerminalSettingsModel\defaults.json">
|
||||
<Content Include="$(OpenConsoleDir)src\cascadia\TerminalApp\defaults.json">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
@@ -1,308 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/ColorScheme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
// an updated TAEF that will let us install framework packages when the test
|
||||
// package is deployed. Until then, these tests won't deploy in CI.
|
||||
|
||||
class ProfileTests : public JsonTestClass
|
||||
{
|
||||
// Use a custom AppxManifest to ensure that we can activate winrt types
|
||||
// from our test. This property will tell taef to manually use this as
|
||||
// the AppxManifest for this test class.
|
||||
// This does not yet work for anything XAML-y. See TabTests.cpp for more
|
||||
// details on that.
|
||||
BEGIN_TEST_CLASS(ProfileTests)
|
||||
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(CanLayerProfile);
|
||||
TEST_METHOD(LayerProfileProperties);
|
||||
TEST_METHOD(LayerProfileIcon);
|
||||
TEST_METHOD(LayerProfilesOnArray);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void ProfileTests::CanLayerProfile()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name" : "profile3"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
|
||||
const auto profile0 = implementation::Profile::FromJson(profile0Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile0->ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_TRUE(profile0->ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile0->ShouldBeLayered(profile3Json));
|
||||
|
||||
const auto profile1 = implementation::Profile::FromJson(profile1Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile0Json));
|
||||
// A profile _can_ be layered with itself, though what's the point?
|
||||
VERIFY_IS_TRUE(profile1->ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile3Json));
|
||||
|
||||
const auto profile3 = implementation::Profile::FromJson(profile3Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile0Json));
|
||||
// A profile _can_ be layered with itself, though what's the point?
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile3Json));
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfileProperties()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010101"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#020202",
|
||||
"startingDirectory": "C:/"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#030303",
|
||||
"selectionBackground": "#020202"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
|
||||
auto profile0 = implementation::Profile::FromJson(profile0Json);
|
||||
VERIFY_IS_NOT_NULL(profile0->Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->Foreground().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->SelectionBackground());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->SelectionBackground().Value() });
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", profile0->Name());
|
||||
|
||||
VERIFY_IS_TRUE(profile0->StartingDirectory().empty());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Layering profile1 on top of profile0"));
|
||||
profile0->LayerJson(profile1Json);
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile0->Foreground().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile1", profile0->Name());
|
||||
|
||||
VERIFY_IS_FALSE(profile0->StartingDirectory().empty());
|
||||
VERIFY_ARE_EQUAL(L"C:/", profile0->StartingDirectory());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Layering profile2 on top of (profile0+profile1)"));
|
||||
profile0->LayerJson(profile2Json);
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Foreground());
|
||||
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile0->Foreground().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->Background());
|
||||
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
|
||||
|
||||
VERIFY_IS_NOT_NULL(profile0->SelectionBackground());
|
||||
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile0->SelectionBackground().Value() });
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile2", profile0->Name());
|
||||
|
||||
VERIFY_IS_FALSE(profile0->StartingDirectory().empty());
|
||||
VERIFY_ARE_EQUAL(L"C:/", profile0->StartingDirectory());
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfileIcon()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": "not-null.png"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": null
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name": "profile3",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": "another-real.png"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
|
||||
auto profile0 = implementation::Profile::FromJson(profile0Json);
|
||||
VERIFY_IS_FALSE(profile0->Icon().empty());
|
||||
VERIFY_ARE_EQUAL(L"not-null.png", profile0->Icon());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that layering an object the key set to null will clear the key"));
|
||||
profile0->LayerJson(profile1Json);
|
||||
VERIFY_IS_TRUE(profile0->Icon().empty());
|
||||
|
||||
profile0->LayerJson(profile2Json);
|
||||
VERIFY_IS_TRUE(profile0->Icon().empty());
|
||||
|
||||
profile0->LayerJson(profile3Json);
|
||||
VERIFY_IS_FALSE(profile0->Icon().empty());
|
||||
VERIFY_ARE_EQUAL(L"another-real.png", profile0->Icon());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that layering an object _without_ the key will not clear the key"));
|
||||
profile0->LayerJson(profile2Json);
|
||||
VERIFY_IS_FALSE(profile0->Icon().empty());
|
||||
VERIFY_ARE_EQUAL(L"another-real.png", profile0->Icon());
|
||||
|
||||
auto profile1 = implementation::Profile::FromJson(profile1Json);
|
||||
VERIFY_IS_TRUE(profile1->Icon().empty());
|
||||
profile1->LayerJson(profile3Json);
|
||||
VERIFY_IS_FALSE(profile1->Icon().empty());
|
||||
VERIFY_ARE_EQUAL(L"another-real.png", profile1->Icon());
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfilesOnArray()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name" : "profile3",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile4String{ R"({
|
||||
"name" : "profile4",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
const auto profile4Json = VerifyParseSucceeded(profile4String);
|
||||
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_profiles.Size());
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
|
||||
settings->_LayerOrCreateProfile(profile0Json);
|
||||
VERIFY_ARE_EQUAL(1u, settings->_profiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
|
||||
settings->_LayerOrCreateProfile(profile1Json);
|
||||
VERIFY_ARE_EQUAL(2u, settings->_profiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
|
||||
settings->_LayerOrCreateProfile(profile2Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile0", settings->_profiles.GetAt(0).Name());
|
||||
|
||||
settings->_LayerOrCreateProfile(profile3Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile3", settings->_profiles.GetAt(0).Name());
|
||||
|
||||
settings->_LayerOrCreateProfile(profile4Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings->_profiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile4", settings->_profiles.GetAt(0).Name());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!-- A note about this project: We're building the test code dll from this
|
||||
project, but it _MUST_ be run in conjunction with the TestHostApp project.
|
||||
TestHostApp actually will build a TestHost executable and packaging bits
|
||||
that we can use to run our tests. We need TestHostApp so that our
|
||||
dependencies, like MUX, can be aggregated correctly, and resources properly
|
||||
combined into a resources.pri file.
|
||||
|
||||
TestHostApp will manually copy the output of this project into it's own
|
||||
OutDir, so we can run the tests from there. -->
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{CA5CAD1A-9B68-456A-B13E-C8218070DC42}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>SettingsModelLocalTests</RootNamespace>
|
||||
<ProjectName>LocalTests_SettingsModel</ProjectName>
|
||||
<TargetName>SettingsModel.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\cppwinrt.build.pre.props" />
|
||||
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="JsonTestClass.h" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ProfileTests.cpp" />
|
||||
<ClCompile Include="ColorSchemeTests.cpp" />
|
||||
<ClCompile Include="KeyBindingsTests.cpp" />
|
||||
<ClCompile Include="CommandTests.cpp" />
|
||||
<ClCompile Include="DeserializationTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<!-- You _NEED_ to include this file and the jsoncpp IncludePath (below) if
|
||||
you want to use jsoncpp -->
|
||||
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\types\lib\types.vcxproj" />
|
||||
|
||||
<!-- If you don't reference these projects here, the
|
||||
_ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's -->
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Globals ======================== -->
|
||||
|
||||
<!-- ====================== Compiler & Linker Flags ===================== -->
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\jsoncpp\json;$(OpenConsoleDir)src\inc;$(OpenConsoleDir)src\inc\test;$(WinRT_IncludePath)\..\cppwinrt\winrt;"$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\Generated Files";%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
|
||||
<!-- Manually disable unreachable code warning, because jconcpp has a ton of that. -->
|
||||
<DisableSpecificWarnings>4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
<EmbedManifest>true</EmbedManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
|
||||
|
||||
<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.5.0-prerelease.200609001\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.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
@@ -1,65 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- precomp.h
|
||||
|
||||
Abstract:
|
||||
- Contains external headers to include in the precompile phase of console build process.
|
||||
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
|
||||
|
||||
Author(s):
|
||||
- Carlos Zamora (cazamor) April 2019
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#include "LibraryIncludes.h"
|
||||
// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||||
// SDK definition of this function, so the only fix is to undef it.
|
||||
// from WinBase.h
|
||||
// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
#include <wil/cppwinrt.h>
|
||||
#include <unknwn.h>
|
||||
#include <hstring.h>
|
||||
|
||||
#include <WexTestClass.h>
|
||||
#include <json.h>
|
||||
#include "consoletaeftemplates.hpp"
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||
#include <winrt/Windows.system.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
#include <winrt/Windows.ui.input.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Windows.ui.xaml.media.h>
|
||||
#include <winrt/Windows.ui.xaml.input.h>
|
||||
#include <winrt/Windows.UI.Xaml.Markup.h>
|
||||
#include <winrt/Windows.UI.Xaml.Documents.h>
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
#include <winrt/windows.applicationmodel.core.h>
|
||||
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
// Common includes for most tests:
|
||||
#include "../../inc/argb.h"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
@@ -3,17 +3,18 @@
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/ColorScheme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../TerminalApp/ColorScheme.h"
|
||||
#include "../TerminalApp/CascadiaSettings.h"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp::implementation;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
@@ -130,11 +131,11 @@ namespace SettingsModelLocalTests
|
||||
const auto scheme2Json = VerifyParseSucceeded(scheme2String);
|
||||
|
||||
auto scheme0 = ColorScheme::FromJson(scheme0Json);
|
||||
VERIFY_ARE_EQUAL(L"scheme0", scheme0->_Name);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 0), scheme0->_SelectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 1), scheme0->_CursorColor);
|
||||
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, 0), scheme0->_selectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 1), scheme0->_cursorColor);
|
||||
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]);
|
||||
@@ -143,10 +144,10 @@ namespace SettingsModelLocalTests
|
||||
L"Layering scheme1 on top of scheme0"));
|
||||
scheme0->LayerJson(scheme1Json);
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 0), scheme0->_SelectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 0, 4), scheme0->_CursorColor);
|
||||
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, 4, 0, 4), scheme0->_cursorColor);
|
||||
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]);
|
||||
@@ -155,10 +156,10 @@ namespace SettingsModelLocalTests
|
||||
L"Layering scheme2Json on top of (scheme0+scheme1)"));
|
||||
scheme0->LayerJson(scheme2Json);
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 0), scheme0->_SelectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 0, 6), scheme0->_CursorColor);
|
||||
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, 6, 0, 6), scheme0->_cursorColor);
|
||||
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]);
|
||||
@@ -192,102 +193,102 @@ namespace SettingsModelLocalTests
|
||||
const auto scheme2Json = VerifyParseSucceeded(scheme2String);
|
||||
const auto scheme3Json = VerifyParseSucceeded(scheme3String);
|
||||
|
||||
auto settings = winrt::make_self<CascadiaSettings>();
|
||||
CascadiaSettings settings;
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_globals->ColorSchemes().Size());
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(0u, settings._globals.GetColorSchemes().size());
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
|
||||
settings->_LayerOrCreateColorScheme(scheme0Json);
|
||||
settings._LayerOrCreateColorScheme(scheme0Json);
|
||||
{
|
||||
for (auto kv : settings->_globals->ColorSchemes())
|
||||
for (auto& kv : settings._globals._colorSchemes)
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"kv:%s->%s", kv.Key().data(), kv.Value().Name().data()));
|
||||
L"kv:%s->%s", kv.first.data(), kv.second.Name().data()));
|
||||
}
|
||||
VERIFY_ARE_EQUAL(1u, settings->_globals->ColorSchemes().Size());
|
||||
VERIFY_ARE_EQUAL(1u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0Proj = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
|
||||
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->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_defaultForeground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_defaultBackground);
|
||||
}
|
||||
|
||||
settings->_LayerOrCreateColorScheme(scheme1Json);
|
||||
settings._LayerOrCreateColorScheme(scheme1Json);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0Proj = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
|
||||
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme1") != settings._globals._colorSchemes.end());
|
||||
auto scheme1Proj = settings._globals._colorSchemes.find(L"scheme1")->second;
|
||||
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
|
||||
|
||||
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->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
|
||||
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);
|
||||
settings._LayerOrCreateColorScheme(scheme2Json);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0Proj = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
|
||||
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme1") != settings._globals._colorSchemes.end());
|
||||
auto scheme1Proj = settings._globals._colorSchemes.find(L"scheme1")->second;
|
||||
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
|
||||
|
||||
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->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
|
||||
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);
|
||||
settings._LayerOrCreateColorScheme(scheme3Json);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(3u, settings->_globals->ColorSchemes().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings._globals.GetColorSchemes().size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme0") != settings._globals._colorSchemes.end());
|
||||
auto scheme0Proj = settings._globals._colorSchemes.find(L"scheme0")->second;
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
|
||||
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"scheme1") != settings._globals._colorSchemes.end());
|
||||
auto scheme1Proj = settings._globals._colorSchemes.find(L"scheme1")->second;
|
||||
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L""));
|
||||
auto scheme2Proj = settings->_globals->ColorSchemes().Lookup(L"");
|
||||
VERIFY_IS_TRUE(settings._globals._colorSchemes.find(L"") != settings._globals._colorSchemes.end());
|
||||
auto scheme2Proj = settings._globals._colorSchemes.find(L"")->second;
|
||||
auto scheme2 = winrt::get_self<ColorScheme>(scheme2Proj);
|
||||
|
||||
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->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 6, 6), scheme2->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 7, 7, 7), scheme2->_Background);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,20 @@
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../TerminalApp/CascadiaSettings.h"
|
||||
#include "JsonTestClass.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
@@ -61,7 +62,7 @@ namespace SettingsModelLocalTests
|
||||
const auto commands1Json = VerifyParseSucceeded(commands1String);
|
||||
const auto commands2Json = VerifyParseSucceeded(commands2String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
IMap<winrt::hstring, winrt::TerminalApp::Command> commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
{
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
@@ -95,7 +96,7 @@ namespace SettingsModelLocalTests
|
||||
const auto commands2Json = VerifyParseSucceeded(commands2String);
|
||||
const auto commands3Json = VerifyParseSucceeded(commands3String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
IMap<winrt::hstring, winrt::TerminalApp::Command> commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
{
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
@@ -153,7 +154,7 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
IMap<winrt::hstring, winrt::TerminalApp::Command> commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
@@ -167,7 +168,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command1");
|
||||
@@ -177,7 +178,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command2");
|
||||
@@ -187,7 +188,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command4");
|
||||
@@ -197,7 +198,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"command5");
|
||||
@@ -207,7 +208,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
void CommandTests::TestResourceKeyName()
|
||||
@@ -217,7 +218,7 @@ namespace SettingsModelLocalTests
|
||||
const std::string commands0String{ R"([ { "name": { "key": "DuplicateTabCommandKey"}, "command": "copy" } ])" };
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
IMap<winrt::hstring, winrt::TerminalApp::Command> commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
{
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
@@ -265,7 +266,7 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
IMap<winrt::hstring, winrt::TerminalApp::Command> commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
@@ -283,7 +284,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"Split pane, split: vertical");
|
||||
@@ -293,7 +294,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.Lookup(L"Split pane, split: horizontal");
|
||||
@@ -303,7 +304,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
void CommandTests::TestLayerOnAutogeneratedName()
|
||||
@@ -315,7 +316,7 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
||||
IMap<winrt::hstring, Command> commands = winrt::single_threaded_map<winrt::hstring, Command>();
|
||||
IMap<winrt::hstring, winrt::TerminalApp::Command> commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
@@ -329,7 +330,7 @@ namespace SettingsModelLocalTests
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
#include "../TerminalApp/TerminalPage.h"
|
||||
#include "../TerminalApp/AppCommandlineArgs.h"
|
||||
#include "../TerminalApp/ActionArgs.h"
|
||||
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
@@ -57,7 +57,6 @@ namespace TerminalAppLocalTests
|
||||
TEST_METHOD(TestSimpleExecuteCommandlineAction);
|
||||
TEST_METHOD(TestMultipleCommandExecuteCommandlineAction);
|
||||
TEST_METHOD(TestInvalidExecuteCommandlineAction);
|
||||
TEST_METHOD(TestLaunchMode);
|
||||
|
||||
private:
|
||||
void _buildCommandlinesHelper(AppCommandlineArgs& appArgs,
|
||||
@@ -1077,8 +1076,9 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void CommandlineTest::TestSimpleExecuteCommandlineAction()
|
||||
{
|
||||
ExecuteCommandlineArgs args{ L"new-tab" };
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(args);
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
args->Commandline(L"new-tab");
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(*args);
|
||||
VERIFY_ARE_EQUAL(1u, actions.size());
|
||||
auto actionAndArgs = actions.at(0);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
@@ -1095,8 +1095,9 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void CommandlineTest::TestMultipleCommandExecuteCommandlineAction()
|
||||
{
|
||||
ExecuteCommandlineArgs args{ L"new-tab ; split-pane" };
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(args);
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
args->Commandline(L"new-tab ; split-pane");
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(*args);
|
||||
VERIFY_ARE_EQUAL(2u, actions.size());
|
||||
{
|
||||
auto actionAndArgs = actions.at(0);
|
||||
@@ -1128,100 +1129,10 @@ namespace TerminalAppLocalTests
|
||||
|
||||
void CommandlineTest::TestInvalidExecuteCommandlineAction()
|
||||
{
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
// -H and -V cannot be combined.
|
||||
ExecuteCommandlineArgs args{ L"split-pane -H -V" };
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(args);
|
||||
args->Commandline(L"split-pane -H -V");
|
||||
auto actions = implementation::TerminalPage::ConvertExecuteCommandlineToActions(*args);
|
||||
VERIFY_ARE_EQUAL(0u, actions.size());
|
||||
}
|
||||
|
||||
void CommandlineTest::TestLaunchMode()
|
||||
{
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_FALSE(appArgs.GetLaunchMode().has_value());
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"-F" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::FullscreenMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"--fullscreen" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::FullscreenMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"-M" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::MaximizedMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"--maximized" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::MaximizedMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"-f" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::FocusMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"--focus" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::FocusMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"-fM" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::MaximizedFocusMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"--maximized", L"--focus" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::MaximizedFocusMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"--maximized", L"--focus", L"--focus" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::MaximizedFocusMode);
|
||||
}
|
||||
{
|
||||
AppCommandlineArgs appArgs{};
|
||||
std::vector<const wchar_t*> rawCommands{ L"wt.exe", L"--maximized", L"--focus", L"--maximized" };
|
||||
_buildCommandlinesHelper(appArgs, 1u, rawCommands);
|
||||
|
||||
VERIFY_IS_TRUE(appArgs.GetLaunchMode().has_value());
|
||||
VERIFY_ARE_EQUAL(appArgs.GetLaunchMode().value(), LaunchMode::MaximizedFocusMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/ColorScheme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../TerminalSettingsModel/KeyMapping.h"
|
||||
#include "../TerminalApp/ColorScheme.h"
|
||||
#include "../TerminalApp/CascadiaSettings.h"
|
||||
#include "JsonTestClass.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace SettingsModelLocalTests
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
@@ -66,18 +66,18 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
auto appKeyBindings = winrt::make_self<winrt::TerminalApp::implementation::AppKeyBindings>();
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(2u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(4u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(4u, appKeyBindings->_keyShortcuts.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::LayerKeybindings()
|
||||
@@ -90,18 +90,18 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings1Json = VerifyParseSucceeded(bindings1String);
|
||||
const auto bindings2Json = VerifyParseSucceeded(bindings2String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
auto appKeyBindings = winrt::make_self<winrt::TerminalApp::implementation::AppKeyBindings>();
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(2u, appKeyBindings->_keyShortcuts.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::UnbindKeybindings()
|
||||
@@ -120,52 +120,52 @@ namespace SettingsModelLocalTests
|
||||
const auto bindings4Json = VerifyParseSucceeded(bindings4String);
|
||||
const auto bindings5Json = VerifyParseSucceeded(bindings5String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
auto appKeyBindings = winrt::make_self<winrt::TerminalApp::implementation::AppKeyBindings>();
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
keymap->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings1Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using `\"unbound\"` to unbind the key"));
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using `null` to unbind the key"));
|
||||
// First add back a good binding
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
// Then try layering in the bad setting
|
||||
keymap->LayerJson(bindings3Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings3Json);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using an unrecognized command to unbind the key"));
|
||||
// First add back a good binding
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
// Then try layering in the bad setting
|
||||
keymap->LayerJson(bindings4Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings4Json);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key using a straight up invalid value to unbind the key"));
|
||||
// First add back a good binding
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
// Then try layering in the bad setting
|
||||
keymap->LayerJson(bindings5Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings5Json);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Try unbinding a key that wasn't bound at all"));
|
||||
keymap->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings2Json);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestArbitraryArgs()
|
||||
@@ -189,17 +189,17 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(10u, keymap->_keyShortcuts.size());
|
||||
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(10u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `copy` without args parses as Copy(SingleLine=false)"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
@@ -210,7 +210,7 @@ namespace SettingsModelLocalTests
|
||||
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 = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
@@ -221,7 +221,7 @@ namespace SettingsModelLocalTests
|
||||
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 = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
@@ -232,7 +232,7 @@ namespace SettingsModelLocalTests
|
||||
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 = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -244,7 +244,7 @@ namespace SettingsModelLocalTests
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `newTab` parses args correctly"));
|
||||
KeyChord kc{ true, false, true, static_cast<int32_t>('T') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -258,7 +258,7 @@ namespace SettingsModelLocalTests
|
||||
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 = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<NewTabArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -272,7 +272,7 @@ namespace SettingsModelLocalTests
|
||||
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 = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -284,7 +284,7 @@ namespace SettingsModelLocalTests
|
||||
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 = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::CopyText, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -296,7 +296,7 @@ namespace SettingsModelLocalTests
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `adjustFontSize` with a positive delta parses args correctly"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -308,7 +308,7 @@ namespace SettingsModelLocalTests
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that `adjustFontSize` with a negative delta parses args correctly"));
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::AdjustFontSize, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<AdjustFontSizeArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -329,56 +329,56 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(5u, keymap->_keyShortcuts.size());
|
||||
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(5u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Vertical, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('H') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle());
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,15 +392,15 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(3u, keymap->_keyShortcuts.size());
|
||||
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(3u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -409,7 +409,7 @@ namespace SettingsModelLocalTests
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -420,7 +420,7 @@ namespace SettingsModelLocalTests
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
@@ -437,15 +437,15 @@ namespace SettingsModelLocalTests
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
|
||||
auto keymap = winrt::make_self<implementation::KeyMapping>();
|
||||
VERIFY_IS_NOT_NULL(keymap);
|
||||
VERIFY_ARE_EQUAL(0u, keymap->_keyShortcuts.size());
|
||||
keymap->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, keymap->_keyShortcuts.size());
|
||||
auto appKeyBindings = winrt::make_self<implementation::AppKeyBindings>();
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
auto actionAndArgs = ::TestUtils::GetActionAndArgs(*keymap, kc);
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
308
src/cascadia/LocalTests_TerminalApp/ProfileTests.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalApp/ColorScheme.h"
|
||||
#include "../TerminalApp/CascadiaSettings.h"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace TerminalApp;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
// TODO:microsoft/terminal#3838:
|
||||
// Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for
|
||||
// an updated TAEF that will let us install framework packages when the test
|
||||
// package is deployed. Until then, these tests won't deploy in CI.
|
||||
|
||||
class ProfileTests : public JsonTestClass
|
||||
{
|
||||
// Use a custom AppxManifest to ensure that we can activate winrt types
|
||||
// from our test. This property will tell taef to manually use this as
|
||||
// the AppxManifest for this test class.
|
||||
// This does not yet work for anything XAML-y. See TabTests.cpp for more
|
||||
// details on that.
|
||||
BEGIN_TEST_CLASS(ProfileTests)
|
||||
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(CanLayerProfile);
|
||||
TEST_METHOD(LayerProfileProperties);
|
||||
TEST_METHOD(LayerProfileIcon);
|
||||
TEST_METHOD(LayerProfilesOnArray);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void ProfileTests::CanLayerProfile()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name" : "profile3"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
|
||||
const auto profile0 = Profile::FromJson(profile0Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile0.ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_TRUE(profile0.ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile0.ShouldBeLayered(profile3Json));
|
||||
|
||||
const auto profile1 = Profile::FromJson(profile1Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile1.ShouldBeLayered(profile0Json));
|
||||
// A profile _can_ be layered with itself, though what's the point?
|
||||
VERIFY_IS_TRUE(profile1.ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_FALSE(profile1.ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile1.ShouldBeLayered(profile3Json));
|
||||
|
||||
const auto profile3 = Profile::FromJson(profile3Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile3.ShouldBeLayered(profile0Json));
|
||||
// A profile _can_ be layered with itself, though what's the point?
|
||||
VERIFY_IS_FALSE(profile3.ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_FALSE(profile3.ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile3.ShouldBeLayered(profile3Json));
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfileProperties()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010101"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#020202",
|
||||
"startingDirectory": "C:/"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#030303",
|
||||
"selectionBackground": "#020202"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
|
||||
auto profile0 = Profile::FromJson(profile0Json);
|
||||
VERIFY_IS_TRUE(profile0._defaultForeground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), profile0._defaultForeground.value());
|
||||
|
||||
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());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Layering profile1 on top of profile0"));
|
||||
profile0.LayerJson(profile1Json);
|
||||
|
||||
VERIFY_IS_TRUE(profile0._defaultForeground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), profile0._defaultForeground.value());
|
||||
|
||||
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());
|
||||
VERIFY_ARE_EQUAL(L"C:/", profile0._startingDirectory.value());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Layering profile2 on top of (profile0+profile1)"));
|
||||
profile0.LayerJson(profile2Json);
|
||||
|
||||
VERIFY_IS_TRUE(profile0._defaultForeground.has_value());
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), profile0._defaultForeground.value());
|
||||
|
||||
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());
|
||||
VERIFY_ARE_EQUAL(L"C:/", profile0._startingDirectory.value());
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfileIcon()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": "not-null.png"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": null
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name": "profile3",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": "another-real.png"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
|
||||
auto profile0 = Profile::FromJson(profile0Json);
|
||||
VERIFY_IS_TRUE(profile0._icon.has_value());
|
||||
VERIFY_ARE_EQUAL(L"not-null.png", profile0._icon.value());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that layering an object the key set to null will clear the key"));
|
||||
profile0.LayerJson(profile1Json);
|
||||
VERIFY_IS_FALSE(profile0._icon.has_value());
|
||||
|
||||
profile0.LayerJson(profile2Json);
|
||||
VERIFY_IS_FALSE(profile0._icon.has_value());
|
||||
|
||||
profile0.LayerJson(profile3Json);
|
||||
VERIFY_IS_TRUE(profile0._icon.has_value());
|
||||
VERIFY_ARE_EQUAL(L"another-real.png", profile0._icon.value());
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Verify that layering an object _without_ the key will not clear the key"));
|
||||
profile0.LayerJson(profile2Json);
|
||||
VERIFY_IS_TRUE(profile0._icon.has_value());
|
||||
VERIFY_ARE_EQUAL(L"another-real.png", profile0._icon.value());
|
||||
|
||||
auto profile1 = Profile::FromJson(profile1Json);
|
||||
VERIFY_IS_FALSE(profile1._icon.has_value());
|
||||
profile1.LayerJson(profile3Json);
|
||||
VERIFY_IS_TRUE(profile1._icon.has_value());
|
||||
VERIFY_ARE_EQUAL(L"another-real.png", profile1._icon.value());
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfilesOnArray()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name" : "profile3",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile4String{ R"({
|
||||
"name" : "profile4",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
const auto profile4Json = VerifyParseSucceeded(profile4String);
|
||||
|
||||
CascadiaSettings settings;
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings._profiles.size());
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile4Json));
|
||||
|
||||
settings._LayerOrCreateProfile(profile0Json);
|
||||
VERIFY_ARE_EQUAL(1u, settings._profiles.size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile4Json));
|
||||
|
||||
settings._LayerOrCreateProfile(profile1Json);
|
||||
VERIFY_ARE_EQUAL(2u, settings._profiles.size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings._FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile4Json));
|
||||
|
||||
settings._LayerOrCreateProfile(profile2Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings._profiles.size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile0", settings._profiles.at(0)._name);
|
||||
|
||||
settings._LayerOrCreateProfile(profile3Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings._profiles.size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile3", settings._profiles.at(0)._name);
|
||||
|
||||
settings._LayerOrCreateProfile(profile4Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings._profiles.size());
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings._FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile4", settings._profiles.at(0)._name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "../TerminalApp/ShortcutActionDispatch.h"
|
||||
#include "../TerminalApp/Tab.h"
|
||||
#include "../CppWinrtTailored.h"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
@@ -26,7 +26,7 @@ namespace TerminalAppLocalTests
|
||||
// an updated TAEF that will let us install framework packages when the test
|
||||
// package is deployed. Until then, these tests won't deploy in CI.
|
||||
|
||||
class TabTests
|
||||
class TabTests : public JsonTestClass
|
||||
{
|
||||
// For this set of tests, we need to activate some XAML content. For
|
||||
// release builds, the application runs as a centennial application,
|
||||
@@ -64,6 +64,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
private:
|
||||
void _initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
|
||||
CascadiaSettings initialSettings);
|
||||
std::shared_ptr<CascadiaSettings> initialSettings);
|
||||
};
|
||||
|
||||
void TabTests::EnsureTestsActivate()
|
||||
@@ -189,7 +190,7 @@ namespace TerminalAppLocalTests
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TabTests::_initializeTerminalPage(winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage>& page,
|
||||
CascadiaSettings initialSettings)
|
||||
std::shared_ptr<CascadiaSettings> initialSettings)
|
||||
{
|
||||
// This is super wacky, but we can't just initialize the
|
||||
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
||||
@@ -275,8 +276,12 @@ namespace TerminalAppLocalTests
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
VerifyParseSucceeded(settingsJson0);
|
||||
auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
settings0->_ParseJsonString(settingsJson0, false);
|
||||
settings0->LayerJson(settings0->_userSettings);
|
||||
settings0->_ValidateSettings();
|
||||
|
||||
// This is super wacky, but we can't just initialize the
|
||||
// com_ptr<impl::TerminalPage> in the lambda and assign it back out of
|
||||
@@ -333,11 +338,19 @@ namespace TerminalAppLocalTests
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
VerifyParseSucceeded(settingsJson0);
|
||||
auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
settings0->_ParseJsonString(settingsJson0, false);
|
||||
settings0->LayerJson(settings0->_userSettings);
|
||||
settings0->_ValidateSettings();
|
||||
|
||||
CascadiaSettings settings1{ til::u8u16(settingsJson1) };
|
||||
VerifyParseSucceeded(settingsJson1);
|
||||
auto settings1 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings1);
|
||||
settings1->_ParseJsonString(settingsJson1, false);
|
||||
settings1->LayerJson(settings1->_userSettings);
|
||||
settings1->_ValidateSettings();
|
||||
|
||||
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}");
|
||||
@@ -420,11 +433,19 @@ namespace TerminalAppLocalTests
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
VerifyParseSucceeded(settingsJson0);
|
||||
auto settings0 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
settings0->_ParseJsonString(settingsJson0, false);
|
||||
settings0->LayerJson(settings0->_userSettings);
|
||||
settings0->_ValidateSettings();
|
||||
|
||||
CascadiaSettings settings1{ til::u8u16(settingsJson1) };
|
||||
VerifyParseSucceeded(settingsJson1);
|
||||
auto settings1 = std::make_shared<CascadiaSettings>(false);
|
||||
VERIFY_IS_NOT_NULL(settings1);
|
||||
settings1->_ParseJsonString(settingsJson1, false);
|
||||
settings1->LayerJson(settings1->_userSettings);
|
||||
settings1->_ValidateSettings();
|
||||
|
||||
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}");
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="JsonTestClass.h" />
|
||||
<ClInclude Include="CppWinrtTailored.h" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -56,10 +57,19 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CommandlineTest.cpp" />
|
||||
<ClCompile Include="SettingsTests.cpp" />
|
||||
<ClCompile Include="ProfileTests.cpp" />
|
||||
<ClCompile Include="ColorSchemeTests.cpp" />
|
||||
<ClCompile Include="KeyBindingsTests.cpp" />
|
||||
<ClCompile Include="CommandTests.cpp" />
|
||||
<ClCompile Include="TabTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<!-- You _NEED_ to include this file and the jsoncpp IncludePath (below) if
|
||||
you want to use jsoncpp -->
|
||||
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Project References ======================== -->
|
||||
@@ -71,8 +81,7 @@
|
||||
_ConsoleGenerateAdditionalWinmdManifests step won't gather the winmd's -->
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalApp\dll\TerminalApp.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)\src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalApp\dll\TerminalApp.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Globals ======================== -->
|
||||
|
||||
@@ -100,10 +100,6 @@
|
||||
<Project>{ca5cad1a-44bd-4ac7-ac72-f16e576fdd12}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettingsModel\dll\Microsoft.Terminal.Settings.Model.vcxproj">
|
||||
<Project>{CA5CAD1A-082C-4476-9F33-94B339494076}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -157,10 +153,6 @@
|
||||
<Copy SourceFiles="$(_TestBinRoot)\LocalTests_TerminalApp\TerminalApp.LocalTests.dll"
|
||||
DestinationFiles="$(TargetDir)\TerminalApp.LocalTests.dll" />
|
||||
|
||||
<!-- Copy our test code from LocalTests_TerminalApp into this directory -->
|
||||
<Copy SourceFiles="$(_TestBinRoot)\LocalTests_SettingsModel\SettingsModel.LocalTests.dll"
|
||||
DestinationFiles="$(TargetDir)\SettingsModel.LocalTests.dll" />
|
||||
|
||||
<!-- Copy some dlls which TerminalConnection is dependent upon that didn't
|
||||
get rolled up into this directory -->
|
||||
<Copy SourceFiles="@(TerminalConnectionDlls)"
|
||||
|
||||
@@ -12,19 +12,19 @@ Author(s):
|
||||
Mike Griese (migrie) December-2019
|
||||
--*/
|
||||
|
||||
class TestUtils
|
||||
class TerminalAppLocalTests::TestUtils
|
||||
{
|
||||
public:
|
||||
// Function Description:
|
||||
// - This is a helper to retrieve the ActionAndArgs from the keybindings
|
||||
// for a given chord.
|
||||
// Arguments:
|
||||
// - keymap: The AppKeyBindings to lookup the ActionAndArgs from.
|
||||
// - 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 winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs GetActionAndArgs(const winrt::Microsoft::Terminal::Settings::Model::KeyMapping& keymap,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::KeyChord& kc)
|
||||
static const winrt::TerminalApp::ActionAndArgs GetActionAndArgs(const winrt::TerminalApp::implementation::AppKeyBindings& bindings,
|
||||
const winrt::Microsoft::Terminal::TerminalControl::KeyChord& kc)
|
||||
{
|
||||
std::wstring buffer{ L"" };
|
||||
if (WI_IsFlagSet(kc.Modifiers(), winrt::Microsoft::Terminal::TerminalControl::KeyModifiers::Ctrl))
|
||||
@@ -42,8 +42,12 @@ public:
|
||||
buffer += static_cast<wchar_t>(MapVirtualKeyW(kc.Vkey(), MAPVK_VK_TO_CHAR));
|
||||
WEX::Logging::Log::Comment(WEX::Common::NoThrowString().Format(L"Looking for key:%s", buffer.c_str()));
|
||||
|
||||
const auto action = keymap.TryLookup(kc);
|
||||
VERIFY_IS_NOT_NULL(action, L"Expected to find an action bound to the given KeyChord");
|
||||
return action;
|
||||
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;
|
||||
};
|
||||
};
|
||||
@@ -15,8 +15,6 @@ Author(s):
|
||||
|
||||
#pragma once
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#include "LibraryIncludes.h"
|
||||
// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||||
@@ -35,6 +33,12 @@ Author(s):
|
||||
#include <json.h>
|
||||
#include "consoletaeftemplates.hpp"
|
||||
|
||||
// Common includes for most tests:
|
||||
#include "../../inc/argb.h"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||
#include <winrt/Windows.system.h>
|
||||
@@ -54,18 +58,8 @@ Author(s):
|
||||
#include <winrt/windows.applicationmodel.core.h>
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.Model.h>
|
||||
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
|
||||
#include <regex>
|
||||
#include <CLI11/CLI11.hpp>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
// Common includes for most tests:
|
||||
#include "../../inc/argb.h"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
@@ -51,9 +51,9 @@ 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" };
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
// 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.
|
||||
@@ -100,12 +100,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{ ToggleCommandPaletteKey, ShortcutAction::ToggleCommandPalette },
|
||||
{ ToggleFocusModeKey, ShortcutAction::ToggleFocusMode },
|
||||
{ ToggleFullscreenKey, ShortcutAction::ToggleFullscreen },
|
||||
{ TogglePaneZoomKey, ShortcutAction::TogglePaneZoom },
|
||||
// { TogglePaneZoomKey, ShortcutAction::TogglePaneZoom }, // TODO GH#7252: Re-enable pane zooming
|
||||
{ ToggleRetroEffectKey, ShortcutAction::ToggleRetroEffect },
|
||||
{ UnboundKey, ShortcutAction::Invalid },
|
||||
};
|
||||
|
||||
using ParseResult = std::tuple<IActionArgs, std::vector<SettingsLoadWarnings>>;
|
||||
using ParseResult = std::tuple<IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
|
||||
using ParseActionFunction = std::function<ParseResult(const Json::Value&)>;
|
||||
|
||||
// This is a map of ShortcutAction->function<IActionArgs(Json::Value)>. It holds
|
||||
@@ -169,7 +169,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// - a deserialized ActionAndArgs corresponding to the values in json, or
|
||||
// null if we failed to deserialize an action.
|
||||
winrt::com_ptr<ActionAndArgs> ActionAndArgs::FromJson(const Json::Value& json,
|
||||
std::vector<SettingsLoadWarnings>& warnings)
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
|
||||
{
|
||||
// Invalid is our placeholder that the action was not parsed.
|
||||
ShortcutAction action = ShortcutAction::Invalid;
|
||||
@@ -208,7 +208,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// does, we'll try to deserialize any "args" that were provided with
|
||||
// the binding.
|
||||
IActionArgs args{ nullptr };
|
||||
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> parseWarnings;
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> parseWarnings;
|
||||
const auto deserializersIter = argParsers.find(action);
|
||||
if (deserializersIter != argParsers.end())
|
||||
{
|
||||
@@ -1,33 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ActionAndArgs.g.h"
|
||||
#include "TerminalWarnings.h"
|
||||
#include "..\inc\cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct ActionAndArgs : public ActionAndArgsT<ActionAndArgs>
|
||||
{
|
||||
static const std::map<std::string_view, ShortcutAction, std::less<>> ActionKeyNamesMap;
|
||||
static winrt::com_ptr<ActionAndArgs> FromJson(const Json::Value& json,
|
||||
std::vector<SettingsLoadWarnings>& warnings);
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
|
||||
|
||||
ActionAndArgs() = default;
|
||||
ActionAndArgs(ShortcutAction action, IActionArgs args) :
|
||||
_Action{ action },
|
||||
_Args{ args } {};
|
||||
|
||||
hstring GenerateName() const;
|
||||
|
||||
GETSET_PROPERTY(ShortcutAction, Action, ShortcutAction::Invalid);
|
||||
GETSET_PROPERTY(TerminalApp::ShortcutAction, Action, TerminalApp::ShortcutAction::Invalid);
|
||||
GETSET_PROPERTY(IActionArgs, Args, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ActionAndArgs);
|
||||
}
|
||||
@@ -20,14 +20,14 @@
|
||||
#include "SetTabColorArgs.g.cpp"
|
||||
#include "RenameTabArgs.g.cpp"
|
||||
#include "ExecuteCommandlineArgs.g.cpp"
|
||||
#include "CloseOtherTabsArgs.g.cpp"
|
||||
#include "CloseTabsAfterArgs.g.cpp"
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
winrt::hstring NewTerminalArgs::GenerateName() const
|
||||
{
|
||||
@@ -212,7 +212,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// The string will be similar to the following:
|
||||
// * "Send Input: ...input..."
|
||||
|
||||
auto escapedInput = til::visualize_control_codes(_Input);
|
||||
auto escapedInput = VisualizeControlCodes(_Input);
|
||||
auto name = fmt::format(std::wstring_view(RS_(L"SendInputCommandKey")), escapedInput);
|
||||
return winrt::hstring{ name };
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "CloseTabsAfterArgs.g.h"
|
||||
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalWarnings.h"
|
||||
|
||||
@@ -35,16 +36,16 @@
|
||||
// * ActionEventArgs holds a single IActionArgs. For events that don't need
|
||||
// additional args, this can be nullptr.
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using FromJsonResult = std::tuple<Model::IActionArgs, std::vector<SettingsLoadWarnings>>;
|
||||
using namespace ::TerminalApp;
|
||||
using FromJsonResult = std::tuple<winrt::TerminalApp::IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
|
||||
|
||||
struct ActionEventArgs : public ActionEventArgsT<ActionEventArgs>
|
||||
{
|
||||
ActionEventArgs() = default;
|
||||
|
||||
explicit ActionEventArgs(const Model::IActionArgs& args) :
|
||||
explicit ActionEventArgs(const TerminalApp::IActionArgs& args) :
|
||||
_ActionArgs{ args } {};
|
||||
GETSET_PROPERTY(IActionArgs, ActionArgs, nullptr);
|
||||
GETSET_PROPERTY(bool, Handled, false);
|
||||
@@ -53,8 +54,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct NewTerminalArgs : public NewTerminalArgsT<NewTerminalArgs>
|
||||
{
|
||||
NewTerminalArgs() = default;
|
||||
NewTerminalArgs(int32_t& profileIndex) :
|
||||
_ProfileIndex{ profileIndex } {};
|
||||
GETSET_PROPERTY(winrt::hstring, Commandline, L"");
|
||||
GETSET_PROPERTY(winrt::hstring, StartingDirectory, L"");
|
||||
GETSET_PROPERTY(winrt::hstring, TabTitle, L"");
|
||||
@@ -70,7 +69,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
||||
bool Equals(const Model::NewTerminalArgs& other)
|
||||
bool Equals(const winrt::TerminalApp::NewTerminalArgs& other)
|
||||
{
|
||||
return other.Commandline() == _Commandline &&
|
||||
other.StartingDirectory() == _StartingDirectory &&
|
||||
@@ -78,7 +77,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
other.ProfileIndex() == _ProfileIndex &&
|
||||
other.Profile() == _Profile;
|
||||
};
|
||||
static Model::NewTerminalArgs FromJson(const Json::Value& json)
|
||||
static winrt::TerminalApp::NewTerminalArgs FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<NewTerminalArgs>();
|
||||
@@ -95,7 +94,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
CopyTextArgs() = default;
|
||||
GETSET_PROPERTY(bool, SingleLine, false);
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<TerminalControl::CopyFormat>, CopyFormatting, nullptr);
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<Microsoft::Terminal::TerminalControl::CopyFormat>, CopyFormatting, nullptr);
|
||||
|
||||
static constexpr std::string_view SingleLineKey{ "singleLine" };
|
||||
static constexpr std::string_view CopyFormattingKey{ "copyFormatting" };
|
||||
@@ -126,9 +125,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct NewTabArgs : public NewTabArgsT<NewTabArgs>
|
||||
{
|
||||
NewTabArgs() = default;
|
||||
NewTabArgs(const Model::NewTerminalArgs& terminalArgs) :
|
||||
_TerminalArgs{ terminalArgs } {};
|
||||
GETSET_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::NewTerminalArgs, TerminalArgs, nullptr);
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
@@ -154,8 +151,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct SwitchToTabArgs : public SwitchToTabArgsT<SwitchToTabArgs>
|
||||
{
|
||||
SwitchToTabArgs() = default;
|
||||
SwitchToTabArgs(uint32_t& tabIndex) :
|
||||
_TabIndex{ tabIndex } {};
|
||||
GETSET_PROPERTY(uint32_t, TabIndex, 0);
|
||||
|
||||
static constexpr std::string_view TabIndexKey{ "index" };
|
||||
@@ -184,7 +179,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct ResizePaneArgs : public ResizePaneArgsT<ResizePaneArgs>
|
||||
{
|
||||
ResizePaneArgs() = default;
|
||||
GETSET_PROPERTY(Model::Direction, Direction, Direction::None);
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::None);
|
||||
|
||||
static constexpr std::string_view DirectionKey{ "direction" };
|
||||
|
||||
@@ -205,9 +200,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->_Direction == Direction::None)
|
||||
if (args->_Direction == TerminalApp::Direction::None)
|
||||
{
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -219,7 +214,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct MoveFocusArgs : public MoveFocusArgsT<MoveFocusArgs>
|
||||
{
|
||||
MoveFocusArgs() = default;
|
||||
GETSET_PROPERTY(Model::Direction, Direction, Direction::None);
|
||||
GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::None);
|
||||
|
||||
static constexpr std::string_view DirectionKey{ "direction" };
|
||||
|
||||
@@ -240,9 +235,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->_Direction == Direction::None)
|
||||
if (args->_Direction == TerminalApp::Direction::None)
|
||||
{
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -304,7 +299,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
JsonUtils::GetValueForKey(json, InputKey, args->_Input);
|
||||
if (args->_Input.empty())
|
||||
{
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
}
|
||||
return { *args, {} };
|
||||
}
|
||||
@@ -313,12 +308,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
|
||||
{
|
||||
SplitPaneArgs() = default;
|
||||
SplitPaneArgs(SplitState style, const Model::NewTerminalArgs& terminalArgs) :
|
||||
_SplitStyle{ style },
|
||||
_TerminalArgs{ terminalArgs } {};
|
||||
GETSET_PROPERTY(SplitState, SplitStyle, SplitState::Automatic);
|
||||
GETSET_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr);
|
||||
GETSET_PROPERTY(SplitType, SplitMode, SplitType::Manual);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::SplitState, SplitStyle, winrt::TerminalApp::SplitState::Automatic);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::NewTerminalArgs, TerminalArgs, nullptr);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::SplitType, SplitMode, winrt::TerminalApp::SplitType::Manual);
|
||||
|
||||
static constexpr std::string_view SplitKey{ "split" };
|
||||
static constexpr std::string_view SplitModeKey{ "splitMode" };
|
||||
@@ -352,7 +344,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct OpenSettingsArgs : public OpenSettingsArgsT<OpenSettingsArgs>
|
||||
{
|
||||
OpenSettingsArgs() = default;
|
||||
GETSET_PROPERTY(SettingsTarget, Target, SettingsTarget::SettingsFile);
|
||||
GETSET_PROPERTY(TerminalApp::SettingsTarget, Target, TerminalApp::SettingsTarget::SettingsFile);
|
||||
|
||||
static constexpr std::string_view TargetKey{ "target" };
|
||||
|
||||
@@ -403,7 +395,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
JsonUtils::GetValueForKey(json, NameKey, args->_SchemeName);
|
||||
if (args->_SchemeName.empty())
|
||||
{
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
}
|
||||
return { *args, {} };
|
||||
}
|
||||
@@ -471,8 +463,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct ExecuteCommandlineArgs : public ExecuteCommandlineArgsT<ExecuteCommandlineArgs>
|
||||
{
|
||||
ExecuteCommandlineArgs() = default;
|
||||
ExecuteCommandlineArgs(winrt::hstring commandline) :
|
||||
_Commandline{ commandline } {};
|
||||
GETSET_PROPERTY(winrt::hstring, Commandline, L"");
|
||||
|
||||
static constexpr std::string_view CommandlineKey{ "commandline" };
|
||||
@@ -496,7 +486,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
JsonUtils::GetValueForKey(json, CommandlineKey, args->_Commandline);
|
||||
if (args->_Commandline.empty())
|
||||
{
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
}
|
||||
return { *args, {} };
|
||||
}
|
||||
@@ -505,9 +495,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct CloseOtherTabsArgs : public CloseOtherTabsArgsT<CloseOtherTabsArgs>
|
||||
{
|
||||
CloseOtherTabsArgs() = default;
|
||||
CloseOtherTabsArgs(uint32_t& tabIndex) :
|
||||
_Index{ tabIndex } {};
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<uint32_t>, Index, nullptr);
|
||||
GETSET_PROPERTY(winrt::Windows::Foundation::IReference<uint32_t>, Index, nullptr);
|
||||
|
||||
static constexpr std::string_view IndexKey{ "index" };
|
||||
|
||||
@@ -535,9 +523,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct CloseTabsAfterArgs : public CloseTabsAfterArgsT<CloseTabsAfterArgs>
|
||||
{
|
||||
CloseTabsAfterArgs() = default;
|
||||
CloseTabsAfterArgs(uint32_t& tabIndex) :
|
||||
_Index{ tabIndex } {};
|
||||
GETSET_PROPERTY(Windows::Foundation::IReference<uint32_t>, Index, nullptr);
|
||||
GETSET_PROPERTY(winrt::Windows::Foundation::IReference<uint32_t>, Index, nullptr);
|
||||
|
||||
static constexpr std::string_view IndexKey{ "index" };
|
||||
|
||||
@@ -563,14 +549,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ActionEventArgs);
|
||||
BASIC_FACTORY(SwitchToTabArgs);
|
||||
BASIC_FACTORY(NewTerminalArgs);
|
||||
BASIC_FACTORY(NewTabArgs);
|
||||
BASIC_FACTORY(SplitPaneArgs);
|
||||
BASIC_FACTORY(ExecuteCommandlineArgs);
|
||||
BASIC_FACTORY(CloseOtherTabsArgs);
|
||||
BASIC_FACTORY(CloseTabsAfterArgs);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Model
|
||||
namespace TerminalApp
|
||||
{
|
||||
interface IActionArgs
|
||||
{
|
||||
@@ -47,8 +47,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass NewTerminalArgs {
|
||||
NewTerminalArgs();
|
||||
NewTerminalArgs(Int32 profileIndex);
|
||||
|
||||
String Commandline;
|
||||
String StartingDirectory;
|
||||
String TabTitle;
|
||||
@@ -63,7 +61,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass ActionEventArgs : IActionEventArgs
|
||||
{
|
||||
ActionEventArgs();
|
||||
ActionEventArgs(IActionArgs args);
|
||||
};
|
||||
|
||||
@@ -75,14 +72,12 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass NewTabArgs : IActionArgs
|
||||
{
|
||||
NewTabArgs(NewTerminalArgs terminalArgs);
|
||||
NewTerminalArgs TerminalArgs { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SwitchToTabArgs : IActionArgs
|
||||
{
|
||||
SwitchToTabArgs(UInt32 tabIndex);
|
||||
UInt32 TabIndex;
|
||||
UInt32 TabIndex { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ResizePaneArgs : IActionArgs
|
||||
@@ -107,7 +102,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass SplitPaneArgs : IActionArgs
|
||||
{
|
||||
SplitPaneArgs(SplitState style, NewTerminalArgs terminalArgs);
|
||||
SplitState SplitStyle { get; };
|
||||
NewTerminalArgs TerminalArgs { get; };
|
||||
SplitType SplitMode { get; };
|
||||
@@ -135,19 +129,16 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass ExecuteCommandlineArgs : IActionArgs
|
||||
{
|
||||
ExecuteCommandlineArgs(String commandline);
|
||||
String Commandline;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass CloseOtherTabsArgs : IActionArgs
|
||||
{
|
||||
CloseOtherTabsArgs(UInt32 tabIndex);
|
||||
Windows.Foundation.IReference<UInt32> Index { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass CloseTabsAfterArgs : IActionArgs
|
||||
{
|
||||
CloseTabsAfterArgs(UInt32 tabIndex);
|
||||
Windows.Foundation.IReference<UInt32> Index { get; };
|
||||
};
|
||||
}
|
||||
@@ -14,7 +14,6 @@ using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace ::TerminalApp;
|
||||
@@ -28,76 +27,76 @@ namespace winrt
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void TerminalPage::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_OpenNewTabDropdown();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_DuplicateTabViewItem();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleCloseTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_CloseFocusedTab();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleClosePane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_CloseFocusedPane();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
CloseWindow();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_Scroll(-1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleScrollDown(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_Scroll(1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_SelectNextTab(true);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_SelectNextTab(false);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSendInput(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
args.Handled(false);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<SendInputArgs>())
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SendInputArgs>())
|
||||
{
|
||||
const auto termControl = _GetActiveControl();
|
||||
termControl.SendInput(realArgs.Input());
|
||||
@@ -106,13 +105,13 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSplitPane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
args.Handled(false);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SplitPaneArgs>())
|
||||
{
|
||||
_SplitPane(realArgs.SplitStyle(), realArgs.SplitMode(), realArgs.TerminalArgs());
|
||||
args.Handled(true);
|
||||
@@ -120,7 +119,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
auto activeTab = _GetFocusedTab();
|
||||
|
||||
@@ -142,23 +141,23 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_ScrollPage(-1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleScrollDownPage(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_ScrollPage(1);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<OpenSettingsArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::OpenSettingsArgs>())
|
||||
{
|
||||
_LaunchSettings(realArgs.Target());
|
||||
args.Handled(true);
|
||||
@@ -166,21 +165,21 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandlePasteText(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_PasteText();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (args == nullptr)
|
||||
{
|
||||
_OpenNewTab(nullptr);
|
||||
args.Handled(true);
|
||||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<NewTabArgs>())
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::NewTabArgs>())
|
||||
{
|
||||
_OpenNewTab(realArgs.TerminalArgs());
|
||||
args.Handled(true);
|
||||
@@ -188,9 +187,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSwitchToTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SwitchToTabArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SwitchToTabArgs>())
|
||||
{
|
||||
const auto handled = _SelectTab({ realArgs.TabIndex() });
|
||||
args.Handled(handled);
|
||||
@@ -198,11 +197,11 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleResizePane(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<ResizePaneArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::ResizePaneArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == Direction::None)
|
||||
if (realArgs.Direction() == TerminalApp::Direction::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
@@ -216,11 +215,11 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMoveFocus(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<MoveFocusArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::MoveFocusArgs>())
|
||||
{
|
||||
if (realArgs.Direction() == Direction::None)
|
||||
if (realArgs.Direction() == TerminalApp::Direction::None)
|
||||
{
|
||||
// Do nothing
|
||||
args.Handled(false);
|
||||
@@ -234,9 +233,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleCopyText(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<CopyTextArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::CopyTextArgs>())
|
||||
{
|
||||
const auto handled = _CopyText(realArgs.SingleLine(), realArgs.CopyFormatting());
|
||||
args.Handled(handled);
|
||||
@@ -244,9 +243,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleAdjustFontSize(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<AdjustFontSizeArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::AdjustFontSizeArgs>())
|
||||
{
|
||||
const auto termControl = _GetActiveControl();
|
||||
termControl.AdjustFontSize(realArgs.Delta());
|
||||
@@ -255,14 +254,14 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleFind(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
_Find();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleResetFontSize(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
const auto termControl = _GetActiveControl();
|
||||
termControl.ResetFontSize();
|
||||
@@ -270,7 +269,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleRetroEffect(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
const auto termControl = _GetActiveControl();
|
||||
termControl.ToggleRetroEffect();
|
||||
@@ -278,28 +277,28 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleFocusMode(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
ToggleFocusMode();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleFullscreen(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
ToggleFullscreen();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleAlwaysOnTop(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
ToggleAlwaysOnTop();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleCommandPalette(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
// TODO GH#6677: When we add support for commandline mode, first set the
|
||||
// mode that the command palette should be in, before making it visible.
|
||||
@@ -311,20 +310,19 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSetColorScheme(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
args.Handled(false);
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SetColorSchemeArgs>())
|
||||
{
|
||||
if (auto activeTab = _GetFocusedTab())
|
||||
{
|
||||
if (auto activeControl = activeTab->GetActiveTerminalControl())
|
||||
{
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
auto controlSettings = activeControl.Settings();
|
||||
if (_settings->ApplyColorScheme(controlSettings, realArgs.SchemeName()))
|
||||
{
|
||||
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
controlSettings->ApplyColorScheme(scheme);
|
||||
activeControl.UpdateSettings(*controlSettings);
|
||||
activeControl.UpdateSettings(controlSettings);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
@@ -333,11 +331,11 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSetTabColor(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
std::optional<til::color> tabColor;
|
||||
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SetTabColorArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SetTabColorArgs>())
|
||||
{
|
||||
if (realArgs.TabColor() != nullptr)
|
||||
{
|
||||
@@ -361,7 +359,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
auto activeTab = _GetFocusedTab();
|
||||
if (activeTab)
|
||||
@@ -372,11 +370,11 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleRenameTab(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
std::optional<winrt::hstring> title;
|
||||
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<RenameTabArgs>())
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::RenameTabArgs>())
|
||||
{
|
||||
title = realArgs.Title();
|
||||
}
|
||||
@@ -397,11 +395,11 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleExecuteCommandline(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& actionArgs)
|
||||
const TerminalApp::ActionEventArgs& actionArgs)
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<ExecuteCommandlineArgs>())
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<TerminalApp::ExecuteCommandlineArgs>())
|
||||
{
|
||||
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(
|
||||
auto actions = winrt::single_threaded_vector<winrt::TerminalApp::ActionAndArgs>(std::move(
|
||||
TerminalPage::ConvertExecuteCommandlineToActions(realArgs)));
|
||||
|
||||
if (_startupActions.Size() != 0)
|
||||
@@ -413,9 +411,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleCloseOtherTabs(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& actionArgs)
|
||||
const TerminalApp::ActionEventArgs& actionArgs)
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<CloseOtherTabsArgs>())
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<TerminalApp::CloseOtherTabsArgs>())
|
||||
{
|
||||
uint32_t index;
|
||||
if (realArgs.Index())
|
||||
@@ -450,9 +448,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleCloseTabsAfter(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& actionArgs)
|
||||
const TerminalApp::ActionEventArgs& actionArgs)
|
||||
{
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<CloseTabsAfterArgs>())
|
||||
if (const auto& realArgs = actionArgs.ActionArgs().try_as<TerminalApp::CloseTabsAfterArgs>())
|
||||
{
|
||||
uint32_t index;
|
||||
if (realArgs.Index())
|
||||
@@ -487,7 +485,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleOpenTabSearch(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
const TerminalApp::ActionEventArgs& args)
|
||||
{
|
||||
auto opt = _GetFocusedTabIndex();
|
||||
uint32_t startIdx = opt.value_or(0);
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
#include "pch.h"
|
||||
#include "AppLogic.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
#include "ActionArgs.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace TerminalApp;
|
||||
|
||||
// Either a ; at the start of a line, or a ; preceded by any non-\ char.
|
||||
@@ -159,42 +159,33 @@ void AppCommandlineArgs::_buildParser()
|
||||
{
|
||||
// -v,--version: Displays version info
|
||||
auto versionCallback = [this](int64_t /*count*/) {
|
||||
// Set our message to display the application name and the current version.
|
||||
_exitMessage = fmt::format("{0}\n{1}",
|
||||
til::u16u8(CascadiaSettings::ApplicationDisplayName()),
|
||||
til::u16u8(CascadiaSettings::ApplicationVersion()));
|
||||
// Theoretically, we don't need to exit now, since this isn't really
|
||||
// an error case. However, in practice, it feels weird to have `wt
|
||||
// -v` open a new tab, and makes enough sense that `wt -v ;
|
||||
// split-pane` (or whatever) just displays the version and exits.
|
||||
_shouldExitEarly = true;
|
||||
if (const auto appLogic{ winrt::TerminalApp::implementation::AppLogic::Current() })
|
||||
{
|
||||
// Set our message to display the application name and the current version.
|
||||
_exitMessage = fmt::format("{0}\n{1}",
|
||||
til::u16u8(appLogic->ApplicationDisplayName()),
|
||||
til::u16u8(appLogic->ApplicationVersion()));
|
||||
// Theoretically, we don't need to exit now, since this isn't really
|
||||
// an error case. However, in practice, it feels weird to have `wt
|
||||
// -v` open a new tab, and makes enough sense that `wt -v ;
|
||||
// split-pane` (or whatever) just displays the version and exits.
|
||||
_shouldExitEarly = true;
|
||||
}
|
||||
};
|
||||
_app.add_flag_function("-v,--version", versionCallback, RS_A(L"CmdVersionDesc"));
|
||||
|
||||
// Launch mode related flags
|
||||
// Maximized and Fullscreen flags
|
||||
// -M,--maximized: Maximizes the window on launch
|
||||
// -F,--fullscreen: Fullscreens the window on launch
|
||||
// -f,--focus: Sets the terminal into the Focus mode
|
||||
// While fullscreen excludes both maximized and focus mode, the user can combine between the maximized and focused (-fM)
|
||||
auto maximizedCallback = [this](int64_t /*count*/) {
|
||||
_launchMode = (_launchMode.has_value() && _launchMode.value() == LaunchMode::FocusMode) ?
|
||||
LaunchMode::MaximizedFocusMode :
|
||||
LaunchMode::MaximizedMode;
|
||||
_launchMode = winrt::TerminalApp::LaunchMode::MaximizedMode;
|
||||
};
|
||||
auto fullscreenCallback = [this](int64_t /*count*/) {
|
||||
_launchMode = LaunchMode::FullscreenMode;
|
||||
_launchMode = winrt::TerminalApp::LaunchMode::FullscreenMode;
|
||||
};
|
||||
auto focusCallback = [this](int64_t /*count*/) {
|
||||
_launchMode = (_launchMode.has_value() && _launchMode.value() == LaunchMode::MaximizedMode) ?
|
||||
LaunchMode::MaximizedFocusMode :
|
||||
LaunchMode::FocusMode;
|
||||
};
|
||||
|
||||
auto maximized = _app.add_flag_function("-M,--maximized", maximizedCallback, RS_A(L"CmdMaximizedDesc"));
|
||||
auto fullscreen = _app.add_flag_function("-F,--fullscreen", fullscreenCallback, RS_A(L"CmdFullscreenDesc"));
|
||||
auto focus = _app.add_flag_function("-f,--focus", focusCallback, RS_A(L"CmdFocusDesc"));
|
||||
maximized->excludes(fullscreen);
|
||||
focus->excludes(fullscreen);
|
||||
|
||||
// Subcommands
|
||||
_buildNewTabParser();
|
||||
@@ -223,13 +214,14 @@ void AppCommandlineArgs::_buildNewTabParser()
|
||||
// command was parsed.
|
||||
subcommand.subcommand->callback([&, this]() {
|
||||
// Build the NewTab action from the values we've parsed on the commandline.
|
||||
ActionAndArgs newTabAction{};
|
||||
newTabAction.Action(ShortcutAction::NewTab);
|
||||
auto newTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
||||
newTabAction->Action(ShortcutAction::NewTab);
|
||||
auto args = winrt::make_self<implementation::NewTabArgs>();
|
||||
// _getNewTerminalArgs MUST be called before parsing any other options,
|
||||
// as it might clear those options while finding the commandline
|
||||
NewTabArgs args{ _getNewTerminalArgs(subcommand) };
|
||||
newTabAction.Args(args);
|
||||
_startupActions.push_back(newTabAction);
|
||||
args->TerminalArgs(_getNewTerminalArgs(subcommand));
|
||||
newTabAction->Args(*args);
|
||||
_startupActions.push_back(*newTabAction);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -265,29 +257,29 @@ void AppCommandlineArgs::_buildSplitPaneParser()
|
||||
// command was parsed.
|
||||
subcommand.subcommand->callback([&, this]() {
|
||||
// Build the SplitPane action from the values we've parsed on the commandline.
|
||||
ActionAndArgs splitPaneActionAndArgs{};
|
||||
splitPaneActionAndArgs.Action(ShortcutAction::SplitPane);
|
||||
|
||||
auto splitPaneActionAndArgs = winrt::make_self<implementation::ActionAndArgs>();
|
||||
splitPaneActionAndArgs->Action(ShortcutAction::SplitPane);
|
||||
auto args = winrt::make_self<implementation::SplitPaneArgs>();
|
||||
// _getNewTerminalArgs MUST be called before parsing any other options,
|
||||
// as it might clear those options while finding the commandline
|
||||
auto terminalArgs{ _getNewTerminalArgs(subcommand) };
|
||||
auto style{ SplitState::Automatic };
|
||||
args->TerminalArgs(_getNewTerminalArgs(subcommand));
|
||||
args->SplitStyle(SplitState::Automatic);
|
||||
// Make sure to use the `Option`s here to check if they were set -
|
||||
// _getNewTerminalArgs might reset them while parsing a commandline
|
||||
if ((*subcommand._horizontalOption || *subcommand._verticalOption))
|
||||
{
|
||||
if (_splitHorizontal)
|
||||
{
|
||||
style = SplitState::Horizontal;
|
||||
args->SplitStyle(SplitState::Horizontal);
|
||||
}
|
||||
else if (_splitVertical)
|
||||
{
|
||||
style = SplitState::Vertical;
|
||||
args->SplitStyle(SplitState::Vertical);
|
||||
}
|
||||
}
|
||||
SplitPaneArgs args{ style, terminalArgs };
|
||||
splitPaneActionAndArgs.Args(args);
|
||||
_startupActions.push_back(splitPaneActionAndArgs);
|
||||
|
||||
splitPaneActionAndArgs->Args(*args);
|
||||
_startupActions.push_back(*splitPaneActionAndArgs);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -327,19 +319,20 @@ void AppCommandlineArgs::_buildFocusTabParser()
|
||||
// command was parsed.
|
||||
subcommand->callback([&, this]() {
|
||||
// Build the action from the values we've parsed on the commandline.
|
||||
ActionAndArgs focusTabAction{};
|
||||
auto focusTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
||||
|
||||
if (_focusTabIndex >= 0)
|
||||
{
|
||||
focusTabAction.Action(ShortcutAction::SwitchToTab);
|
||||
SwitchToTabArgs args{ static_cast<unsigned int>(_focusTabIndex) };
|
||||
focusTabAction.Args(args);
|
||||
_startupActions.push_back(focusTabAction);
|
||||
focusTabAction->Action(ShortcutAction::SwitchToTab);
|
||||
auto args = winrt::make_self<implementation::SwitchToTabArgs>();
|
||||
args->TabIndex(_focusTabIndex);
|
||||
focusTabAction->Args(*args);
|
||||
_startupActions.push_back(*focusTabAction);
|
||||
}
|
||||
else if (_focusNextTab || _focusPrevTab)
|
||||
{
|
||||
focusTabAction.Action(_focusNextTab ? ShortcutAction::NextTab : ShortcutAction::PrevTab);
|
||||
_startupActions.push_back(std::move(focusTabAction));
|
||||
focusTabAction->Action(_focusNextTab ? ShortcutAction::NextTab : ShortcutAction::PrevTab);
|
||||
_startupActions.push_back(*focusTabAction);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -386,7 +379,7 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc
|
||||
// - A fully initialized NewTerminalArgs corresponding to values we've currently parsed.
|
||||
NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewTerminalSubcommand& subcommand)
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
auto args = winrt::make_self<implementation::NewTerminalArgs>();
|
||||
|
||||
if (!_commandline.empty())
|
||||
{
|
||||
@@ -410,25 +403,25 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
|
||||
}
|
||||
}
|
||||
|
||||
args.Commandline(winrt::to_hstring(cmdlineBuffer.str()));
|
||||
args->Commandline(winrt::to_hstring(cmdlineBuffer.str()));
|
||||
}
|
||||
|
||||
if (*subcommand.profileNameOption)
|
||||
{
|
||||
args.Profile(winrt::to_hstring(_profileName));
|
||||
args->Profile(winrt::to_hstring(_profileName));
|
||||
}
|
||||
|
||||
if (*subcommand.startingDirectoryOption)
|
||||
{
|
||||
args.StartingDirectory(winrt::to_hstring(_startingDirectory));
|
||||
args->StartingDirectory(winrt::to_hstring(_startingDirectory));
|
||||
}
|
||||
|
||||
if (*subcommand.titleOption)
|
||||
{
|
||||
args.TabTitle(winrt::to_hstring(_startingTitle));
|
||||
args->TabTitle(winrt::to_hstring(_startingTitle));
|
||||
}
|
||||
|
||||
return args;
|
||||
return *args;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -606,7 +599,7 @@ void AppCommandlineArgs::_addCommandsForArg(std::vector<Commandline>& commands,
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the deque of actions we've buffered as a result of parsing commands.
|
||||
std::vector<ActionAndArgs>& AppCommandlineArgs::GetStartupActions()
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs>& AppCommandlineArgs::GetStartupActions()
|
||||
{
|
||||
return _startupActions;
|
||||
}
|
||||
@@ -659,15 +652,18 @@ void AppCommandlineArgs::ValidateStartupCommands()
|
||||
_startupActions.front().Action() != ShortcutAction::NewTab)
|
||||
{
|
||||
// Build the NewTab action from the values we've parsed on the commandline.
|
||||
NewTerminalArgs newTerminalArgs{};
|
||||
NewTabArgs args{ newTerminalArgs };
|
||||
ActionAndArgs newTabAction{ ShortcutAction::NewTab, args };
|
||||
auto newTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
||||
newTabAction->Action(ShortcutAction::NewTab);
|
||||
auto args = winrt::make_self<implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<implementation::NewTerminalArgs>();
|
||||
args->TerminalArgs(*newTerminalArgs);
|
||||
newTabAction->Args(*args);
|
||||
// push the arg onto the front
|
||||
_startupActions.insert(_startupActions.begin(), 1, newTabAction);
|
||||
_startupActions.insert(_startupActions.begin(), 1, *newTabAction);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> AppCommandlineArgs::GetLaunchMode() const noexcept
|
||||
std::optional<winrt::TerminalApp::LaunchMode> AppCommandlineArgs::GetLaunchMode() const noexcept
|
||||
{
|
||||
return _launchMode;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
#pragma once
|
||||
|
||||
#include "ActionAndArgs.h"
|
||||
|
||||
#include "Commandline.h"
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
@@ -34,11 +36,11 @@ public:
|
||||
static std::vector<Commandline> BuildCommands(winrt::array_view<const winrt::hstring>& args);
|
||||
|
||||
void ValidateStartupCommands();
|
||||
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs>& GetStartupActions();
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs>& GetStartupActions();
|
||||
const std::string& GetExitMessage();
|
||||
bool ShouldExitEarly() const noexcept;
|
||||
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> GetLaunchMode() const noexcept;
|
||||
std::optional<winrt::TerminalApp::LaunchMode> GetLaunchMode() const noexcept;
|
||||
|
||||
private:
|
||||
static const std::wregex _commandDelimiterRegex;
|
||||
@@ -87,14 +89,14 @@ private:
|
||||
bool _focusNextTab{ false };
|
||||
bool _focusPrevTab{ false };
|
||||
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> _launchMode{ std::nullopt };
|
||||
std::optional<winrt::TerminalApp::LaunchMode> _launchMode{ std::nullopt };
|
||||
// Are you adding more args here? Make sure to reset them in _resetStateToDefault
|
||||
|
||||
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> _startupActions;
|
||||
std::vector<winrt::TerminalApp::ActionAndArgs> _startupActions;
|
||||
std::string _exitMessage;
|
||||
bool _shouldExitEarly{ false };
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
|
||||
winrt::TerminalApp::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
|
||||
void _addNewTerminalArgs(NewTerminalSubcommand& subcommand);
|
||||
void _buildParser();
|
||||
void _buildNewTabParser();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppKeyBindings.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
|
||||
#include "AppKeyBindings.g.cpp"
|
||||
|
||||
@@ -12,11 +13,71 @@ using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void AppKeyBindings::SetKeyBinding(const TerminalApp::ActionAndArgs& actionAndArgs,
|
||||
const KeyChord& chord)
|
||||
{
|
||||
_keyShortcuts[chord] = actionAndArgs;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Remove the action that's bound to a particular KeyChord.
|
||||
// Arguments:
|
||||
// - chord: the keystroke to remove the action for.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppKeyBindings::ClearKeyBinding(const KeyChord& chord)
|
||||
{
|
||||
_keyShortcuts.erase(chord);
|
||||
}
|
||||
|
||||
KeyChord AppKeyBindings::GetKeyBindingForAction(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)
|
||||
{
|
||||
if (actionAndArgs == nullptr)
|
||||
{
|
||||
return { nullptr };
|
||||
}
|
||||
|
||||
for (auto& kv : _keyShortcuts)
|
||||
{
|
||||
const auto action = kv.second.Action();
|
||||
const auto args = kv.second.Args();
|
||||
const auto actionMatched = action == actionAndArgs.Action();
|
||||
const auto argsMatched = args ? args.Equals(actionAndArgs.Args()) : args == actionAndArgs.Args();
|
||||
if (actionMatched && argsMatched)
|
||||
{
|
||||
return kv.first;
|
||||
}
|
||||
}
|
||||
return { nullptr };
|
||||
}
|
||||
|
||||
bool AppKeyBindings::TryKeyChord(const KeyChord& kc)
|
||||
{
|
||||
const auto actionAndArgs = _keymap.TryLookup(kc);
|
||||
if (actionAndArgs)
|
||||
const auto keyIter = _keyShortcuts.find(kc);
|
||||
if (keyIter != _keyShortcuts.end())
|
||||
{
|
||||
const auto actionAndArgs = keyIter->second;
|
||||
return _dispatch.DoAction(actionAndArgs);
|
||||
}
|
||||
return false;
|
||||
@@ -27,8 +88,28 @@ namespace winrt::TerminalApp::implementation
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
|
||||
void AppKeyBindings::SetKeyMapping(const winrt::Microsoft::Terminal::Settings::Model::KeyMapping& keymap)
|
||||
// Method Description:
|
||||
// - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML
|
||||
// Return Value:
|
||||
// - a Windows::System::VirtualKeyModifiers object with the flags of which modifiers used.
|
||||
Windows::System::VirtualKeyModifiers AppKeyBindings::ConvertVKModifiers(KeyModifiers modifiers)
|
||||
{
|
||||
_keymap = keymap;
|
||||
Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None;
|
||||
|
||||
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
|
||||
{
|
||||
keyModifiers |= Windows::System::VirtualKeyModifiers::Control;
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
|
||||
{
|
||||
keyModifiers |= Windows::System::VirtualKeyModifiers::Shift;
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
|
||||
{
|
||||
// note: Menu is the Alt VK_MENU
|
||||
keyModifiers |= Windows::System::VirtualKeyModifiers::Menu;
|
||||
}
|
||||
|
||||
return keyModifiers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppKeyBindings.g.h"
|
||||
#include "ActionArgs.h"
|
||||
#include "ShortcutActionDispatch.h"
|
||||
#include "..\inc\cppwinrt_utils.h"
|
||||
|
||||
@@ -11,25 +12,60 @@
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class KeyBindingsTests;
|
||||
class TestUtils;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct KeyChordHash
|
||||
{
|
||||
std::size_t operator()(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& key) const
|
||||
{
|
||||
std::hash<int32_t> keyHash;
|
||||
std::hash<winrt::Microsoft::Terminal::TerminalControl::KeyModifiers> modifiersHash;
|
||||
std::size_t hashedKey = keyHash(key.Vkey());
|
||||
std::size_t hashedMods = modifiersHash(key.Modifiers());
|
||||
return hashedKey ^ hashedMods;
|
||||
}
|
||||
};
|
||||
|
||||
struct KeyChordEquality
|
||||
{
|
||||
bool operator()(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& lhs, const winrt::Microsoft::Terminal::TerminalControl::KeyChord& rhs) const
|
||||
{
|
||||
return lhs.Modifiers() == rhs.Modifiers() && lhs.Vkey() == rhs.Vkey();
|
||||
}
|
||||
};
|
||||
|
||||
struct AppKeyBindings : AppKeyBindingsT<AppKeyBindings>
|
||||
{
|
||||
AppKeyBindings() = default;
|
||||
|
||||
bool TryKeyChord(winrt::Microsoft::Terminal::TerminalControl::KeyChord const& kc);
|
||||
|
||||
void SetKeyBinding(TerminalApp::ActionAndArgs const& actionAndArgs,
|
||||
winrt::Microsoft::Terminal::TerminalControl::KeyChord const& chord);
|
||||
void ClearKeyBinding(winrt::Microsoft::Terminal::TerminalControl::KeyChord const& chord);
|
||||
Microsoft::Terminal::TerminalControl::KeyChord GetKeyBindingForAction(TerminalApp::ShortcutAction const& action);
|
||||
Microsoft::Terminal::TerminalControl::KeyChord GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs);
|
||||
|
||||
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::TerminalControl::KeyModifiers modifiers);
|
||||
|
||||
// Defined in AppKeyBindingsSerialization.cpp
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> LayerJson(const Json::Value& json);
|
||||
Json::Value ToJson();
|
||||
|
||||
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
|
||||
void SetKeyMapping(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap);
|
||||
|
||||
private:
|
||||
winrt::Microsoft::Terminal::Settings::Model::KeyMapping _keymap{ nullptr };
|
||||
std::unordered_map<winrt::Microsoft::Terminal::TerminalControl::KeyChord, TerminalApp::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
|
||||
|
||||
winrt::TerminalApp::ShortcutActionDispatch _dispatch{ nullptr };
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::KeyBindingsTests;
|
||||
friend class TerminalAppLocalTests::TestUtils;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
import "ActionArgs.idl";
|
||||
import "ShortcutActionDispatch.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
@@ -8,7 +9,12 @@ namespace TerminalApp
|
||||
{
|
||||
AppKeyBindings();
|
||||
|
||||
void SetKeyBinding(ActionAndArgs actionAndArgs, Microsoft.Terminal.TerminalControl.KeyChord chord);
|
||||
void ClearKeyBinding(Microsoft.Terminal.TerminalControl.KeyChord chord);
|
||||
|
||||
Microsoft.Terminal.TerminalControl.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
Microsoft.Terminal.TerminalControl.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
|
||||
|
||||
void SetDispatch(ShortcutActionDispatch dispatch);
|
||||
void SetKeyMapping(Microsoft.Terminal.Settings.Model.KeyMapping keymap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
// - A couple helper functions for serializing/deserializing a KeyMapping
|
||||
// - A couple helper functions for serializing/deserializing an AppKeyBindings
|
||||
// to/from json.
|
||||
//
|
||||
// Author(s):
|
||||
// - Mike Griese - May 2019
|
||||
|
||||
#include "pch.h"
|
||||
#include "KeyMapping.h"
|
||||
#include "AppKeyBindings.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::TerminalApp;
|
||||
|
||||
static constexpr std::string_view KeysKey{ "keys" };
|
||||
static constexpr std::string_view CommandKey{ "command" };
|
||||
@@ -51,12 +52,12 @@ static Json::Value _ShortcutAsJsonObject(const KeyChord& chord,
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Serialize this KeyMapping to a json array of objects. Each object in
|
||||
// - Serialize this AppKeyBindings to a json array of objects. Each object in
|
||||
// the array represents a single keybinding, mapping a KeyChord to a
|
||||
// ShortcutAction.
|
||||
// Return Value:
|
||||
// - a Json::Value which is an equivalent serialization of this object.
|
||||
Json::Value winrt::Microsoft::Terminal::Settings::Model::implementation::KeyMapping::ToJson()
|
||||
Json::Value winrt::TerminalApp::implementation::AppKeyBindings::ToJson()
|
||||
{
|
||||
Json::Value bindingsArray;
|
||||
|
||||
@@ -80,7 +81,7 @@ Json::Value winrt::Microsoft::Terminal::Settings::Model::implementation::KeyMapp
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Deserialize a KeyMapping from the key mappings that are in the array
|
||||
// - 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
|
||||
// `command` string and a `keys` array, where `command` is one of the names
|
||||
// listed in `ActionAndArgs::ActionKeyNamesMap`, and `keys` is an array of
|
||||
@@ -92,13 +93,13 @@ Json::Value winrt::Microsoft::Terminal::Settings::Model::implementation::KeyMapp
|
||||
// `"unbound"`, then we'll clear the keybinding from the existing keybindings.
|
||||
// Arguments:
|
||||
// - json: an array of Json::Value's to deserialize into our _keyShortcuts mapping.
|
||||
std::vector<SettingsLoadWarnings> winrt::Microsoft::Terminal::Settings::Model::implementation::KeyMapping::LayerJson(const Json::Value& json)
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// It's possible that the user provided keybindings have some warnings in
|
||||
// them - problems that we should alert the user to, but we can recover
|
||||
// from. Most of these warnings cannot be detected later in the Validate
|
||||
// settings phase, so we'll collect them now.
|
||||
std::vector<SettingsLoadWarnings> warnings;
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> warnings;
|
||||
|
||||
for (const auto& value : json)
|
||||
{
|
||||
@@ -120,7 +121,7 @@ std::vector<SettingsLoadWarnings> winrt::Microsoft::Terminal::Settings::Model::i
|
||||
// TODO: GH#1334 - remove this check.
|
||||
if (keys.isArray() && keys.size() > 1)
|
||||
{
|
||||
warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord);
|
||||
warnings.push_back(::TerminalApp::SettingsLoadWarnings::TooManyKeysForChord);
|
||||
}
|
||||
|
||||
if (!validString && !validArray)
|
||||
@@ -16,7 +16,6 @@ using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
namespace winrt
|
||||
@@ -77,7 +76,7 @@ static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_vi
|
||||
// - warning: the SettingsLoadWarnings value to get the localized text for.
|
||||
// Return Value:
|
||||
// - localized text for the given warning
|
||||
static winrt::hstring _GetWarningText(SettingsLoadWarnings warning)
|
||||
static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(warning), settingsLoadWarningsLabels);
|
||||
}
|
||||
@@ -90,7 +89,7 @@ static winrt::hstring _GetWarningText(SettingsLoadWarnings warning)
|
||||
// - error: the SettingsLoadErrors value to get the localized text for.
|
||||
// Return Value:
|
||||
// - localized text for the given error
|
||||
static winrt::hstring _GetErrorText(SettingsLoadErrors error)
|
||||
static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error)
|
||||
{
|
||||
return _GetMessageText(static_cast<uint32_t>(error), settingsLoadErrorsLabels);
|
||||
}
|
||||
@@ -166,17 +165,6 @@ namespace winrt::TerminalApp::implementation
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the settings currently in use by the entire Terminal application.
|
||||
// Throws:
|
||||
// - HR E_INVALIDARG if the app isn't up and running.
|
||||
const CascadiaSettings AppLogic::CurrentAppSettings()
|
||||
{
|
||||
auto appLogic{ ::winrt::TerminalApp::implementation::AppLogic::Current() };
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, appLogic);
|
||||
return appLogic->GetSettings();
|
||||
}
|
||||
|
||||
AppLogic::AppLogic() :
|
||||
_dialogLock{},
|
||||
_loadedInitialSettings{ false },
|
||||
@@ -250,7 +238,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// so this setting is overridden to false no matter what the preference is.
|
||||
if (_isUwp)
|
||||
{
|
||||
_settings.GlobalSettings().ShowTabsInTitlebar(false);
|
||||
_settings->GlobalSettings().ShowTabsInTitlebar(false);
|
||||
}
|
||||
|
||||
_root->SetSettings(_settings, false);
|
||||
@@ -265,21 +253,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
_root->ToggleFullscreen();
|
||||
}
|
||||
else if (launchMode == LaunchMode::FocusMode || launchMode == LaunchMode::MaximizedFocusMode)
|
||||
{
|
||||
_root->ToggleFocusMode();
|
||||
}
|
||||
});
|
||||
_root->Create();
|
||||
|
||||
_ApplyTheme(_settings.GlobalSettings().Theme());
|
||||
_ApplyTheme(_settings->GlobalSettings().Theme());
|
||||
_ApplyStartupTaskStateChange();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"AppCreated",
|
||||
TraceLoggingDescription("Event emitted when the application is started"),
|
||||
TraceLoggingBool(_settings.GlobalSettings().ShowTabsInTitlebar(), "TabsInTitlebar"),
|
||||
TraceLoggingBool(_settings->GlobalSettings().ShowTabsInTitlebar(), "TabsInTitlebar"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
@@ -326,7 +310,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// details here, but it does have the desired effect.
|
||||
// It's not enough to set the theme on the dialog alone.
|
||||
auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) {
|
||||
auto theme{ _settings.GlobalSettings().Theme() };
|
||||
auto theme{ _settings->GlobalSettings().Theme() };
|
||||
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
|
||||
while (element)
|
||||
{
|
||||
@@ -416,7 +400,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Make sure the lines of text wrap
|
||||
warningsTextBlock.TextWrapping(TextWrapping::Wrap);
|
||||
|
||||
const auto warnings = _settings.Warnings();
|
||||
const auto& warnings = _settings->GetWarnings();
|
||||
for (const auto& warning : warnings)
|
||||
{
|
||||
// Try looking up the warning message key for each warning.
|
||||
@@ -499,7 +483,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Use the default profile to determine how big of a window we need.
|
||||
const auto [_, settings] = TerminalSettings::BuildSettings(_settings, nullptr, nullptr);
|
||||
const auto [_, settings] = _settings->BuildSettings(nullptr);
|
||||
|
||||
auto proposedSize = TermControl::GetProposedDimensions(settings, dpi);
|
||||
|
||||
@@ -508,7 +492,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// GH#2061 - 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.
|
||||
if (_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
if (_settings->GlobalSettings().ShowTabsInTitlebar())
|
||||
{
|
||||
// If we're showing the tabs in the titlebar, we need to use a
|
||||
// TitlebarControl here to calculate how much space to reserve.
|
||||
@@ -522,7 +506,7 @@ namespace winrt::TerminalApp::implementation
|
||||
titlebar.Measure({ SHRT_MAX, SHRT_MAX });
|
||||
proposedSize.Height += (titlebar.DesiredSize().Height) * scale;
|
||||
}
|
||||
else if (_settings.GlobalSettings().AlwaysShowTabs())
|
||||
else if (_settings->GlobalSettings().AlwaysShowTabs())
|
||||
{
|
||||
// Otherwise, let's use a TabRowControl to calculate how much extra
|
||||
// space we'll need.
|
||||
@@ -560,7 +544,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// GH#4620/#5801 - If the user passed --maximized or --fullscreen on the
|
||||
// commandline, then use that to override the value from the settings.
|
||||
const auto valueFromSettings = _settings.GlobalSettings().LaunchMode();
|
||||
const auto valueFromSettings = _settings->GlobalSettings().LaunchMode();
|
||||
const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode();
|
||||
return valueFromCommandlineArgs.has_value() ?
|
||||
valueFromCommandlineArgs.value() :
|
||||
@@ -577,7 +561,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - defaultInitialY: the system default y coordinate value
|
||||
// Return Value:
|
||||
// - a point containing the requested initial position in pixels.
|
||||
TerminalApp::InitialPosition AppLogic::GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY)
|
||||
winrt::Windows::Foundation::Point AppLogic::GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY)
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
@@ -585,11 +569,13 @@ namespace winrt::TerminalApp::implementation
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
const auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
|
||||
return {
|
||||
initialPosition.X ? initialPosition.X.Value() : defaultInitialX,
|
||||
initialPosition.Y ? initialPosition.Y.Value() : defaultInitialY
|
||||
const auto initialPosition{ _settings->GlobalSettings().InitialPosition() };
|
||||
winrt::Windows::Foundation::Point point{
|
||||
/* X */ gsl::narrow_cast<float>(initialPosition.x.value_or(defaultInitialX)),
|
||||
/* Y */ gsl::narrow_cast<float>(initialPosition.y.value_or(defaultInitialY))
|
||||
};
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
|
||||
@@ -600,7 +586,7 @@ namespace winrt::TerminalApp::implementation
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().Theme();
|
||||
return _settings->GlobalSettings().Theme();
|
||||
}
|
||||
|
||||
bool AppLogic::GetShowTabsInTitlebar()
|
||||
@@ -611,7 +597,7 @@ namespace winrt::TerminalApp::implementation
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().ShowTabsInTitlebar();
|
||||
return _settings->GlobalSettings().ShowTabsInTitlebar();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -632,20 +618,9 @@ namespace winrt::TerminalApp::implementation
|
||||
try
|
||||
{
|
||||
auto newSettings = _isUwp ? CascadiaSettings::LoadUniversal() : CascadiaSettings::LoadAll();
|
||||
_settings = newSettings;
|
||||
|
||||
if (_settings.GetLoadingError())
|
||||
{
|
||||
_settingsLoadExceptionText = _GetErrorText(_settings.GetLoadingError().Value());
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
else if (!_settings.GetSerializationErrorMessage().empty())
|
||||
{
|
||||
_settingsLoadExceptionText = _settings.GetSerializationErrorMessage();
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
hr = _settings.Warnings().Size() == 0 ? S_OK : S_FALSE;
|
||||
_settings = std::move(newSettings);
|
||||
const auto& warnings = _settings->GetWarnings();
|
||||
hr = warnings.size() == 0 ? S_OK : S_FALSE;
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
@@ -653,6 +628,17 @@ namespace winrt::TerminalApp::implementation
|
||||
_settingsLoadExceptionText = e.message();
|
||||
LOG_HR(hr);
|
||||
}
|
||||
catch (const ::TerminalApp::SettingsException& ex)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
_settingsLoadExceptionText = _GetErrorText(ex.Error());
|
||||
}
|
||||
catch (const ::TerminalApp::SettingsTypedDeserializationException& e)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
std::string_view what{ e.what() };
|
||||
_settingsLoadExceptionText = til::u8u16(what);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
hr = wil::ResultFromCaughtException();
|
||||
@@ -709,8 +695,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Register for directory change notification.
|
||||
_RegisterSettingsChange();
|
||||
|
||||
Jumplist::UpdateJumplist(_settings);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -723,7 +707,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void AppLogic::_RegisterSettingsChange()
|
||||
{
|
||||
// Get the containing folder.
|
||||
const std::filesystem::path settingsPath{ std::wstring_view{ CascadiaSettings::SettingsPath() } };
|
||||
const auto settingsPath{ CascadiaSettings::GetSettingsPath() };
|
||||
const auto folder = settingsPath.parent_path();
|
||||
|
||||
_reader.create(folder.c_str(),
|
||||
@@ -789,7 +773,7 @@ namespace winrt::TerminalApp::implementation
|
||||
co_await winrt::resume_foreground(_root->Dispatcher());
|
||||
|
||||
// Refresh the UI theme
|
||||
_ApplyTheme(_settings.GlobalSettings().Theme());
|
||||
_ApplyTheme(_settings->GlobalSettings().Theme());
|
||||
}
|
||||
|
||||
fire_and_forget AppLogic::_ApplyStartupTaskStateChange()
|
||||
@@ -808,7 +792,7 @@ namespace winrt::TerminalApp::implementation
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
StartupTaskState state;
|
||||
bool tryEnableStartupTask = _settings.GlobalSettings().StartOnUserLogin();
|
||||
bool tryEnableStartupTask = _settings->GlobalSettings().StartOnUserLogin();
|
||||
StartupTask task = co_await StartupTask::GetAsync(StartupTaskName);
|
||||
|
||||
state = task.State();
|
||||
@@ -869,13 +853,11 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
_RefreshThemeRoutine();
|
||||
_ApplyStartupTaskStateChange();
|
||||
|
||||
Jumplist::UpdateJumplist(_settings);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns a pointer to the global shared settings.
|
||||
[[nodiscard]] CascadiaSettings AppLogic::GetSettings() const noexcept
|
||||
[[nodiscard]] std::shared_ptr<::TerminalApp::CascadiaSettings> AppLogic::GetSettings() const noexcept
|
||||
{
|
||||
return _settings;
|
||||
}
|
||||
@@ -1046,6 +1028,65 @@ namespace winrt::TerminalApp::implementation
|
||||
return _appArgs.ShouldExitEarly();
|
||||
}
|
||||
|
||||
winrt::hstring AppLogic::ApplicationDisplayName() const
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
return package.DisplayName();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"ApplicationDisplayNameUnpackaged");
|
||||
}
|
||||
|
||||
winrt::hstring AppLogic::ApplicationVersion() const
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto version{ package.Id().Version() };
|
||||
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u.%u", version.Major, version.Minor, version.Build, version.Revision) };
|
||||
return formatted;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
// Try to get the version the old-fashioned way
|
||||
try
|
||||
{
|
||||
struct LocalizationInfo
|
||||
{
|
||||
WORD language, codepage;
|
||||
};
|
||||
// Use the current module instance handle for TerminalApp.dll, nullptr for WindowsTerminal.exe
|
||||
auto filename{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
||||
auto size{ GetFileVersionInfoSizeExW(0, filename.c_str(), nullptr) };
|
||||
THROW_LAST_ERROR_IF(size == 0);
|
||||
auto versionBuffer{ std::make_unique<std::byte[]>(size) };
|
||||
THROW_IF_WIN32_BOOL_FALSE(GetFileVersionInfoExW(0, filename.c_str(), 0, size, versionBuffer.get()));
|
||||
|
||||
// Get the list of Version localizations
|
||||
LocalizationInfo* pVarLocalization{ nullptr };
|
||||
UINT varLen{ 0 };
|
||||
THROW_IF_WIN32_BOOL_FALSE(VerQueryValueW(versionBuffer.get(), L"\\VarFileInfo\\Translation", reinterpret_cast<void**>(&pVarLocalization), &varLen));
|
||||
THROW_HR_IF(E_UNEXPECTED, varLen < sizeof(*pVarLocalization)); // there must be at least one translation
|
||||
|
||||
// Get the product version from the localized version compartment
|
||||
// We're using String/ProductVersion here because our build pipeline puts more rich information in it (like the branch name)
|
||||
// than in the unlocalized numeric version fields.
|
||||
WCHAR* pProductVersion{ nullptr };
|
||||
UINT versionLen{ 0 };
|
||||
const auto localizedVersionName{ wil::str_printf<std::wstring>(L"\\StringFileInfo\\%04x%04x\\ProductVersion",
|
||||
pVarLocalization->language ? pVarLocalization->language : 0x0409, // well-known en-US LCID
|
||||
pVarLocalization->codepage) };
|
||||
THROW_IF_WIN32_BOOL_FALSE(VerQueryValueW(versionBuffer.get(), localizedVersionName.c_str(), reinterpret_cast<void**>(&pProductVersion), &versionLen));
|
||||
return { pProductVersion };
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"ApplicationVersionUnknown");
|
||||
}
|
||||
|
||||
bool AppLogic::FocusMode() const
|
||||
{
|
||||
return _root ? _root->FocusMode() : false;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "AppLogic.g.h"
|
||||
|
||||
#include "Tab.h"
|
||||
#include "CascadiaSettings.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "Jumplist.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
@@ -16,7 +16,6 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
public:
|
||||
static AppLogic* Current() noexcept;
|
||||
static const Microsoft::Terminal::Settings::Model::CascadiaSettings CurrentAppSettings();
|
||||
|
||||
AppLogic();
|
||||
~AppLogic() = default;
|
||||
@@ -26,20 +25,22 @@ namespace winrt::TerminalApp::implementation
|
||||
void RunAsUwp();
|
||||
bool IsElevated() const noexcept;
|
||||
void LoadSettings();
|
||||
[[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
|
||||
[[nodiscard]] std::shared_ptr<::TerminalApp::CascadiaSettings> GetSettings() const noexcept;
|
||||
|
||||
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
|
||||
winrt::hstring ParseCommandlineMessage();
|
||||
bool ShouldExitEarly();
|
||||
|
||||
winrt::hstring ApplicationDisplayName() const;
|
||||
winrt::hstring ApplicationVersion() const;
|
||||
bool FocusMode() const;
|
||||
bool Fullscreen() const;
|
||||
bool AlwaysOnTop() const;
|
||||
|
||||
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
|
||||
TerminalApp::InitialPosition GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY);
|
||||
winrt::Windows::Foundation::Point GetLaunchInitialPositions(int32_t defaultInitialX, int32_t defaultInitialY);
|
||||
winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme();
|
||||
Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode();
|
||||
LaunchMode GetLaunchMode();
|
||||
bool GetShowTabsInTitlebar();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
|
||||
@@ -67,7 +68,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// updated in _ApplyTheme. The root currently is _root.
|
||||
winrt::com_ptr<TerminalPage> _root{ nullptr };
|
||||
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr };
|
||||
|
||||
HRESULT _settingsLoadedResult;
|
||||
winrt::hstring _settingsLoadExceptionText{};
|
||||
|
||||
@@ -7,10 +7,11 @@ import "IDirectKeyListener.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
struct InitialPosition
|
||||
enum LaunchMode
|
||||
{
|
||||
Int64 X;
|
||||
Int64 Y;
|
||||
DefaultMode,
|
||||
MaximizedMode,
|
||||
FullscreenMode,
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter
|
||||
@@ -37,15 +38,18 @@ namespace TerminalApp
|
||||
|
||||
String Title { get; };
|
||||
|
||||
String ApplicationDisplayName { get; };
|
||||
String ApplicationVersion { get; };
|
||||
|
||||
Boolean FocusMode { get; };
|
||||
Boolean Fullscreen { get; };
|
||||
Boolean AlwaysOnTop { get; };
|
||||
|
||||
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
|
||||
|
||||
InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY);
|
||||
Windows.Foundation.Point GetLaunchInitialPositions(Int32 defaultInitialX, Int32 defaultInitialY);
|
||||
Windows.UI.Xaml.ElementTheme GetRequestedTheme();
|
||||
Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode();
|
||||
LaunchMode GetLaunchMode();
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
|
||||
void TitlebarClicked();
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
|
||||
#include "AzureCloudShellGenerator.h"
|
||||
#include "LegacyProfileGeneratorNamespaces.h"
|
||||
|
||||
@@ -11,9 +13,7 @@
|
||||
#include "Utils.h"
|
||||
#include "DefaultProfileUtils.h"
|
||||
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
std::wstring_view AzureCloudShellGenerator::GetNamespace()
|
||||
{
|
||||
@@ -27,17 +27,17 @@ std::wstring_view AzureCloudShellGenerator::GetNamespace()
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a vector with the Azure Cloud Shell connection profile, if available.
|
||||
std::vector<Profile> AzureCloudShellGenerator::GenerateProfiles()
|
||||
std::vector<TerminalApp::Profile> AzureCloudShellGenerator::GenerateProfiles()
|
||||
{
|
||||
std::vector<Profile> profiles;
|
||||
std::vector<TerminalApp::Profile> profiles;
|
||||
|
||||
if (AzureConnection::IsAzureConnectionAvailable())
|
||||
if (winrt::Microsoft::Terminal::TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
|
||||
{
|
||||
auto azureCloudShellProfile{ CreateDefaultProfile(L"Azure Cloud Shell") };
|
||||
azureCloudShellProfile.Commandline(L"Azure");
|
||||
azureCloudShellProfile.StartingDirectory(DEFAULT_STARTING_DIRECTORY);
|
||||
azureCloudShellProfile.ColorSchemeName(L"Vintage");
|
||||
azureCloudShellProfile.ConnectionType(AzureConnection::ConnectionType());
|
||||
azureCloudShellProfile.SetCommandline(L"Azure");
|
||||
azureCloudShellProfile.SetStartingDirectory(DEFAULT_STARTING_DIRECTORY);
|
||||
azureCloudShellProfile.SetColorScheme({ L"Vintage" });
|
||||
azureCloudShellProfile.SetConnectionType(AzureConnectionType);
|
||||
profiles.emplace_back(azureCloudShellProfile);
|
||||
}
|
||||
|
||||
@@ -18,15 +18,17 @@ Author(s):
|
||||
#pragma once
|
||||
#include "IDynamicProfileGenerator.h"
|
||||
|
||||
namespace Microsoft::Terminal::Settings::Model
|
||||
static constexpr GUID AzureConnectionType = { 0xd9fcfdfa, 0xa479, 0x412c, { 0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62 } };
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
class AzureCloudShellGenerator : public IDynamicProfileGenerator
|
||||
class AzureCloudShellGenerator : public TerminalApp::IDynamicProfileGenerator
|
||||
{
|
||||
public:
|
||||
AzureCloudShellGenerator() = default;
|
||||
~AzureCloudShellGenerator() = default;
|
||||
std::wstring_view GetNamespace() override;
|
||||
|
||||
std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() override;
|
||||
std::vector<TerminalApp::Profile> GenerateProfiles() override;
|
||||
};
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "CascadiaSettings.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "AppLogic.h"
|
||||
#include "Utils.h"
|
||||
#include "LibraryResources.h"
|
||||
|
||||
@@ -16,12 +17,9 @@
|
||||
#include "WslDistroGenerator.h"
|
||||
#include "AzureCloudShellGenerator.h"
|
||||
|
||||
#include "CascadiaSettings.g.cpp"
|
||||
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using namespace ::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace Microsoft::Console;
|
||||
|
||||
static constexpr std::wstring_view PACKAGED_PROFILE_ICON_PATH{ L"ms-appx:///ProfileIcons/" };
|
||||
@@ -32,6 +30,17 @@ static constexpr std::wstring_view DEFAULT_LINUX_ICON_GUID{ L"{9acb9455-ca41-5af
|
||||
// make sure this matches defaults.json.
|
||||
static constexpr std::wstring_view DEFAULT_WINDOWS_POWERSHELL_GUID{ L"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" };
|
||||
|
||||
// Method Description:
|
||||
// - Returns the settings currently in use by the entire Terminal application.
|
||||
// Throws:
|
||||
// - HR E_INVALIDARG if the app isn't up and running.
|
||||
const CascadiaSettings& CascadiaSettings::GetCurrentAppSettings()
|
||||
{
|
||||
auto appLogic{ ::winrt::TerminalApp::implementation::AppLogic::Current() };
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, appLogic);
|
||||
return *(appLogic->GetSettings());
|
||||
}
|
||||
|
||||
CascadiaSettings::CascadiaSettings() :
|
||||
CascadiaSettings(true)
|
||||
{
|
||||
@@ -43,11 +52,7 @@ CascadiaSettings::CascadiaSettings() :
|
||||
// generators. Set this to `false` for unit testing.
|
||||
// Arguments:
|
||||
// - addDynamicProfiles: if true, we'll add the built-in DPGs.
|
||||
CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles) :
|
||||
_globals{ winrt::make_self<implementation::GlobalAppSettings>() },
|
||||
_profiles{ winrt::single_threaded_observable_vector<Model::Profile>() },
|
||||
_warnings{ winrt::single_threaded_vector<SettingsLoadWarnings>() },
|
||||
_deserializationErrorMessage{ L"" }
|
||||
CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles)
|
||||
{
|
||||
if (addDynamicProfiles)
|
||||
{
|
||||
@@ -57,15 +62,6 @@ CascadiaSettings::CascadiaSettings(const bool addDynamicProfiles) :
|
||||
}
|
||||
}
|
||||
|
||||
CascadiaSettings::CascadiaSettings(winrt::hstring json) :
|
||||
CascadiaSettings(false)
|
||||
{
|
||||
const auto jsonString{ til::u16u8(json) };
|
||||
_ParseJsonString(jsonString, false);
|
||||
LayerJson(_userSettings);
|
||||
_ValidateSettings();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Finds a profile that matches the given GUID. If there is no profile in this
|
||||
// settings object that matches, returns nullptr.
|
||||
@@ -74,16 +70,15 @@ CascadiaSettings::CascadiaSettings(winrt::hstring json) :
|
||||
// Return Value:
|
||||
// - a non-ownership pointer to the profile matching the given guid, or nullptr
|
||||
// if there is no match.
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::FindProfile(winrt::guid profileGuid) const noexcept
|
||||
const Profile* CascadiaSettings::FindProfile(GUID profileGuid) const noexcept
|
||||
{
|
||||
const winrt::guid guid{ profileGuid };
|
||||
for (auto profile : _profiles)
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (profile.Guid() == guid)
|
||||
if (profile.GetGuid() == profileGuid)
|
||||
{
|
||||
return profile;
|
||||
return &profile;
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
@@ -97,9 +92,9 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::FindProfi
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - an iterable collection of all of our Profiles.
|
||||
IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Profile> CascadiaSettings::Profiles() const noexcept
|
||||
gsl::span<const Profile> CascadiaSettings::GetProfiles() const noexcept
|
||||
{
|
||||
return _profiles;
|
||||
return { &_profiles[0], _profiles.size() };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -108,9 +103,9 @@ IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Profile> Cascadia
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the globally configured keybindings
|
||||
winrt::Microsoft::Terminal::Settings::Model::KeyMapping CascadiaSettings::KeyMap() const noexcept
|
||||
AppKeyBindings CascadiaSettings::GetKeybindings() const noexcept
|
||||
{
|
||||
return _globals->KeyMap();
|
||||
return _globals.GetKeybindings();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -119,9 +114,9 @@ winrt::Microsoft::Terminal::Settings::Model::KeyMapping CascadiaSettings::KeyMap
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a reference to our global settings
|
||||
winrt::Microsoft::Terminal::Settings::Model::GlobalAppSettings CascadiaSettings::GlobalSettings() const
|
||||
GlobalAppSettings& CascadiaSettings::GlobalSettings()
|
||||
{
|
||||
return *_globals;
|
||||
return _globals;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -129,19 +124,9 @@ winrt::Microsoft::Terminal::Settings::Model::GlobalAppSettings CascadiaSettings:
|
||||
// knew were bad when we called `_ValidateSettings` last.
|
||||
// Return Value:
|
||||
// - a reference to our list of warnings.
|
||||
IVectorView<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> CascadiaSettings::Warnings()
|
||||
std::vector<TerminalApp::SettingsLoadWarnings>& CascadiaSettings::GetWarnings()
|
||||
{
|
||||
return _warnings.GetView();
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IReference<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadErrors> CascadiaSettings::GetLoadingError()
|
||||
{
|
||||
return _loadError;
|
||||
}
|
||||
|
||||
winrt::hstring CascadiaSettings::GetSerializationErrorMessage()
|
||||
{
|
||||
return _deserializationErrorMessage;
|
||||
return _warnings;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -156,7 +141,7 @@ winrt::hstring CascadiaSettings::GetSerializationErrorMessage()
|
||||
// - <none>
|
||||
void CascadiaSettings::_ValidateSettings()
|
||||
{
|
||||
_warnings.Clear();
|
||||
_warnings.clear();
|
||||
|
||||
// Make sure to check that profiles exists at all first and foremost:
|
||||
_ValidateProfilesExist();
|
||||
@@ -209,7 +194,7 @@ void CascadiaSettings::_ValidateSettings()
|
||||
// profiles at all, we'll throw an error if there aren't any profiles.
|
||||
void CascadiaSettings::_ValidateProfilesExist()
|
||||
{
|
||||
const bool hasProfiles = _profiles.Size() > 0;
|
||||
const bool hasProfiles = !_profiles.empty();
|
||||
if (!hasProfiles)
|
||||
{
|
||||
// Throw an exception. This is an invalid state, and we want the app to
|
||||
@@ -218,7 +203,7 @@ void CascadiaSettings::_ValidateProfilesExist()
|
||||
// We can't add the warning to the list of warnings here, because this
|
||||
// object is not going to be returned at any point.
|
||||
|
||||
throw SettingsException(Microsoft::Terminal::Settings::Model::SettingsLoadErrors::NoProfiles);
|
||||
throw ::TerminalApp::SettingsException(::TerminalApp::SettingsLoadErrors::NoProfiles);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,10 +213,9 @@ void CascadiaSettings::_ValidateProfilesExist()
|
||||
// temporary runtime GUID for it. This validation does not add any warnings.
|
||||
void CascadiaSettings::_ValidateProfilesHaveGuid()
|
||||
{
|
||||
for (auto profile : _profiles)
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
auto profileImpl = winrt::get_self<implementation::Profile>(profile);
|
||||
profileImpl->GenerateGuidIfNecessary();
|
||||
profile.GenerateGuidIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +225,10 @@ void CascadiaSettings::_ValidateProfilesHaveGuid()
|
||||
void CascadiaSettings::_ResolveDefaultProfile()
|
||||
{
|
||||
const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() };
|
||||
if (!unparsedDefaultProfile.empty())
|
||||
if (unparsedDefaultProfile)
|
||||
{
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ til::coalesce_value(maybeParsedDefaultProfile, winrt::guid{}) };
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(*unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ til::coalesce_value(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
}
|
||||
}
|
||||
@@ -258,12 +242,12 @@ void CascadiaSettings::_ResolveDefaultProfile()
|
||||
// warnings if we failed to find the default.
|
||||
void CascadiaSettings::_ValidateDefaultProfileExists()
|
||||
{
|
||||
const winrt::guid defaultProfileGuid{ GlobalSettings().DefaultProfile() };
|
||||
const bool nullDefaultProfile = defaultProfileGuid == winrt::guid{};
|
||||
const auto defaultProfileGuid = GlobalSettings().DefaultProfile();
|
||||
const bool nullDefaultProfile = defaultProfileGuid == GUID{};
|
||||
bool defaultProfileNotInProfiles = true;
|
||||
for (const auto& profile : _profiles)
|
||||
{
|
||||
if (profile.Guid() == defaultProfileGuid)
|
||||
if (profile.GetGuid() == defaultProfileGuid)
|
||||
{
|
||||
defaultProfileNotInProfiles = false;
|
||||
break;
|
||||
@@ -272,12 +256,12 @@ void CascadiaSettings::_ValidateDefaultProfileExists()
|
||||
|
||||
if (nullDefaultProfile || defaultProfileNotInProfiles)
|
||||
{
|
||||
_warnings.Append(Microsoft::Terminal::Settings::Model::SettingsLoadWarnings::MissingDefaultProfile);
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile);
|
||||
// Use the first profile as the new default
|
||||
|
||||
// _temporarily_ set the default profile to the first profile. Because
|
||||
// we're adding a warning, this settings change won't be re-serialized.
|
||||
GlobalSettings().DefaultProfile(_profiles.GetAt(0).Guid());
|
||||
GlobalSettings().DefaultProfile(_profiles[0].GetGuid());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,15 +275,15 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles()
|
||||
{
|
||||
bool foundDupe = false;
|
||||
|
||||
std::vector<uint32_t> indicesToDelete;
|
||||
std::vector<size_t> indicesToDelete;
|
||||
|
||||
std::set<winrt::guid> uniqueGuids;
|
||||
std::set<GUID> uniqueGuids;
|
||||
|
||||
// Try collecting all the unique guids. If we ever encounter a guid that's
|
||||
// already in the set, then we need to delete that profile.
|
||||
for (uint32_t i = 0; i < _profiles.Size(); i++)
|
||||
for (size_t i = 0; i < _profiles.size(); i++)
|
||||
{
|
||||
if (!uniqueGuids.insert(_profiles.GetAt(i).Guid()).second)
|
||||
if (!uniqueGuids.insert(_profiles.at(i).GetGuid()).second)
|
||||
{
|
||||
foundDupe = true;
|
||||
indicesToDelete.push_back(i);
|
||||
@@ -310,12 +294,12 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles()
|
||||
// Walk backwards, so we don't accidentally shift any of the elements
|
||||
for (auto iter = indicesToDelete.rbegin(); iter != indicesToDelete.rend(); iter++)
|
||||
{
|
||||
_profiles.RemoveAt(*iter);
|
||||
_profiles.erase(_profiles.begin() + *iter);
|
||||
}
|
||||
|
||||
if (foundDupe)
|
||||
{
|
||||
_warnings.Append(Microsoft::Terminal::Settings::Model::SettingsLoadWarnings::DuplicateProfile);
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::DuplicateProfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,15 +314,15 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles()
|
||||
// - <none>
|
||||
void CascadiaSettings::_ReorderProfilesToMatchUserSettingsOrder()
|
||||
{
|
||||
std::set<winrt::guid> uniqueGuids;
|
||||
std::deque<winrt::guid> guidOrder;
|
||||
std::set<GUID> uniqueGuids;
|
||||
std::deque<GUID> guidOrder;
|
||||
|
||||
auto collectGuids = [&](const auto& json) {
|
||||
for (auto profileJson : _GetProfilesJsonObject(json))
|
||||
{
|
||||
if (profileJson.isObject())
|
||||
{
|
||||
auto guid = implementation::Profile::GetGuidOrGenerateForJson(profileJson);
|
||||
auto guid = Profile::GetGuidOrGenerateForJson(profileJson);
|
||||
if (uniqueGuids.insert(guid).second)
|
||||
{
|
||||
guidOrder.push_back(guid);
|
||||
@@ -352,23 +336,21 @@ void CascadiaSettings::_ReorderProfilesToMatchUserSettingsOrder()
|
||||
|
||||
// Push all the defaultSettings profiles' GUIDS into the set
|
||||
collectGuids(_defaultSettings);
|
||||
std::equal_to<winrt::guid> equals;
|
||||
std::equal_to<GUID> equals;
|
||||
// Re-order the list of _profiles to match that ordering
|
||||
// for (gIndex=0 -> uniqueGuids.size)
|
||||
// pIndex = the pIndex of the profile with guid==guids[gIndex]
|
||||
// profiles.swap(pIndex <-> gIndex)
|
||||
// This is O(N^2), which is kinda rough. I'm sure there's a better way
|
||||
for (uint32_t gIndex = 0; gIndex < guidOrder.size(); gIndex++)
|
||||
for (size_t gIndex = 0; gIndex < guidOrder.size(); gIndex++)
|
||||
{
|
||||
const auto guid = guidOrder.at(gIndex);
|
||||
for (uint32_t pIndex = gIndex; pIndex < _profiles.Size(); pIndex++)
|
||||
for (size_t pIndex = gIndex; pIndex < _profiles.size(); pIndex++)
|
||||
{
|
||||
auto profileGuid = _profiles.GetAt(pIndex).Guid();
|
||||
auto profileGuid = _profiles.at(pIndex).GetGuid();
|
||||
if (equals(profileGuid, guid))
|
||||
{
|
||||
auto prof1 = _profiles.GetAt(pIndex);
|
||||
_profiles.SetAt(pIndex, _profiles.GetAt(gIndex));
|
||||
_profiles.SetAt(gIndex, prof1);
|
||||
std::iter_swap(_profiles.begin() + pIndex, _profiles.begin() + gIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -384,27 +366,24 @@ void CascadiaSettings::_ReorderProfilesToMatchUserSettingsOrder()
|
||||
// - <none>
|
||||
void CascadiaSettings::_RemoveHiddenProfiles()
|
||||
{
|
||||
for (uint32_t i = 0; i < _profiles.Size();)
|
||||
{
|
||||
if (_profiles.GetAt(i).Hidden())
|
||||
{
|
||||
// remove hidden profile, don't increment 'i'
|
||||
_profiles.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
// remove_if will move all the profiles where the lambda is true to the end
|
||||
// of the list, then return a iterator to the point in the list where those
|
||||
// profiles start. The erase call will then remove all of those profiles
|
||||
// from the list. This is the [erase-remove
|
||||
// idiom](https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom)
|
||||
_profiles.erase(std::remove_if(_profiles.begin(),
|
||||
_profiles.end(),
|
||||
[](auto&& profile) { return profile.IsHidden(); }),
|
||||
_profiles.end());
|
||||
|
||||
// Ensure that we still have some profiles here. If we don't, then throw an
|
||||
// exception, so the app can use the defaults.
|
||||
const bool hasProfiles = _profiles.Size() > 0;
|
||||
const bool hasProfiles = !_profiles.empty();
|
||||
if (!hasProfiles)
|
||||
{
|
||||
// Throw an exception. This is an invalid state, and we want the app to
|
||||
// be able to gracefully use the default settings.
|
||||
throw SettingsException(SettingsLoadErrors::AllProfilesHidden);
|
||||
throw ::TerminalApp::SettingsException(::TerminalApp::SettingsLoadErrors::AllProfilesHidden);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,19 +400,23 @@ void CascadiaSettings::_RemoveHiddenProfiles()
|
||||
void CascadiaSettings::_ValidateAllSchemesExist()
|
||||
{
|
||||
bool foundInvalidScheme = false;
|
||||
for (auto profile : _profiles)
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
const auto schemeName = profile.ColorSchemeName();
|
||||
if (!_globals->ColorSchemes().HasKey(schemeName))
|
||||
auto schemeName = profile.GetSchemeName();
|
||||
if (schemeName.has_value())
|
||||
{
|
||||
profile.ColorSchemeName({ L"Campbell" });
|
||||
foundInvalidScheme = true;
|
||||
const auto found = _globals.GetColorSchemes().find(schemeName.value());
|
||||
if (found == _globals.GetColorSchemes().end())
|
||||
{
|
||||
profile.SetColorScheme({ L"Campbell" });
|
||||
foundInvalidScheme = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundInvalidScheme)
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::UnknownColorScheme);
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::UnknownColorScheme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,56 +436,109 @@ void CascadiaSettings::_ValidateMediaResources()
|
||||
bool invalidBackground{ false };
|
||||
bool invalidIcon{ false };
|
||||
|
||||
for (auto profile : _profiles)
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
if (!profile.BackgroundImagePath().empty())
|
||||
if (profile.HasBackgroundImage())
|
||||
{
|
||||
// Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable.
|
||||
// This covers file paths on the machine, app data, URLs, and other resource paths.
|
||||
try
|
||||
{
|
||||
winrt::Windows::Foundation::Uri imagePath{ profile.ExpandedBackgroundImagePath() };
|
||||
winrt::Windows::Foundation::Uri imagePath{ profile.GetExpandedBackgroundImagePath() };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// reset background image path
|
||||
profile.BackgroundImagePath(L"");
|
||||
profile.ResetBackgroundImagePath();
|
||||
invalidBackground = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!profile.Icon().empty())
|
||||
if (profile.HasIcon())
|
||||
{
|
||||
const auto iconPath{ wil::ExpandEnvironmentStringsW<std::wstring>(profile.Icon().c_str()) };
|
||||
try
|
||||
{
|
||||
winrt::Windows::Foundation::Uri imagePath{ iconPath };
|
||||
winrt::Windows::Foundation::Uri imagePath{ profile.GetExpandedIconPath() };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Anything longer than 2 wchar_t's _isn't_ an emoji or symbol,
|
||||
// so treat it as an invalid path.
|
||||
if (iconPath.size() > 2)
|
||||
{
|
||||
// reset icon path
|
||||
profile.Icon(L"");
|
||||
invalidIcon = true;
|
||||
}
|
||||
profile.ResetIconPath();
|
||||
invalidIcon = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidBackground)
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::InvalidBackgroundImage);
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::InvalidBackgroundImage);
|
||||
}
|
||||
|
||||
if (invalidIcon)
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::InvalidIcon);
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::InvalidIcon);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a TerminalSettings object for the provided newTerminalArgs. We'll
|
||||
// use the newTerminalArgs to look up the profile that should be used to
|
||||
// create these TerminalSettings. Then, we'll apply settings contained in the
|
||||
// newTerminalArgs to the profile's settings, to enable customization on top
|
||||
// of the profile's default values.
|
||||
// Arguments:
|
||||
// - newTerminalArgs: An object that may contain a profile name or GUID to
|
||||
// actually use. If the Profile value is not a guid, we'll treat it as a name,
|
||||
// and attempt to look the profile up by name instead.
|
||||
// * Additionally, we'll use other values (such as Commandline,
|
||||
// StartingDirectory) in this object to override the settings directly from
|
||||
// the profile.
|
||||
// Return Value:
|
||||
// - the GUID of the created profile, and a fully initialized TerminalSettings object
|
||||
std::tuple<GUID, TerminalSettings> CascadiaSettings::BuildSettings(const NewTerminalArgs& newTerminalArgs) const
|
||||
{
|
||||
const GUID profileGuid = _GetProfileForArgs(newTerminalArgs);
|
||||
auto settings = BuildSettings(profileGuid);
|
||||
|
||||
if (newTerminalArgs)
|
||||
{
|
||||
// Override commandline, starting directory if they exist in newTerminalArgs
|
||||
if (!newTerminalArgs.Commandline().empty())
|
||||
{
|
||||
settings.Commandline(newTerminalArgs.Commandline());
|
||||
}
|
||||
if (!newTerminalArgs.StartingDirectory().empty())
|
||||
{
|
||||
settings.StartingDirectory(newTerminalArgs.StartingDirectory());
|
||||
}
|
||||
if (!newTerminalArgs.TabTitle().empty())
|
||||
{
|
||||
settings.StartingTitle(newTerminalArgs.TabTitle());
|
||||
}
|
||||
}
|
||||
|
||||
return { profileGuid, settings };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a TerminalSettings object for the profile with a GUID matching the
|
||||
// provided GUID. If no profile matches this GUID, then this method will
|
||||
// throw.
|
||||
// Arguments:
|
||||
// - profileGuid: The GUID of a profile to use to create a settings object for.
|
||||
// Return Value:
|
||||
// - the GUID of the created profile, and a fully initialized TerminalSettings object
|
||||
TerminalSettings CascadiaSettings::BuildSettings(GUID profileGuid) const
|
||||
{
|
||||
const Profile* const profile = FindProfile(profileGuid);
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, profile);
|
||||
|
||||
TerminalSettings result = profile->CreateTerminalSettings(_globals.GetColorSchemes());
|
||||
|
||||
// Place our appropriate global settings into the Terminal Settings
|
||||
_globals.ApplyToSettings(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper to get the GUID of a profile, given an optional index and a possible
|
||||
// "profile" value to override that.
|
||||
@@ -519,9 +555,9 @@ void CascadiaSettings::_ValidateMediaResources()
|
||||
// and attempt to look the profile up by name instead.
|
||||
// Return Value:
|
||||
// - the GUID of the profile corresponding to this combination of index and NewTerminalArgs
|
||||
winrt::guid CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const
|
||||
GUID CascadiaSettings::_GetProfileForArgs(const NewTerminalArgs& newTerminalArgs) const
|
||||
{
|
||||
std::optional<winrt::guid> profileByIndex, profileByName;
|
||||
std::optional<GUID> profileByIndex, profileByName;
|
||||
if (newTerminalArgs)
|
||||
{
|
||||
if (newTerminalArgs.ProfileIndex() != nullptr)
|
||||
@@ -532,7 +568,7 @@ winrt::guid CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs& ne
|
||||
profileByName = _GetProfileGuidByName(newTerminalArgs.Profile());
|
||||
}
|
||||
|
||||
return til::coalesce_value(profileByName, profileByIndex, _globals->DefaultProfile());
|
||||
return til::coalesce_value(profileByName, profileByIndex, _globals.DefaultProfile());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -541,7 +577,7 @@ winrt::guid CascadiaSettings::GetProfileForArgs(const Model::NewTerminalArgs& ne
|
||||
// - name: a guid string _or_ the name of a profile
|
||||
// Return Value:
|
||||
// - the GUID of the profile corresponding to this name
|
||||
std::optional<winrt::guid> CascadiaSettings::_GetProfileGuidByName(const winrt::hstring name) const
|
||||
std::optional<GUID> CascadiaSettings::_GetProfileGuidByName(const std::wstring_view name) const
|
||||
try
|
||||
{
|
||||
// First, try and parse the "name" as a GUID. If it's a
|
||||
@@ -565,12 +601,13 @@ try
|
||||
// Here, we were unable to use the profile string as a GUID to
|
||||
// lookup a profile. Instead, try using the string to look the
|
||||
// Profile up by name.
|
||||
for (auto profile : _profiles)
|
||||
const auto profileIterator{ std::find_if(_profiles.cbegin(), _profiles.cend(), [&](auto&& profile) {
|
||||
return profile.GetName().compare(name) == 0;
|
||||
}) };
|
||||
|
||||
if (profileIterator != _profiles.cend())
|
||||
{
|
||||
if (profile.Name() == name)
|
||||
{
|
||||
return profile.Guid();
|
||||
}
|
||||
return profileIterator->GetGuid();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,17 +629,17 @@ catch (...)
|
||||
// If omitted, instead return the default profile's GUID
|
||||
// Return Value:
|
||||
// - the Nth profile's GUID, or the default profile's GUID
|
||||
std::optional<winrt::guid> CascadiaSettings::_GetProfileGuidByIndex(std::optional<int> index) const
|
||||
std::optional<GUID> CascadiaSettings::_GetProfileGuidByIndex(std::optional<int> index) const
|
||||
{
|
||||
if (index)
|
||||
{
|
||||
const auto realIndex{ index.value() };
|
||||
// If we don't have that many profiles, then do nothing.
|
||||
if (realIndex >= 0 &&
|
||||
realIndex < gsl::narrow_cast<decltype(realIndex)>(_profiles.Size()))
|
||||
realIndex < gsl::narrow_cast<decltype(realIndex)>(_profiles.size()))
|
||||
{
|
||||
const auto& selectedProfile = _profiles.GetAt(realIndex);
|
||||
return selectedProfile.Guid();
|
||||
const auto& selectedProfile = _profiles.at(realIndex);
|
||||
return selectedProfile.GetGuid();
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -619,15 +656,12 @@ std::optional<winrt::guid> CascadiaSettings::_GetProfileGuidByIndex(std::optiona
|
||||
// - <none>
|
||||
void CascadiaSettings::_ValidateKeybindings()
|
||||
{
|
||||
auto keybindingWarnings = _globals->KeybindingsWarnings();
|
||||
auto keybindingWarnings = _globals.GetKeybindingsWarnings();
|
||||
|
||||
if (!keybindingWarnings.empty())
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::AtLeastOneKeybindingWarning);
|
||||
for (auto warning : keybindingWarnings)
|
||||
{
|
||||
_warnings.Append(warning);
|
||||
}
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::AtLeastOneKeybindingWarning);
|
||||
_warnings.insert(_warnings.end(), keybindingWarnings.begin(), keybindingWarnings.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,7 +680,7 @@ void CascadiaSettings::_ValidateNoGlobalsKey()
|
||||
{
|
||||
if (auto oldGlobalsProperty{ _userSettings["globals"] })
|
||||
{
|
||||
_warnings.Append(SettingsLoadWarnings::LegacyGlobalsProperty);
|
||||
_warnings.push_back(::TerminalApp::SettingsLoadWarnings::LegacyGlobalsProperty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +701,7 @@ std::string CascadiaSettings::_ApplyFirstRunChangesToSettingsTemplate(std::strin
|
||||
std::string finalSettings{ settingsTemplate };
|
||||
|
||||
std::wstring defaultProfileGuid{ DEFAULT_WINDOWS_POWERSHELL_GUID };
|
||||
if (const auto psCoreProfileGuid{ _GetProfileGuidByName(hstring{ PowershellCoreProfileGenerator::GetPreferredPowershellProfileName() }) })
|
||||
if (const auto psCoreProfileGuid{ _GetProfileGuidByName(PowershellCoreProfileGenerator::GetPreferredPowershellProfileName()) })
|
||||
{
|
||||
defaultProfileGuid = Utils::GuidToString(*psCoreProfileGuid);
|
||||
}
|
||||
@@ -675,13 +709,15 @@ std::string CascadiaSettings::_ApplyFirstRunChangesToSettingsTemplate(std::strin
|
||||
til::replace_needle_in_haystack_inplace(finalSettings,
|
||||
"%DEFAULT_PROFILE%",
|
||||
til::u16u8(defaultProfileGuid));
|
||||
|
||||
til::replace_needle_in_haystack_inplace(finalSettings,
|
||||
"%VERSION%",
|
||||
til::u16u8(ApplicationVersion()));
|
||||
til::replace_needle_in_haystack_inplace(finalSettings,
|
||||
"%PRODUCT%",
|
||||
til::u16u8(ApplicationDisplayName()));
|
||||
if (const auto appLogic{ winrt::TerminalApp::implementation::AppLogic::Current() })
|
||||
{
|
||||
til::replace_needle_in_haystack_inplace(finalSettings,
|
||||
"%VERSION%",
|
||||
til::u16u8(appLogic->ApplicationVersion()));
|
||||
til::replace_needle_in_haystack_inplace(finalSettings,
|
||||
"%PRODUCT%",
|
||||
til::u16u8(appLogic->ApplicationDisplayName()));
|
||||
}
|
||||
|
||||
til::replace_needle_in_haystack_inplace(finalSettings,
|
||||
"%COMMAND_PROMPT_LOCALIZED_NAME%",
|
||||
@@ -698,72 +734,44 @@ std::string CascadiaSettings::_ApplyFirstRunChangesToSettingsTemplate(std::strin
|
||||
// - profileGuid: the GUID of the profile to find the scheme for.
|
||||
// Return Value:
|
||||
// - a non-owning pointer to the scheme.
|
||||
winrt::Microsoft::Terminal::Settings::Model::ColorScheme CascadiaSettings::GetColorSchemeForProfile(const winrt::guid profileGuid) const
|
||||
const ColorScheme CascadiaSettings::GetColorSchemeForProfile(const GUID profileGuid) const
|
||||
{
|
||||
auto profile = FindProfile(profileGuid);
|
||||
auto* profile = FindProfile(profileGuid);
|
||||
if (!profile)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto schemeName = profile.ColorSchemeName();
|
||||
return _globals->ColorSchemes().TryLookup(schemeName);
|
||||
auto schemeName = profile->GetSchemeName().has_value() ? profile->GetSchemeName().value() : L"\0";
|
||||
auto scheme = _globals.GetColorSchemes().find(schemeName);
|
||||
if (scheme != _globals.GetColorSchemes().end())
|
||||
{
|
||||
return scheme->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
winrt::hstring CascadiaSettings::ApplicationDisplayName()
|
||||
// Method Description:
|
||||
// - Apply the color scheme (provided by name) to the given IControlSettings.
|
||||
// The settings are modified in-place.
|
||||
// - If the name doesn't correspond to any of our schemes, this does nothing.
|
||||
// Arguments:
|
||||
// - settings: the IControlSettings object to modify
|
||||
// - name: the name of the scheme to apply
|
||||
// Return Value:
|
||||
// - true iff we found a matching scheme for the name schemeName
|
||||
bool CascadiaSettings::ApplyColorScheme(winrt::Microsoft::Terminal::TerminalControl::IControlSettings& settings,
|
||||
std::wstring_view schemeName)
|
||||
{
|
||||
try
|
||||
std::wstring name{ schemeName };
|
||||
auto schemeAndName = _globals.GetColorSchemes().find(name);
|
||||
if (schemeAndName != _globals.GetColorSchemes().end())
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
return package.DisplayName();
|
||||
const auto& scheme = schemeAndName->second;
|
||||
scheme.ApplyScheme(settings);
|
||||
return true;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"ApplicationDisplayNameUnpackaged");
|
||||
}
|
||||
|
||||
winrt::hstring CascadiaSettings::ApplicationVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto version{ package.Id().Version() };
|
||||
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u.%u", version.Major, version.Minor, version.Build, version.Revision) };
|
||||
return formatted;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
// Try to get the version the old-fashioned way
|
||||
try
|
||||
{
|
||||
struct LocalizationInfo
|
||||
{
|
||||
WORD language, codepage;
|
||||
};
|
||||
// Use the current module instance handle for TerminalApp.dll, nullptr for WindowsTerminal.exe
|
||||
auto filename{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
||||
auto size{ GetFileVersionInfoSizeExW(0, filename.c_str(), nullptr) };
|
||||
THROW_LAST_ERROR_IF(size == 0);
|
||||
auto versionBuffer{ std::make_unique<std::byte[]>(size) };
|
||||
THROW_IF_WIN32_BOOL_FALSE(GetFileVersionInfoExW(0, filename.c_str(), 0, size, versionBuffer.get()));
|
||||
|
||||
// Get the list of Version localizations
|
||||
LocalizationInfo* pVarLocalization{ nullptr };
|
||||
UINT varLen{ 0 };
|
||||
THROW_IF_WIN32_BOOL_FALSE(VerQueryValueW(versionBuffer.get(), L"\\VarFileInfo\\Translation", reinterpret_cast<void**>(&pVarLocalization), &varLen));
|
||||
THROW_HR_IF(E_UNEXPECTED, varLen < sizeof(*pVarLocalization)); // there must be at least one translation
|
||||
|
||||
// Get the product version from the localized version compartment
|
||||
// We're using String/ProductVersion here because our build pipeline puts more rich information in it (like the branch name)
|
||||
// than in the unlocalized numeric version fields.
|
||||
WCHAR* pProductVersion{ nullptr };
|
||||
UINT versionLen{ 0 };
|
||||
const auto localizedVersionName{ wil::str_printf<std::wstring>(L"\\StringFileInfo\\%04x%04x\\ProductVersion",
|
||||
pVarLocalization->language ? pVarLocalization->language : 0x0409, // well-known en-US LCID
|
||||
pVarLocalization->codepage) };
|
||||
THROW_IF_WIN32_BOOL_FALSE(VerQueryValueW(versionBuffer.get(), localizedVersionName.c_str(), reinterpret_cast<void**>(&pProductVersion), &versionLen));
|
||||
return { pProductVersion };
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return RS_(L"ApplicationVersionUnknown");
|
||||
return false;
|
||||
}
|
||||
145
src/cascadia/TerminalApp/CascadiaSettings.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- CascadiaSettings.h
|
||||
|
||||
Abstract:
|
||||
- This class acts as the container for all app settings. It's composed of two
|
||||
parts: Globals, which are app-wide settings, and Profiles, which contain
|
||||
a set of settings that apply to a single instance of the terminal.
|
||||
Also contains the logic for serializing and deserializing this object.
|
||||
|
||||
Author(s):
|
||||
- Mike Griese - March 2019
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include "GlobalAppSettings.h"
|
||||
#include "TerminalWarnings.h"
|
||||
#include "Profile.h"
|
||||
#include "IDynamicProfileGenerator.h"
|
||||
|
||||
#include "ColorScheme.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class ProfileTests;
|
||||
class ColorSchemeTests;
|
||||
class KeyBindingsTests;
|
||||
class TabTests;
|
||||
};
|
||||
namespace TerminalAppUnitTests
|
||||
{
|
||||
class DynamicProfileTests;
|
||||
class JsonTests;
|
||||
};
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
class SettingsTypedDeserializationException;
|
||||
class CascadiaSettings;
|
||||
};
|
||||
|
||||
class TerminalApp::SettingsTypedDeserializationException final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
SettingsTypedDeserializationException(const std::string_view description) :
|
||||
runtime_error(description.data()) {}
|
||||
};
|
||||
|
||||
class TerminalApp::CascadiaSettings final
|
||||
{
|
||||
public:
|
||||
CascadiaSettings();
|
||||
explicit CascadiaSettings(const bool addDynamicProfiles);
|
||||
|
||||
static std::unique_ptr<CascadiaSettings> LoadDefaults();
|
||||
static std::unique_ptr<CascadiaSettings> LoadAll();
|
||||
static std::unique_ptr<CascadiaSettings> LoadUniversal();
|
||||
|
||||
static const CascadiaSettings& GetCurrentAppSettings();
|
||||
|
||||
std::tuple<GUID, winrt::TerminalApp::TerminalSettings> BuildSettings(const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs) const;
|
||||
winrt::TerminalApp::TerminalSettings BuildSettings(GUID profileGuid) const;
|
||||
|
||||
GlobalAppSettings& GlobalSettings();
|
||||
|
||||
gsl::span<const Profile> GetProfiles() const noexcept;
|
||||
|
||||
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
|
||||
|
||||
static std::unique_ptr<CascadiaSettings> FromJson(const Json::Value& json);
|
||||
void LayerJson(const Json::Value& json);
|
||||
|
||||
static std::filesystem::path GetSettingsPath();
|
||||
static std::filesystem::path GetDefaultSettingsPath();
|
||||
|
||||
const Profile* FindProfile(GUID profileGuid) const noexcept;
|
||||
const winrt::TerminalApp::ColorScheme GetColorSchemeForProfile(const GUID profileGuid) const;
|
||||
|
||||
std::vector<TerminalApp::SettingsLoadWarnings>& GetWarnings();
|
||||
|
||||
bool ApplyColorScheme(winrt::Microsoft::Terminal::TerminalControl::IControlSettings& settings, std::wstring_view schemeName);
|
||||
|
||||
private:
|
||||
GlobalAppSettings _globals;
|
||||
std::vector<Profile> _profiles;
|
||||
std::vector<TerminalApp::SettingsLoadWarnings> _warnings;
|
||||
|
||||
std::vector<std::unique_ptr<TerminalApp::IDynamicProfileGenerator>> _profileGenerators;
|
||||
|
||||
std::string _userSettingsString;
|
||||
Json::Value _userSettings;
|
||||
Json::Value _defaultSettings;
|
||||
Json::Value _userDefaultProfileSettings{ Json::Value::null };
|
||||
|
||||
void _LayerOrCreateProfile(const Json::Value& profileJson);
|
||||
Profile* _FindMatchingProfile(const Json::Value& profileJson);
|
||||
void _LayerOrCreateColorScheme(const Json::Value& schemeJson);
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::ColorScheme> _FindMatchingColorScheme(const Json::Value& schemeJson);
|
||||
void _ParseJsonString(std::string_view fileData, const bool isDefaultSettings);
|
||||
static const Json::Value& _GetProfilesJsonObject(const Json::Value& json);
|
||||
static const Json::Value& _GetDisabledProfileSourcesJsonObject(const Json::Value& json);
|
||||
bool _PrependSchemaDirective();
|
||||
bool _AppendDynamicProfilesToUserSettings();
|
||||
std::string _ApplyFirstRunChangesToSettingsTemplate(std::string_view settingsTemplate) const;
|
||||
|
||||
void _ApplyDefaultsFromUserSettings();
|
||||
|
||||
void _LoadDynamicProfiles();
|
||||
|
||||
static bool _IsPackaged();
|
||||
static void _WriteSettings(const std::string_view content);
|
||||
static std::optional<std::string> _ReadUserSettings();
|
||||
static std::optional<std::string> _ReadFile(HANDLE hFile);
|
||||
|
||||
std::optional<GUID> _GetProfileGuidByName(const std::wstring_view) const;
|
||||
std::optional<GUID> _GetProfileGuidByIndex(std::optional<int> index) const;
|
||||
GUID _GetProfileForArgs(const winrt::TerminalApp::NewTerminalArgs& newTerminalArgs) const;
|
||||
|
||||
void _ValidateSettings();
|
||||
void _ValidateProfilesExist();
|
||||
void _ValidateProfilesHaveGuid();
|
||||
void _ValidateDefaultProfileExists();
|
||||
void _ValidateNoDuplicateProfiles();
|
||||
void _ResolveDefaultProfile();
|
||||
void _ReorderProfilesToMatchUserSettingsOrder();
|
||||
void _RemoveHiddenProfiles();
|
||||
void _ValidateAllSchemesExist();
|
||||
void _ValidateMediaResources();
|
||||
void _ValidateKeybindings();
|
||||
void _ValidateNoGlobalsKey();
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ProfileTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
friend class TerminalAppLocalTests::KeyBindingsTests;
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
friend class TerminalAppUnitTests::DynamicProfileTests;
|
||||
friend class TerminalAppUnitTests::JsonTests;
|
||||
};
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <argb.h>
|
||||
#include "CascadiaSettings.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "Utils.h"
|
||||
#include "utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include <appmodel.h>
|
||||
#include <shlobj.h>
|
||||
@@ -18,7 +18,8 @@
|
||||
// Both defaults.h and userDefaults.h are generated at build time into the
|
||||
// "Generated Files" directory.
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace ::TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
static constexpr std::wstring_view SettingsFilename{ L"settings.json" };
|
||||
@@ -100,163 +101,146 @@ static void _CatchRethrowSerializationExceptionWithLocationInfo(std::string_view
|
||||
// profiles inserted into their list of profiles.
|
||||
// Return Value:
|
||||
// - a unique_ptr containing a new CascadiaSettings object.
|
||||
winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::LoadAll()
|
||||
std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
{
|
||||
auto resultPtr = LoadDefaults();
|
||||
|
||||
// GH 3588, we need this below to know if the user chose something that wasn't our default.
|
||||
// Collect it up here in case it gets modified by any of the other layers between now and when
|
||||
// the user's preferences are loaded and layered.
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
std::optional<std::string> fileData = _ReadUserSettings();
|
||||
const bool foundFile = fileData.has_value();
|
||||
|
||||
// Make sure the file isn't totally empty. If it is, we'll treat the file
|
||||
// like it doesn't exist at all.
|
||||
const bool fileHasData = foundFile && !fileData.value().empty();
|
||||
bool needToWriteFile = false;
|
||||
if (fileHasData)
|
||||
{
|
||||
resultPtr->_ParseJsonString(fileData.value(), false);
|
||||
}
|
||||
|
||||
// Load profiles from dynamic profile generators. _userSettings should be
|
||||
// created by now, because we're going to check in there for any generators
|
||||
// that should be disabled (if the user had any settings.)
|
||||
resultPtr->_LoadDynamicProfiles();
|
||||
|
||||
if (!fileHasData)
|
||||
{
|
||||
// We didn't find the user settings. We'll need to create a file
|
||||
// to use as the user defaults.
|
||||
// For now, just parse our user settings template as their user settings.
|
||||
auto userSettings{ resultPtr->_ApplyFirstRunChangesToSettingsTemplate(UserSettingsJson) };
|
||||
resultPtr->_ParseJsonString(userSettings, false);
|
||||
needToWriteFile = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto settings = LoadDefaults();
|
||||
auto resultPtr = winrt::get_self<CascadiaSettings>(settings);
|
||||
// See microsoft/terminal#2325: find the defaultSettings from the user's
|
||||
// settings. Layer those settings upon all the existing profiles we have
|
||||
// (defaults and dynamic profiles). We'll also set
|
||||
// _userDefaultProfileSettings here. When we LayerJson below to apply the
|
||||
// user settings, we'll make sure to use these defaultSettings _before_ any
|
||||
// profiles the user might have.
|
||||
resultPtr->_ApplyDefaultsFromUserSettings();
|
||||
|
||||
// GH 3588, we need this below to know if the user chose something that wasn't our default.
|
||||
// Collect it up here in case it gets modified by any of the other layers between now and when
|
||||
// the user's preferences are loaded and layered.
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
std::optional<std::string> fileData = _ReadUserSettings();
|
||||
const bool foundFile = fileData.has_value();
|
||||
|
||||
// Make sure the file isn't totally empty. If it is, we'll treat the file
|
||||
// like it doesn't exist at all.
|
||||
const bool fileHasData = foundFile && !fileData.value().empty();
|
||||
bool needToWriteFile = false;
|
||||
if (fileHasData)
|
||||
{
|
||||
resultPtr->_ParseJsonString(fileData.value(), false);
|
||||
}
|
||||
|
||||
// Load profiles from dynamic profile generators. _userSettings should be
|
||||
// created by now, because we're going to check in there for any generators
|
||||
// that should be disabled (if the user had any settings.)
|
||||
resultPtr->_LoadDynamicProfiles();
|
||||
|
||||
if (!fileHasData)
|
||||
{
|
||||
// We didn't find the user settings. We'll need to create a file
|
||||
// to use as the user defaults.
|
||||
// For now, just parse our user settings template as their user settings.
|
||||
auto userSettings{ resultPtr->_ApplyFirstRunChangesToSettingsTemplate(UserSettingsJson) };
|
||||
resultPtr->_ParseJsonString(userSettings, false);
|
||||
needToWriteFile = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// See microsoft/terminal#2325: find the defaultSettings from the user's
|
||||
// settings. Layer those settings upon all the existing profiles we have
|
||||
// (defaults and dynamic profiles). We'll also set
|
||||
// _userDefaultProfileSettings here. When we LayerJson below to apply the
|
||||
// user settings, we'll make sure to use these defaultSettings _before_ any
|
||||
// profiles the user might have.
|
||||
resultPtr->_ApplyDefaultsFromUserSettings();
|
||||
|
||||
// Apply the user's settings
|
||||
resultPtr->LayerJson(resultPtr->_userSettings);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_CatchRethrowSerializationExceptionWithLocationInfo(resultPtr->_userSettingsString);
|
||||
}
|
||||
|
||||
// After layering the user settings, check if there are any new profiles
|
||||
// that need to be inserted into their user settings file.
|
||||
needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
|
||||
|
||||
if (needToWriteFile)
|
||||
{
|
||||
// For safety's sake, we need to re-parse the JSON document to ensure that
|
||||
// all future patches are applied with updated object offsets.
|
||||
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
|
||||
}
|
||||
|
||||
// Make sure there's a $schema at the top of the file.
|
||||
needToWriteFile = resultPtr->_PrependSchemaDirective() || needToWriteFile;
|
||||
|
||||
// TODO:GH#2721 If powershell core is installed, we need to set that to the
|
||||
// default profile, but only when the settings file was newly created. We'll
|
||||
// re-write the segment of the user settings for "default profile" to have
|
||||
// the powershell core GUID instead.
|
||||
|
||||
// If we created the file, or found new dynamic profiles, write the user
|
||||
// settings string back to the file.
|
||||
if (needToWriteFile)
|
||||
{
|
||||
// If AppendDynamicProfilesToUserSettings (or the pwsh check above)
|
||||
// changed the file, then our local settings JSON is no longer accurate.
|
||||
// We should re-parse, but not re-layer
|
||||
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
|
||||
|
||||
_WriteSettings(resultPtr->_userSettingsString);
|
||||
}
|
||||
|
||||
// If this throws, the app will catch it and use the default settings
|
||||
resultPtr->_ValidateSettings();
|
||||
|
||||
// GH 3855 - Gathering Data on custom profiles to inform better defaults
|
||||
// Do it after everything else so it won't happen unless validation passed.
|
||||
// Also, avoid processing unless someone's listening for measures. The keybindings work, at least,
|
||||
// is a lot of computation we can skip if no one cares.
|
||||
if (TraceLoggingProviderEnabled(g_hSettingsModelProvider, 0, MICROSOFT_KEYWORD_MEASURES))
|
||||
{
|
||||
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
// Compare to the defaults.json one that we set on install.
|
||||
// If it's different, log what the user chose.
|
||||
if (hardcodedDefaultGuid != guid)
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hSettingsModelProvider, // handle to TerminalApp tracelogging provider
|
||||
"CustomDefaultProfile",
|
||||
TraceLoggingDescription("Event emitted when user has chosen a different default profile than hardcoded one on load/reload"),
|
||||
TraceLoggingGuid(guid, "DefaultProfile", "ID of user-chosen default profile"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
|
||||
// If the user had keybinding settings preferences, we want to learn from them to make better defaults
|
||||
auto userKeybindings = resultPtr->_userSettings[JsonKey(KeybindingsKey)];
|
||||
if (!userKeybindings.empty())
|
||||
{
|
||||
// If there are custom key bindings, let's understand what they are because maybe the defaults aren't good enough
|
||||
|
||||
// Run it through the object so we can parse it apart and then only serialize the fields we're interested in
|
||||
// and avoid extraneous data.
|
||||
auto km = winrt::make_self<implementation::KeyMapping>();
|
||||
km->LayerJson(userKeybindings);
|
||||
auto value = km->ToJson();
|
||||
|
||||
// Reserialize the keybindings
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
// Use 4 spaces to indent instead of \t
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
|
||||
|
||||
const auto keybindingsString = Json::writeString(wbuilder, value);
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hSettingsModelProvider, // handle to TerminalApp tracelogging provider
|
||||
"CustomKeybindings",
|
||||
TraceLoggingDescription("Event emitted when custom keybindings are identified on load/reload"),
|
||||
TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
}
|
||||
|
||||
return *resultPtr;
|
||||
// Apply the user's settings
|
||||
resultPtr->LayerJson(resultPtr->_userSettings);
|
||||
}
|
||||
catch (const SettingsException& ex)
|
||||
catch (...)
|
||||
{
|
||||
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
|
||||
settings->_loadError = ex.Error();
|
||||
return *settings;
|
||||
_CatchRethrowSerializationExceptionWithLocationInfo(resultPtr->_userSettingsString);
|
||||
}
|
||||
catch (const SettingsTypedDeserializationException& e)
|
||||
|
||||
// After layering the user settings, check if there are any new profiles
|
||||
// that need to be inserted into their user settings file.
|
||||
needToWriteFile = resultPtr->_AppendDynamicProfilesToUserSettings() || needToWriteFile;
|
||||
|
||||
if (needToWriteFile)
|
||||
{
|
||||
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
|
||||
std::string_view what{ e.what() };
|
||||
settings->_deserializationErrorMessage = til::u8u16(what);
|
||||
return *settings;
|
||||
// For safety's sake, we need to re-parse the JSON document to ensure that
|
||||
// all future patches are applied with updated object offsets.
|
||||
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
|
||||
}
|
||||
|
||||
// Make sure there's a $schema at the top of the file.
|
||||
needToWriteFile = resultPtr->_PrependSchemaDirective() || needToWriteFile;
|
||||
|
||||
// TODO:GH#2721 If powershell core is installed, we need to set that to the
|
||||
// default profile, but only when the settings file was newly created. We'll
|
||||
// re-write the segment of the user settings for "default profile" to have
|
||||
// the powershell core GUID instead.
|
||||
|
||||
// If we created the file, or found new dynamic profiles, write the user
|
||||
// settings string back to the file.
|
||||
if (needToWriteFile)
|
||||
{
|
||||
// If AppendDynamicProfilesToUserSettings (or the pwsh check above)
|
||||
// changed the file, then our local settings JSON is no longer accurate.
|
||||
// We should re-parse, but not re-layer
|
||||
resultPtr->_ParseJsonString(resultPtr->_userSettingsString, false);
|
||||
|
||||
_WriteSettings(resultPtr->_userSettingsString);
|
||||
}
|
||||
|
||||
// If this throws, the app will catch it and use the default settings
|
||||
resultPtr->_ValidateSettings();
|
||||
|
||||
// GH 3855 - Gathering Data on custom profiles to inform better defaults
|
||||
// Do it after everything else so it won't happen unless validation passed.
|
||||
// Also, avoid processing unless someone's listening for measures. The keybindings work, at least,
|
||||
// is a lot of computation we can skip if no one cares.
|
||||
if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
|
||||
{
|
||||
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
// Compare to the defaults.json one that we set on install.
|
||||
// If it's different, log what the user chose.
|
||||
if (hardcodedDefaultGuid != guid)
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"CustomDefaultProfile",
|
||||
TraceLoggingDescription("Event emitted when user has chosen a different default profile than hardcoded one on load/reload"),
|
||||
TraceLoggingGuid(guid, "DefaultProfile", "ID of user-chosen default profile"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
|
||||
// If the user had keybinding settings preferences, we want to learn from them to make better defaults
|
||||
auto userKeybindings = resultPtr->_userSettings[JsonKey(KeybindingsKey)];
|
||||
if (!userKeybindings.empty())
|
||||
{
|
||||
// If there are custom key bindings, let's understand what they are because maybe the defaults aren't good enough
|
||||
|
||||
// Run it through the object so we can parse it apart and then only serialize the fields we're interested in
|
||||
// and avoid extraneous data.
|
||||
auto akb = winrt::make_self<implementation::AppKeyBindings>();
|
||||
akb->LayerJson(userKeybindings);
|
||||
auto value = akb->ToJson();
|
||||
|
||||
// Reserialize the keybindings
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
// Use 4 spaces to indent instead of \t
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
|
||||
|
||||
const auto keybindingsString = Json::writeString(wbuilder, value);
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"CustomKeybindings",
|
||||
TraceLoggingDescription("Event emitted when custom keybindings are identified on load/reload"),
|
||||
TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
}
|
||||
|
||||
return resultPtr;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -265,37 +249,21 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a unique_ptr to a CascadiaSettings with the connection types and settings for Universal terminal
|
||||
winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::LoadUniversal()
|
||||
std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadUniversal()
|
||||
{
|
||||
// We're going to do this ourselves because we want to exclude almost everything
|
||||
// from the special Universal-for-developers configuration
|
||||
|
||||
try
|
||||
{
|
||||
// Create settings and get the universal defaults loaded up.
|
||||
auto resultPtr = winrt::make_self<CascadiaSettings>();
|
||||
resultPtr->_ParseJsonString(DefaultUniversalJson, true);
|
||||
resultPtr->LayerJson(resultPtr->_defaultSettings);
|
||||
// Create settings and get the universal defaults loaded up.
|
||||
auto resultPtr = std::make_unique<CascadiaSettings>();
|
||||
resultPtr->_ParseJsonString(DefaultUniversalJson, true);
|
||||
resultPtr->LayerJson(resultPtr->_defaultSettings);
|
||||
|
||||
// Now validate.
|
||||
// If this throws, the app will catch it and use the default settings
|
||||
resultPtr->_ValidateSettings();
|
||||
// Now validate.
|
||||
// If this throws, the app will catch it and use the default settings
|
||||
resultPtr->_ValidateSettings();
|
||||
|
||||
return *resultPtr;
|
||||
}
|
||||
catch (const SettingsException& ex)
|
||||
{
|
||||
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
|
||||
settings->_loadError = ex.Error();
|
||||
return *settings;
|
||||
}
|
||||
catch (const SettingsTypedDeserializationException& e)
|
||||
{
|
||||
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
|
||||
std::string_view what{ e.what() };
|
||||
settings->_deserializationErrorMessage = til::u8u16(what);
|
||||
return *settings;
|
||||
}
|
||||
return resultPtr;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -305,9 +273,9 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a unique_ptr to a CascadiaSettings with the settings from defaults.json
|
||||
winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::LoadDefaults()
|
||||
std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadDefaults()
|
||||
{
|
||||
auto resultPtr{ winrt::make_self<CascadiaSettings>() };
|
||||
auto resultPtr = std::make_unique<CascadiaSettings>();
|
||||
|
||||
// We already have the defaults in memory, because we stamp them into a
|
||||
// header as part of the build process. We don't need to bother with reading
|
||||
@@ -316,7 +284,7 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
|
||||
resultPtr->LayerJson(resultPtr->_defaultSettings);
|
||||
resultPtr->_ResolveDefaultProfile();
|
||||
|
||||
return *resultPtr;
|
||||
return resultPtr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -359,9 +327,9 @@ void CascadiaSettings::_LoadDynamicProfiles()
|
||||
{
|
||||
// If the profile did not have a GUID when it was generated,
|
||||
// we'll synthesize a GUID for it in _ValidateProfilesHaveGuid
|
||||
profile.Source(generatorNamespace);
|
||||
profile.SetSource(generatorNamespace);
|
||||
|
||||
_profiles.Append(profile);
|
||||
_profiles.emplace_back(profile);
|
||||
}
|
||||
}
|
||||
CATCH_LOG_MSG("Dynamic Profile Namespace: \"%ls\"", generatorNamespace.data());
|
||||
@@ -472,6 +440,8 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
||||
// * Serialize that diff
|
||||
// * Insert that diff to the end of the list of profiles.
|
||||
|
||||
const Profile defaultProfile;
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
// Use 4 spaces to indent instead of \t
|
||||
wbuilder.settings_["indentation"] = " ";
|
||||
@@ -482,8 +452,7 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
||||
{
|
||||
if (profileJson.isObject())
|
||||
{
|
||||
const auto profileImpl = winrt::get_self<implementation::Profile>(profile);
|
||||
if (profileImpl->ShouldBeLayered(profileJson))
|
||||
if (profile.ShouldBeLayered(profileJson))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -533,8 +502,7 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
||||
|
||||
// Generate a diff for the profile, that contains the minimal set of
|
||||
// changes to re-create this profile.
|
||||
const auto profileImpl = winrt::get_self<implementation::Profile>(profile);
|
||||
const auto diff = profileImpl->GenerateStub();
|
||||
const auto diff = profile.GenerateStub();
|
||||
auto profileSerialization = Json::writeString(wbuilder, diff);
|
||||
|
||||
// Add the user's indent to the start of each line
|
||||
@@ -571,9 +539,9 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
|
||||
// - json: an object which should be a serialization of a CascadiaSettings object.
|
||||
// Return Value:
|
||||
// - a new CascadiaSettings instance created from the values in `json`
|
||||
winrt::com_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value& json)
|
||||
std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value& json)
|
||||
{
|
||||
auto resultPtr = winrt::make_self<CascadiaSettings>();
|
||||
auto resultPtr = std::make_unique<CascadiaSettings>();
|
||||
resultPtr->LayerJson(json);
|
||||
return resultPtr;
|
||||
}
|
||||
@@ -590,7 +558,7 @@ winrt::com_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value& j
|
||||
// <none>
|
||||
void CascadiaSettings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
_globals->LayerJson(json);
|
||||
_globals.LayerJson(json);
|
||||
|
||||
if (auto schemes{ json[SchemesKey.data()] })
|
||||
{
|
||||
@@ -639,17 +607,17 @@ void CascadiaSettings::_LayerOrCreateProfile(const Json::Value& profileJson)
|
||||
// `source`. Dynamic profiles _must_ be layered on an existing profile.
|
||||
if (!Profile::IsDynamicProfileObject(profileJson))
|
||||
{
|
||||
auto profile = winrt::make_self<Profile>();
|
||||
Profile profile{};
|
||||
|
||||
// GH#2325: If we have a set of default profile settings, apply them here.
|
||||
// We _won't_ have these settings yet for defaults, dynamic profiles.
|
||||
if (_userDefaultProfileSettings)
|
||||
{
|
||||
profile->LayerJson(_userDefaultProfileSettings);
|
||||
profile.LayerJson(_userDefaultProfileSettings);
|
||||
}
|
||||
|
||||
profile->LayerJson(profileJson);
|
||||
_profiles.Append(*profile);
|
||||
profile.LayerJson(profileJson);
|
||||
_profiles.emplace_back(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -665,14 +633,17 @@ void CascadiaSettings::_LayerOrCreateProfile(const Json::Value& profileJson)
|
||||
// Return Value:
|
||||
// - a Profile that can be layered with the given json object, iff such a
|
||||
// profile exists.
|
||||
winrt::com_ptr<Profile> CascadiaSettings::_FindMatchingProfile(const Json::Value& profileJson)
|
||||
Profile* CascadiaSettings::_FindMatchingProfile(const Json::Value& profileJson)
|
||||
{
|
||||
for (auto profile : _profiles)
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
auto profileImpl = winrt::get_self<Profile>(profile);
|
||||
if (profileImpl->ShouldBeLayered(profileJson))
|
||||
if (profile.ShouldBeLayered(profileJson))
|
||||
{
|
||||
return profileImpl->get_strong();
|
||||
// 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 &profile;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -712,10 +683,9 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings()
|
||||
// hyper-explode, so just don't let them do that.
|
||||
_userDefaultProfileSettings.removeMember({ "guid" });
|
||||
|
||||
for (auto profile : _profiles)
|
||||
for (auto& profile : _profiles)
|
||||
{
|
||||
auto profileImpl = winrt::get_self<Profile>(profile);
|
||||
profileImpl->LayerJson(_userDefaultProfileSettings);
|
||||
profile.LayerJson(_userDefaultProfileSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -738,8 +708,8 @@ void CascadiaSettings::_LayerOrCreateColorScheme(const Json::Value& schemeJson)
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto scheme = ColorScheme::FromJson(schemeJson);
|
||||
_globals->AddColorScheme(*scheme);
|
||||
const auto scheme = implementation::ColorScheme::FromJson(schemeJson);
|
||||
_globals.AddColorScheme(*scheme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,13 +724,15 @@ void CascadiaSettings::_LayerOrCreateColorScheme(const Json::Value& schemeJson)
|
||||
// Return Value:
|
||||
// - a ColorScheme that can be layered with the given json object, iff such a
|
||||
// color scheme exists.
|
||||
winrt::com_ptr<ColorScheme> CascadiaSettings::_FindMatchingColorScheme(const Json::Value& schemeJson)
|
||||
winrt::com_ptr<implementation::ColorScheme> CascadiaSettings::_FindMatchingColorScheme(const Json::Value& schemeJson)
|
||||
{
|
||||
if (auto schemeName = ColorScheme::GetNameFromJson(schemeJson))
|
||||
if (auto schemeName = implementation::ColorScheme::GetNameFromJson(schemeJson))
|
||||
{
|
||||
if (auto scheme{ _globals->ColorSchemes().TryLookup(*schemeName) })
|
||||
auto& schemes = _globals.GetColorSchemes();
|
||||
auto iterator = schemes.find(*schemeName);
|
||||
if (iterator != schemes.end())
|
||||
{
|
||||
return winrt::get_self<ColorScheme>(scheme)->get_strong();
|
||||
return winrt::get_self<implementation::ColorScheme>(iterator->second)->get_strong();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -791,7 +763,7 @@ bool CascadiaSettings::_IsPackaged()
|
||||
// fail to write the file
|
||||
void CascadiaSettings::_WriteSettings(const std::string_view content)
|
||||
{
|
||||
auto pathToSettingsFile{ CascadiaSettings::SettingsPath() };
|
||||
auto pathToSettingsFile{ CascadiaSettings::GetSettingsPath() };
|
||||
|
||||
wil::unique_hfile hOut{ CreateFileW(pathToSettingsFile.c_str(),
|
||||
GENERIC_WRITE,
|
||||
@@ -818,7 +790,7 @@ void CascadiaSettings::_WriteSettings(const std::string_view content)
|
||||
// from reading the file
|
||||
std::optional<std::string> CascadiaSettings::_ReadUserSettings()
|
||||
{
|
||||
const auto pathToSettingsFile{ CascadiaSettings::SettingsPath() };
|
||||
const auto pathToSettingsFile{ CascadiaSettings::GetSettingsPath() };
|
||||
wil::unique_hfile hFile{ CreateFileW(pathToSettingsFile.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
@@ -832,7 +804,7 @@ std::optional<std::string> CascadiaSettings::_ReadUserSettings()
|
||||
// GH#5186 - We moved from profiles.json to settings.json; we want to
|
||||
// migrate any file we find. We're using MoveFile in case their settings.json
|
||||
// is a symbolic link.
|
||||
std::filesystem::path pathToLegacySettingsFile{ std::wstring_view{ pathToSettingsFile } };
|
||||
auto pathToLegacySettingsFile{ pathToSettingsFile };
|
||||
pathToLegacySettingsFile.replace_filename(LegacySettingsFilename);
|
||||
|
||||
wil::unique_hfile hLegacyFile{ CreateFileW(pathToLegacySettingsFile.c_str(),
|
||||
@@ -916,7 +888,7 @@ std::optional<std::string> CascadiaSettings::_ReadFile(HANDLE hFile)
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the full path to the settings file
|
||||
winrt::hstring CascadiaSettings::SettingsPath()
|
||||
std::filesystem::path CascadiaSettings::GetSettingsPath()
|
||||
{
|
||||
wil::unique_cotaskmem_string localAppDataFolder;
|
||||
// KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return
|
||||
@@ -934,10 +906,10 @@ winrt::hstring CascadiaSettings::SettingsPath()
|
||||
// Create the directory if it doesn't exist
|
||||
std::filesystem::create_directories(parentDirectoryForSettingsFile);
|
||||
|
||||
return winrt::hstring{ (parentDirectoryForSettingsFile / SettingsFilename).wstring() };
|
||||
return parentDirectoryForSettingsFile / SettingsFilename;
|
||||
}
|
||||
|
||||
winrt::hstring CascadiaSettings::DefaultSettingsPath()
|
||||
std::filesystem::path CascadiaSettings::GetDefaultSettingsPath()
|
||||
{
|
||||
// Both of these posts suggest getting the path to the exe, then removing
|
||||
// the exe's name to get the package root:
|
||||
@@ -957,7 +929,7 @@ winrt::hstring CascadiaSettings::DefaultSettingsPath()
|
||||
|
||||
const std::filesystem::path exePath{ exePathString };
|
||||
const std::filesystem::path rootDir = exePath.parent_path();
|
||||
return winrt::hstring{ (rootDir / DefaultsFilename).wstring() };
|
||||
return rootDir / DefaultsFilename;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -11,8 +11,8 @@
|
||||
#include "ColorScheme.g.cpp"
|
||||
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp::implementation;
|
||||
using namespace winrt::Windows::UI;
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
@@ -20,7 +20,6 @@ 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 CursorColorKey{ "cursorColor" };
|
||||
|
||||
static constexpr std::array<std::string_view, 16> TableColors = {
|
||||
"black",
|
||||
"red",
|
||||
@@ -41,22 +40,50 @@ static constexpr std::array<std::string_view, 16> TableColors = {
|
||||
};
|
||||
|
||||
ColorScheme::ColorScheme() :
|
||||
_Foreground{ DEFAULT_FOREGROUND_WITH_ALPHA },
|
||||
_Background{ DEFAULT_BACKGROUND_WITH_ALPHA },
|
||||
_SelectionBackground{ DEFAULT_FOREGROUND },
|
||||
_CursorColor{ DEFAULT_CURSOR_COLOR }
|
||||
_schemeName{ L"" },
|
||||
_table{},
|
||||
_defaultForeground{ DEFAULT_FOREGROUND_WITH_ALPHA },
|
||||
_defaultBackground{ DEFAULT_BACKGROUND_WITH_ALPHA },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND },
|
||||
_cursorColor{ DEFAULT_CURSOR_COLOR }
|
||||
{
|
||||
}
|
||||
|
||||
ColorScheme::ColorScheme(winrt::hstring name, Color defaultFg, Color defaultBg, Color cursorColor) :
|
||||
_Name{ name },
|
||||
_Foreground{ defaultFg },
|
||||
_Background{ defaultBg },
|
||||
_SelectionBackground{ DEFAULT_FOREGROUND },
|
||||
_CursorColor{ cursorColor }
|
||||
_schemeName{ name },
|
||||
_table{},
|
||||
_defaultForeground{ defaultFg },
|
||||
_defaultBackground{ defaultBg },
|
||||
_selectionBackground{ DEFAULT_FOREGROUND },
|
||||
_cursorColor{ cursorColor }
|
||||
{
|
||||
}
|
||||
|
||||
ColorScheme::~ColorScheme()
|
||||
{
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Apply our values to the given TerminalSettings object. Sets the foreground,
|
||||
// background, and color table of the settings object.
|
||||
// Arguments:
|
||||
// - terminalSettings: the object to apply our settings to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ColorScheme::ApplyScheme(const winrt::Microsoft::Terminal::TerminalControl::IControlSettings& terminalSettings) const
|
||||
{
|
||||
terminalSettings.DefaultForeground(static_cast<COLORREF>(_defaultForeground));
|
||||
terminalSettings.DefaultBackground(static_cast<COLORREF>(_defaultBackground));
|
||||
terminalSettings.SelectionBackground(static_cast<COLORREF>(_selectionBackground));
|
||||
terminalSettings.CursorColor(static_cast<COLORREF>(_cursorColor));
|
||||
|
||||
auto const tableCount = gsl::narrow_cast<int>(_table.size());
|
||||
for (int i = 0; i < tableCount; i++)
|
||||
{
|
||||
terminalSettings.SetColorTableEntry(i, static_cast<COLORREF>(_table[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a new instance of this class from a serialized JsonObject.
|
||||
// Arguments:
|
||||
@@ -83,7 +110,7 @@ bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
|
||||
std::wstring nameFromJson{};
|
||||
if (JsonUtils::GetValueForKey(json, NameKey, nameFromJson))
|
||||
{
|
||||
return nameFromJson == _Name;
|
||||
return nameFromJson == _schemeName;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -100,60 +127,50 @@ bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
|
||||
// <none>
|
||||
void ColorScheme::LayerJson(const Json::Value& json)
|
||||
{
|
||||
JsonUtils::GetValueForKey(json, NameKey, _Name);
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground);
|
||||
JsonUtils::GetValueForKey(json, BackgroundKey, _Background);
|
||||
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground);
|
||||
JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor);
|
||||
JsonUtils::GetValueForKey(json, NameKey, _schemeName);
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _defaultForeground);
|
||||
JsonUtils::GetValueForKey(json, BackgroundKey, _defaultBackground);
|
||||
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _selectionBackground);
|
||||
JsonUtils::GetValueForKey(json, CursorColorKey, _cursorColor);
|
||||
|
||||
for (unsigned int i = 0; i < TableColors.size(); ++i)
|
||||
int i = 0;
|
||||
for (const auto& current : TableColors)
|
||||
{
|
||||
JsonUtils::GetValueForKey(json, til::at(TableColors, i), _table.at(i));
|
||||
JsonUtils::GetValueForKey(json, current, _table.at(i));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a new serialized JsonObject from an instance of this class
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// <none>
|
||||
Json::Value ColorScheme::ToJson()
|
||||
winrt::hstring ColorScheme::Name() const noexcept
|
||||
{
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
|
||||
JsonUtils::SetValueForKey(json, NameKey, _Name);
|
||||
JsonUtils::SetValueForKey(json, ForegroundKey, _Foreground);
|
||||
JsonUtils::SetValueForKey(json, BackgroundKey, _Background);
|
||||
JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground);
|
||||
JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor);
|
||||
|
||||
for (unsigned int i = 0; i < TableColors.size(); ++i)
|
||||
{
|
||||
JsonUtils::SetValueForKey(json, til::at(TableColors, i), _table.at(i));
|
||||
}
|
||||
|
||||
return json;
|
||||
return _schemeName;
|
||||
}
|
||||
|
||||
winrt::com_array<Color> ColorScheme::Table() const noexcept
|
||||
{
|
||||
winrt::com_array<Color> result{ base::checked_cast<uint32_t>(_table.size()) };
|
||||
winrt::com_array<Color> result{ COLOR_TABLE_SIZE };
|
||||
std::transform(_table.begin(), _table.end(), result.begin(), [](til::color c) -> Color { return c; });
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Set a color in the color table
|
||||
// Arguments:
|
||||
// - index: the index of the desired color within the table
|
||||
// - value: the color value we are setting the color table color to
|
||||
// Return Value:
|
||||
// - none
|
||||
void ColorScheme::SetColorTableEntry(uint8_t index, const winrt::Windows::UI::Color& value) noexcept
|
||||
Color ColorScheme::Foreground() const noexcept
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, index > _table.size() - 1);
|
||||
_table[index] = value;
|
||||
return _defaultForeground;
|
||||
}
|
||||
|
||||
Color ColorScheme::Background() const noexcept
|
||||
{
|
||||
return _defaultBackground;
|
||||
}
|
||||
|
||||
Color ColorScheme::SelectionBackground() const noexcept
|
||||
{
|
||||
return _selectionBackground;
|
||||
}
|
||||
|
||||
Color ColorScheme::CursorColor() const noexcept
|
||||
{
|
||||
return _cursorColor;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -15,47 +15,56 @@ Author(s):
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include "TerminalSettings.h"
|
||||
#include "../../inc/conattrs.hpp"
|
||||
#include "inc/cppwinrt_utils.h"
|
||||
|
||||
#include "ColorScheme.g.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace SettingsModelLocalTests
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class ColorSchemeTests;
|
||||
};
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct ColorScheme : ColorSchemeT<ColorScheme>
|
||||
{
|
||||
public:
|
||||
ColorScheme();
|
||||
ColorScheme(hstring name, Windows::UI::Color defaultFg, Windows::UI::Color defaultBg, Windows::UI::Color cursorColor);
|
||||
~ColorScheme();
|
||||
|
||||
void ApplyScheme(const winrt::Microsoft::Terminal::TerminalControl::IControlSettings& terminalSettings) const;
|
||||
|
||||
static com_ptr<ColorScheme> FromJson(const Json::Value& json);
|
||||
bool ShouldBeLayered(const Json::Value& json) const;
|
||||
void LayerJson(const Json::Value& json);
|
||||
|
||||
Json::Value ToJson();
|
||||
hstring Name() const noexcept;
|
||||
com_array<Windows::UI::Color> Table() const noexcept;
|
||||
Windows::UI::Color Foreground() const noexcept;
|
||||
Windows::UI::Color Background() const noexcept;
|
||||
Windows::UI::Color SelectionBackground() const noexcept;
|
||||
Windows::UI::Color CursorColor() const noexcept;
|
||||
|
||||
static std::optional<std::wstring> GetNameFromJson(const Json::Value& json);
|
||||
|
||||
com_array<Windows::UI::Color> Table() const noexcept;
|
||||
void SetColorTableEntry(uint8_t index, const winrt::Windows::UI::Color& value) noexcept;
|
||||
|
||||
GETSET_PROPERTY(winrt::hstring, Name);
|
||||
GETSET_COLORPROPERTY(Foreground); // defined in constructor
|
||||
GETSET_COLORPROPERTY(Background); // defined in constructor
|
||||
GETSET_COLORPROPERTY(SelectionBackground); // defined in constructor
|
||||
GETSET_COLORPROPERTY(CursorColor); // defined in constructor
|
||||
|
||||
private:
|
||||
hstring _schemeName;
|
||||
std::array<til::color, COLOR_TABLE_SIZE> _table;
|
||||
til::color _defaultForeground;
|
||||
til::color _defaultBackground;
|
||||
til::color _selectionBackground;
|
||||
til::color _cursorColor;
|
||||
|
||||
friend class SettingsModelLocalTests::SettingsTests;
|
||||
friend class SettingsModelLocalTests::ColorSchemeTests;
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ColorScheme);
|
||||
}
|
||||
21
src/cascadia/TerminalApp/ColorScheme.idl
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass ColorScheme {
|
||||
ColorScheme();
|
||||
ColorScheme(String name, Windows.UI.Color defaultFg, Windows.UI.Color defaultBg, Windows.UI.Color cursorColor);
|
||||
|
||||
void ApplyScheme(Microsoft.Terminal.TerminalControl.IControlSettings terminalSettings);
|
||||
|
||||
String Name { get; };
|
||||
|
||||
Windows.UI.Color Foreground { get; };
|
||||
Windows.UI.Color Background { get; };
|
||||
Windows.UI.Color SelectionBackground { get; };
|
||||
Windows.UI.Color CursorColor { get; };
|
||||
|
||||
Windows.UI.Color[] Table { get; };
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,15 @@
|
||||
#include "Command.h"
|
||||
#include "Command.g.cpp"
|
||||
|
||||
#include "Utils.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "JsonUtils.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
@@ -31,14 +32,14 @@ static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
|
||||
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
|
||||
static constexpr std::string_view SchemeNameToken{ "${scheme.name}" };
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
Command::Command()
|
||||
{
|
||||
_setAction(nullptr);
|
||||
}
|
||||
|
||||
IMapView<winrt::hstring, Model::Command> Command::NestedCommands()
|
||||
Collections::IMapView<winrt::hstring, TerminalApp::Command> Command::NestedCommands()
|
||||
{
|
||||
return _subcommands ? _subcommands.GetView() : nullptr;
|
||||
}
|
||||
@@ -106,6 +107,70 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return actionAndArgs->GenerateName();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Actually initialize our IconSource for our _lastIconPath. Supports a variety of icons:
|
||||
// * If the icon is a path to an image, we'll use that.
|
||||
// * If it isn't, then we'll try and use the text as a FontIcon. If the
|
||||
// character is in the range of symbols reserved for the Segoe MDL2
|
||||
// Asserts, well treat it as such. Otherwise, we'll default to a Sego
|
||||
// UI icon, so things like emoji will work.
|
||||
// - MUST BE CALLED ON THE UI THREAD.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Command::RefreshIcon()
|
||||
{
|
||||
if (!_lastIconPath.empty())
|
||||
{
|
||||
_setIconSource(GetColoredIcon<winrt::WUX::Controls::IconSource>(_lastIconPath));
|
||||
|
||||
// If we fail to set the icon source using the "icon" as a path,
|
||||
// let's try it as a symbol/emoji.
|
||||
//
|
||||
// Anything longer that 2 wchar_t's _isn't_ an emoji or symbol, so
|
||||
// don't do this if it's just an invalid path.
|
||||
if (IconSource() == nullptr && _lastIconPath.size() <= 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
WUX::Controls::FontIconSource icon;
|
||||
const wchar_t ch = _lastIconPath[0];
|
||||
|
||||
// The range of MDL2 Icons isn't explicitly defined, but
|
||||
// we're using this based off the table on:
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font
|
||||
const bool isMDL2Icon = ch >= L'\uE700' && ch <= L'\uF8FF';
|
||||
if (isMDL2Icon)
|
||||
{
|
||||
icon.FontFamily(WUX::Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: you _do_ need to manually set the font here.
|
||||
icon.FontFamily(WUX::Media::FontFamily{ L"Segoe UI" });
|
||||
}
|
||||
icon.FontSize(12);
|
||||
icon.Glyph(_lastIconPath);
|
||||
_setIconSource(icon);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
if (IconSource() == nullptr)
|
||||
{
|
||||
// Set the default IconSource to a BitmapIconSource with a null source
|
||||
// (instead of just nullptr) because there's a really weird crash when swapping
|
||||
// data bound IconSourceElements in a ListViewTemplate (i.e. CommandPalette).
|
||||
// Swapping between nullptr IconSources and non-null IconSources causes a crash
|
||||
// to occur, but swapping between IconSources with a null source and non-null IconSources
|
||||
// work perfectly fine :shrug:.
|
||||
winrt::Windows::UI::Xaml::Controls::BitmapIconSource icon;
|
||||
icon.UriSource(nullptr);
|
||||
_setIconSource(icon);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Deserialize a Command from the `json` object. The json object should
|
||||
// contain a "name" and "action", and optionally an "icon".
|
||||
@@ -122,7 +187,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// Return Value:
|
||||
// - the newly constructed Command object.
|
||||
winrt::com_ptr<Command> Command::FromJson(const Json::Value& json,
|
||||
std::vector<SettingsLoadWarnings>& warnings)
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
|
||||
{
|
||||
auto result = winrt::make_self<Command>();
|
||||
|
||||
@@ -135,7 +200,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
if (const auto nestedCommandsJson{ json[JsonKey(CommandsKey)] })
|
||||
{
|
||||
// Initialize our list of subcommands.
|
||||
result->_subcommands = winrt::single_threaded_map<winrt::hstring, Model::Command>();
|
||||
result->_subcommands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
auto nestedWarnings = Command::LayerJson(result->_subcommands, nestedCommandsJson);
|
||||
// It's possible that the nested commands have some warnings
|
||||
warnings.insert(warnings.end(), nestedWarnings.begin(), nestedWarnings.end());
|
||||
@@ -149,7 +214,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JsonUtils::GetValueForKey(json, IconKey, result->_Icon);
|
||||
// Only get the icon path right now. The icon needs to be resolved into
|
||||
// an IconSource on the UI thread, which will be done by RefreshIcon.
|
||||
JsonUtils::GetValueForKey(json, IconKey, result->_lastIconPath);
|
||||
|
||||
// If we're a nested command, we can ignore the current action.
|
||||
if (!nested)
|
||||
@@ -213,10 +280,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// - json: A Json::Value containing an array of serialized commands
|
||||
// Return Value:
|
||||
// - A vector containing any warnings detected while parsing
|
||||
std::vector<SettingsLoadWarnings> Command::LayerJson(IMap<winrt::hstring, Model::Command>& commands,
|
||||
const Json::Value& json)
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> Command::LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
|
||||
const Json::Value& json)
|
||||
{
|
||||
std::vector<SettingsLoadWarnings> warnings;
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> warnings;
|
||||
|
||||
for (const auto& value : json)
|
||||
{
|
||||
@@ -285,13 +352,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// appended to this vector.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Command::ExpandCommands(IMap<winrt::hstring, Model::Command> commands,
|
||||
IVectorView<Model::Profile> profiles,
|
||||
IVectorView<Model::ColorScheme> schemes,
|
||||
IVector<SettingsLoadWarnings> warnings)
|
||||
void Command::ExpandCommands(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
|
||||
gsl::span<const ::TerminalApp::Profile> profiles,
|
||||
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
|
||||
{
|
||||
std::vector<winrt::hstring> commandsToRemove;
|
||||
std::vector<Model::Command> commandsToAdd;
|
||||
std::vector<winrt::TerminalApp::Command> commandsToAdd;
|
||||
|
||||
// First, collect up all the commands that need replacing.
|
||||
for (const auto& nameAndCmd : commands)
|
||||
@@ -341,12 +408,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// Return Value:
|
||||
// - and empty vector if the command wasn't expandable, otherwise a list of
|
||||
// the newly-created commands.
|
||||
std::vector<Model::Command> Command::_expandCommand(Command* const expandable,
|
||||
IVectorView<Model::Profile> profiles,
|
||||
IVectorView<Model::ColorScheme> schemes,
|
||||
IVector<SettingsLoadWarnings>& warnings)
|
||||
std::vector<winrt::TerminalApp::Command> Command::_expandCommand(Command* const expandable,
|
||||
gsl::span<const ::TerminalApp::Profile> profiles,
|
||||
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings)
|
||||
{
|
||||
std::vector<Model::Command> newCommands;
|
||||
std::vector<winrt::TerminalApp::Command> newCommands;
|
||||
|
||||
if (expandable->HasNestedCommands())
|
||||
{
|
||||
@@ -371,18 +438,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
const auto actualDataEnd = newJsonString.data() + newJsonString.size();
|
||||
if (!reader->parse(actualDataStart, actualDataEnd, &newJsonValue, &errs))
|
||||
{
|
||||
warnings.Append(SettingsLoadWarnings::FailedToParseCommandJson);
|
||||
warnings.push_back(::TerminalApp::SettingsLoadWarnings::FailedToParseCommandJson);
|
||||
// If we encounter a re-parsing error, just stop processing the rest of the commands.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass the new json back though FromJson, to get the new expanded value.
|
||||
std::vector<SettingsLoadWarnings> newWarnings;
|
||||
if (auto newCmd{ Command::FromJson(newJsonValue, newWarnings) })
|
||||
if (auto newCmd{ Command::FromJson(newJsonValue, warnings) })
|
||||
{
|
||||
newCommands.push_back(*newCmd);
|
||||
}
|
||||
std::for_each(newWarnings.begin(), newWarnings.end(), [warnings](auto& warn) { warnings.Append(warn); });
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -401,8 +466,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// Replace all the keywords in the original json, and try and parse that
|
||||
|
||||
// - Escape the profile name for JSON appropriately
|
||||
auto escapedProfileName = _escapeForJson(til::u16u8(p.Name()));
|
||||
auto escapedProfileIcon = _escapeForJson(til::u16u8(p.Icon()));
|
||||
auto escapedProfileName = _escapeForJson(til::u16u8(p.GetName()));
|
||||
auto escapedProfileIcon = _escapeForJson(til::u16u8(p.GetExpandedIconPath()));
|
||||
auto newJsonString = til::replace_needle_in_haystack(oldJsonString,
|
||||
ProfileNameToken,
|
||||
escapedProfileName);
|
||||
83
src/cascadia/TerminalApp/Command.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- Command.h
|
||||
|
||||
Abstract:
|
||||
- A command represents a single entry in the Command Palette. This is an object
|
||||
that has a user facing "name" to display to the user, and an associated action
|
||||
which can be dispatched.
|
||||
|
||||
- For more information, see GH#2046, #5400, #5674, and #6635
|
||||
|
||||
Author(s):
|
||||
- Mike Griese - June 2020
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "Command.g.h"
|
||||
#include "TerminalWarnings.h"
|
||||
#include "Profile.h"
|
||||
#include "..\inc\cppwinrt_utils.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class CommandTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct Command : CommandT<Command>
|
||||
{
|
||||
Command();
|
||||
|
||||
static winrt::com_ptr<Command> FromJson(const Json::Value& json,
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
|
||||
|
||||
static void ExpandCommands(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
|
||||
gsl::span<const ::TerminalApp::Profile> profiles,
|
||||
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
|
||||
|
||||
static std::vector<::TerminalApp::SettingsLoadWarnings> LayerJson(Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& commands,
|
||||
const Json::Value& json);
|
||||
bool HasNestedCommands();
|
||||
Windows::Foundation::Collections::IMapView<winrt::hstring, TerminalApp::Command> NestedCommands();
|
||||
|
||||
void RefreshIcon();
|
||||
|
||||
winrt::Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker propertyChangedRevoker;
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Name, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::ActionAndArgs, Action, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, KeyChordText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::IconSource, IconSource, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
GETSET_PROPERTY(::TerminalApp::ExpandCommandType, IterateOn, ::TerminalApp::ExpandCommandType::None);
|
||||
|
||||
private:
|
||||
Json::Value _originalJson;
|
||||
Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command> _subcommands{ nullptr };
|
||||
|
||||
winrt::hstring _lastIconPath{};
|
||||
|
||||
static std::vector<winrt::TerminalApp::Command> _expandCommand(Command* const expandable,
|
||||
gsl::span<const ::TerminalApp::Profile> profiles,
|
||||
gsl::span<winrt::TerminalApp::ColorScheme> schemes,
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings>& warnings);
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::CommandTests;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(Command);
|
||||
}
|
||||
22
src/cascadia/TerminalApp/Command.idl
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ShortcutActionDispatch.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass Command : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
Command();
|
||||
|
||||
String Name;
|
||||
ActionAndArgs Action;
|
||||
String KeyChordText;
|
||||
|
||||
Windows.UI.Xaml.Controls.IconSource IconSource;
|
||||
void RefreshIcon();
|
||||
|
||||
Boolean HasNestedCommands { get; };
|
||||
Windows.Foundation.Collections.IMapView<String, Command> NestedCommands { get; };
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "CommandPalette.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "ActionArgs.h"
|
||||
#include "Command.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
@@ -15,7 +18,6 @@ using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
@@ -24,11 +26,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_filteredActions = winrt::single_threaded_observable_vector<Command>();
|
||||
_nestedActionStack = winrt::single_threaded_vector<Command>();
|
||||
_currentNestedCommands = winrt::single_threaded_vector<Command>();
|
||||
_allCommands = winrt::single_threaded_vector<Command>();
|
||||
_allTabActions = winrt::single_threaded_vector<Command>();
|
||||
_filteredActions = winrt::single_threaded_observable_vector<winrt::TerminalApp::Command>();
|
||||
_nestedActionStack = winrt::single_threaded_vector<winrt::TerminalApp::Command>();
|
||||
_currentNestedCommands = winrt::single_threaded_vector<winrt::TerminalApp::Command>();
|
||||
_allCommands = winrt::single_threaded_vector<winrt::TerminalApp::Command>();
|
||||
_allTabActions = winrt::single_threaded_vector<winrt::TerminalApp::Command>();
|
||||
|
||||
_switchToMode(CommandPaletteMode::ActionMode);
|
||||
|
||||
@@ -93,8 +95,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
_sizeChangedRevoker.revoke();
|
||||
});
|
||||
|
||||
_filteredActionsView().SelectionChanged({ this, &CommandPalette::_selectedCommandChanged });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -117,29 +117,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_filteredActionsView().ScrollIntoView(_filteredActionsView().SelectedItem());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the command selection changes. We'll use this in the tab
|
||||
// switcher to "preview" tabs as the user navigates the list of tabs. To
|
||||
// do that, we'll dispatch the switch to tab command for this tab, but not
|
||||
// dismiss the switcher.
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_selectedCommandChanged(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
|
||||
{
|
||||
if (_currentMode == CommandPaletteMode::TabSwitchMode)
|
||||
{
|
||||
const auto& selectedCommand = _filteredActionsView().SelectedItem();
|
||||
if (const auto& command = selectedCommand.try_as<Command>())
|
||||
{
|
||||
const auto& actionAndArgs = command.Action();
|
||||
_dispatch.DoAction(actionAndArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPalette::_previewKeyDownHandler(IInspectable const& /*sender*/,
|
||||
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
@@ -200,7 +177,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto selectedItem = _filteredActionsView().SelectedItem())
|
||||
{
|
||||
_dispatchCommand(selectedItem.try_as<Command>());
|
||||
_dispatchCommand(selectedItem.try_as<TerminalApp::Command>());
|
||||
}
|
||||
}
|
||||
// Commandline Mode: Use the input to synthesize an ExecuteCommandline action
|
||||
@@ -213,27 +190,41 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (key == VirtualKey::Escape)
|
||||
{
|
||||
// Dismiss the palette if the text is empty, otherwise clear the
|
||||
// search string.
|
||||
if (_searchBox().Text().empty())
|
||||
// Action, TabSearch, TabSwitch Mode: Dismiss the palette if the
|
||||
// text is empty, otherwise clear the search string.
|
||||
if (_currentMode != CommandPaletteMode::CommandlineMode)
|
||||
{
|
||||
_dismissPalette();
|
||||
if (_searchBox().Text().empty())
|
||||
{
|
||||
_dismissPalette();
|
||||
}
|
||||
else
|
||||
{
|
||||
_searchBox().Text(L"");
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (_currentMode == CommandPaletteMode::CommandlineMode)
|
||||
{
|
||||
_searchBox().Text(L"");
|
||||
}
|
||||
const auto currentInput = _getPostPrefixInput();
|
||||
if (currentInput.empty())
|
||||
{
|
||||
// The user's only input "> " so far. We should just dismiss
|
||||
// the palette. This is like dismissing the Action mode with
|
||||
// empty input.
|
||||
_dismissPalette();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear out the current input. We'll leave a ">" in the
|
||||
// input (to stay in commandline mode), and a leading space
|
||||
// (if they currently had one).
|
||||
const bool hasLeadingSpace = (_searchBox().Text().size()) - (currentInput.size()) > 1;
|
||||
_searchBox().Text(hasLeadingSpace ? L"> " : L">");
|
||||
|
||||
e.Handled(true);
|
||||
}
|
||||
else if (key == VirtualKey::Back)
|
||||
{
|
||||
// If the last filter text was empty, and we're backspacing from
|
||||
// that state, then the user "backspaced" the virtual '>' we're
|
||||
// using as the action mode indicator. Switch into commandline mode.
|
||||
if (_searchBox().Text().empty() && _lastFilterTextWasEmpty && _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_switchToMode(CommandPaletteMode::CommandlineMode);
|
||||
// This will conveniently move the cursor to the end of the
|
||||
// text input for us.
|
||||
_searchBox().Select(_searchBox().Text().size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
e.Handled(true);
|
||||
@@ -309,7 +300,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto selectedItem = _filteredActionsView().SelectedItem())
|
||||
{
|
||||
if (const auto data = selectedItem.try_as<Command>())
|
||||
if (const auto data = selectedItem.try_as<TerminalApp::Command>())
|
||||
{
|
||||
_dispatchCommand(data);
|
||||
}
|
||||
@@ -356,7 +347,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void CommandPalette::_listItemClicked(Windows::Foundation::IInspectable const& /*sender*/,
|
||||
Windows::UI::Xaml::Controls::ItemClickEventArgs const& e)
|
||||
{
|
||||
_dispatchCommand(e.ClickedItem().try_as<Command>());
|
||||
_dispatchCommand(e.ClickedItem().try_as<TerminalApp::Command>());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -391,7 +382,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A list of Commands to filter.
|
||||
Collections::IVector<Command> CommandPalette::_commandsToFilter()
|
||||
Collections::IVector<TerminalApp::Command> CommandPalette::_commandsToFilter()
|
||||
{
|
||||
switch (_currentMode)
|
||||
{
|
||||
@@ -406,7 +397,7 @@ namespace winrt::TerminalApp::implementation
|
||||
case CommandPaletteMode::TabSwitchMode:
|
||||
return _allTabActions;
|
||||
case CommandPaletteMode::CommandlineMode:
|
||||
return winrt::single_threaded_vector<Command>();
|
||||
return winrt::single_threaded_vector<TerminalApp::Command>();
|
||||
default:
|
||||
return _allCommands;
|
||||
}
|
||||
@@ -421,7 +412,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - command: the Command to dispatch. This might be null.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_dispatchCommand(const Command& command)
|
||||
void CommandPalette::_dispatchCommand(const TerminalApp::Command& command)
|
||||
{
|
||||
if (command)
|
||||
{
|
||||
@@ -466,13 +457,18 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get all the input text in _searchBox that follows any leading spaces.
|
||||
// - Get all the input text in _searchBox that follows the prefix character
|
||||
// and any whitespace following that prefix character. This can be used in
|
||||
// commandline mode to get all the useful input that the user input after
|
||||
// the leading ">" prefix.
|
||||
// - Note that this will behave unexpectedly in Action Mode.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the string of input following any number of leading spaces
|
||||
std::wstring CommandPalette::_getTrimmedInput()
|
||||
// - the string of input following the prefix character.
|
||||
std::wstring CommandPalette::_getPostPrefixInput()
|
||||
{
|
||||
const std::wstring input{ _searchBox().Text() };
|
||||
if (input.empty())
|
||||
@@ -480,15 +476,17 @@ namespace winrt::TerminalApp::implementation
|
||||
return input;
|
||||
}
|
||||
|
||||
const auto rawCmdline{ input.substr(1) };
|
||||
|
||||
// Trim leading whitespace
|
||||
const auto firstNonSpace = input.find_first_not_of(L" ");
|
||||
const auto firstNonSpace = rawCmdline.find_first_not_of(L" ");
|
||||
if (firstNonSpace == std::wstring::npos)
|
||||
{
|
||||
// All the following characters are whitespace.
|
||||
return L"";
|
||||
}
|
||||
|
||||
return input.substr(firstNonSpace);
|
||||
return rawCmdline.substr(firstNonSpace);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -499,15 +497,19 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void CommandPalette::_dispatchCommandline()
|
||||
{
|
||||
auto cmdline{ _getTrimmedInput() };
|
||||
if (cmdline.empty())
|
||||
const auto input = _getPostPrefixInput();
|
||||
if (input.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
winrt::hstring cmdline{ input };
|
||||
|
||||
// Build the ExecuteCommandline action from the values we've parsed on the commandline.
|
||||
ExecuteCommandlineArgs args{ cmdline };
|
||||
ActionAndArgs executeActionAndArgs{ ShortcutAction::ExecuteCommandline, args };
|
||||
auto executeActionAndArgs = winrt::make_self<implementation::ActionAndArgs>();
|
||||
executeActionAndArgs->Action(ShortcutAction::ExecuteCommandline);
|
||||
auto args = winrt::make_self<implementation::ExecuteCommandlineArgs>();
|
||||
args->Commandline(cmdline);
|
||||
executeActionAndArgs->Args(*args);
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
@@ -516,7 +518,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
if (_dispatch.DoAction(executeActionAndArgs))
|
||||
if (_dispatch.DoAction(*executeActionAndArgs))
|
||||
{
|
||||
_close();
|
||||
}
|
||||
@@ -552,54 +554,37 @@ namespace winrt::TerminalApp::implementation
|
||||
void CommandPalette::_filterTextChanged(IInspectable const& /*sender*/,
|
||||
Windows::UI::Xaml::RoutedEventArgs const& /*args*/)
|
||||
{
|
||||
if (_currentMode == CommandPaletteMode::CommandlineMode)
|
||||
if (_currentMode == CommandPaletteMode::CommandlineMode || _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_evaluatePrefix();
|
||||
}
|
||||
|
||||
// We're setting _lastFilterTextWasEmpty here, because if the user tries
|
||||
// to backspace the last character in the input, the Backspace KeyDown
|
||||
// event will fire _before_ _filterTextChanged does. Updating the value
|
||||
// here will ensure that we can check this case appropriately.
|
||||
_lastFilterTextWasEmpty = _searchBox().Text().empty();
|
||||
|
||||
_updateFilteredActions();
|
||||
_filteredActionsView().SelectedIndex(0);
|
||||
|
||||
if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode)
|
||||
{
|
||||
_noMatchesText().Visibility(_filteredActions.Size() > 0 ? Visibility::Collapsed : Visibility::Visible);
|
||||
}
|
||||
else
|
||||
{
|
||||
_noMatchesText().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
_noMatchesText().Visibility(_filteredActions.Size() > 0 ? Visibility::Collapsed : Visibility::Visible);
|
||||
}
|
||||
|
||||
void CommandPalette::_evaluatePrefix()
|
||||
{
|
||||
// This will take you from commandline mode, into action mode. The
|
||||
// backspace handler in _keyDownHandler will handle taking us from
|
||||
// action mode to commandline mode.
|
||||
auto newMode = CommandPaletteMode::CommandlineMode;
|
||||
auto newMode = CommandPaletteMode::ActionMode;
|
||||
|
||||
auto inputText = _getTrimmedInput();
|
||||
auto inputText = _searchBox().Text();
|
||||
if (inputText.size() > 0)
|
||||
{
|
||||
if (inputText[0] == L'>')
|
||||
{
|
||||
newMode = CommandPaletteMode::ActionMode;
|
||||
newMode = CommandPaletteMode::CommandlineMode;
|
||||
}
|
||||
}
|
||||
|
||||
if (newMode != _currentMode)
|
||||
{
|
||||
//_switchToMode will remove the '>' character from the input.
|
||||
_switchToMode(newMode);
|
||||
}
|
||||
}
|
||||
|
||||
Collections::IObservableVector<Command> CommandPalette::FilteredActions()
|
||||
Collections::IObservableVector<TerminalApp::Command> CommandPalette::FilteredActions()
|
||||
{
|
||||
return _filteredActions;
|
||||
}
|
||||
@@ -609,7 +594,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_bindings = bindings;
|
||||
}
|
||||
|
||||
void CommandPalette::SetCommands(Collections::IVector<Command> const& actions)
|
||||
void CommandPalette::SetCommands(Collections::IVector<TerminalApp::Command> const& actions)
|
||||
{
|
||||
_allCommands = actions;
|
||||
_updateFilteredActions();
|
||||
@@ -639,8 +624,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
_searchBox().Text(L"");
|
||||
_searchBox().Select(_searchBox().Text().size(), 0);
|
||||
// Leaving this block of code outside the above if-statement
|
||||
// guarantees that the correct text is shown for the mode
|
||||
// whenever _switchToMode is called.
|
||||
@@ -649,30 +632,26 @@ namespace winrt::TerminalApp::implementation
|
||||
case CommandPaletteMode::TabSearchMode:
|
||||
case CommandPaletteMode::TabSwitchMode:
|
||||
{
|
||||
SearchBoxPlaceholderText(RS_(L"TabSwitcher_SearchBoxText"));
|
||||
SearchBoxText(RS_(L"TabSwitcher_SearchBoxText"));
|
||||
NoMatchesText(RS_(L"TabSwitcher_NoMatchesText"));
|
||||
ControlName(RS_(L"TabSwitcherControlName"));
|
||||
PrefixCharacter(L"");
|
||||
break;
|
||||
}
|
||||
case CommandPaletteMode::CommandlineMode:
|
||||
SearchBoxPlaceholderText(RS_(L"CmdPalCommandlinePrompt"));
|
||||
NoMatchesText(L"");
|
||||
NoMatchesText(RS_(L"CmdPalCommandlinePrompt"));
|
||||
ControlName(RS_(L"CommandPaletteControlName"));
|
||||
PrefixCharacter(L"");
|
||||
break;
|
||||
case CommandPaletteMode::ActionMode:
|
||||
default:
|
||||
SearchBoxPlaceholderText(RS_(L"CommandPalette_SearchBox/PlaceholderText"));
|
||||
SearchBoxText(RS_(L"CommandPalette_SearchBox/PlaceholderText"));
|
||||
NoMatchesText(RS_(L"CommandPalette_NoMatchesText/Text"));
|
||||
ControlName(RS_(L"CommandPaletteControlName"));
|
||||
PrefixCharacter(L">");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
|
||||
static bool _compareCommandNames(const Command& lhs, const Command& rhs)
|
||||
static bool _compareCommandNames(const TerminalApp::Command& lhs, const TerminalApp::Command& rhs)
|
||||
{
|
||||
std::wstring_view leftName{ lhs.Name() };
|
||||
std::wstring_view rightName{ rhs.Name() };
|
||||
@@ -682,7 +661,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// This is a helper struct to aid in sorting Commands by a given weighting.
|
||||
struct WeightedCommand
|
||||
{
|
||||
Command command;
|
||||
TerminalApp::Command command;
|
||||
int weight;
|
||||
int inOrderCounter;
|
||||
|
||||
@@ -714,11 +693,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// - A collection that will receive the filtered actions
|
||||
// Return Value:
|
||||
// - <none>
|
||||
std::vector<Command> CommandPalette::_collectFilteredActions()
|
||||
std::vector<winrt::TerminalApp::Command> CommandPalette::_collectFilteredActions()
|
||||
{
|
||||
std::vector<Command> actions;
|
||||
std::vector<winrt::TerminalApp::Command> actions;
|
||||
|
||||
winrt::hstring searchText{ _getTrimmedInput() };
|
||||
auto searchText = _searchBox().Text();
|
||||
const bool addAll = searchText.empty();
|
||||
|
||||
auto commandsToFilter = _commandsToFilter();
|
||||
@@ -741,7 +720,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Add all the commands, but make sure they're sorted alphabetically.
|
||||
std::vector<Command> sortedCommands;
|
||||
std::vector<TerminalApp::Command> sortedCommands;
|
||||
sortedCommands.reserve(commandsToFilter.Size());
|
||||
|
||||
for (auto action : commandsToFilter)
|
||||
@@ -996,17 +975,30 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
case CollectionChange::ItemChanged:
|
||||
{
|
||||
winrt::com_ptr<Command> item;
|
||||
item.copy_from(winrt::get_self<Command>(_allTabActions.GetAt(idx)));
|
||||
item->propertyChangedRevoker.revoke();
|
||||
|
||||
auto tab = tabList.GetAt(idx);
|
||||
GenerateCommandForTab(idx, false, tab);
|
||||
UpdateTabIndices(idx);
|
||||
break;
|
||||
}
|
||||
case CollectionChange::ItemInserted:
|
||||
{
|
||||
auto tab = tabList.GetAt(idx);
|
||||
_allTabActions.InsertAt(idx, tab.SwitchToTabCommand());
|
||||
GenerateCommandForTab(idx, true, tab);
|
||||
UpdateTabIndices(idx);
|
||||
break;
|
||||
}
|
||||
case CollectionChange::ItemRemoved:
|
||||
{
|
||||
winrt::com_ptr<Command> item;
|
||||
item.copy_from(winrt::get_self<Command>(_allTabActions.GetAt(idx)));
|
||||
item->propertyChangedRevoker.revoke();
|
||||
|
||||
_allTabActions.RemoveAt(idx);
|
||||
UpdateTabIndices(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1015,6 +1007,83 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - In the case where a tab is removed or reordered, the given indices of
|
||||
// the tab switch commands following the removed/reordered tab will get out of sync by 1
|
||||
// (e.g. if tab 1 is removed, tabs 2,3,4,... need to become tabs 1,2,3,...)
|
||||
// This function just loops through the tabs following startIdx and adjusts their given indices.
|
||||
// Arguments:
|
||||
// - startIdx: The index to start the update loop at.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::UpdateTabIndices(const uint32_t startIdx)
|
||||
{
|
||||
for (auto i = startIdx; i < _allTabActions.Size(); ++i)
|
||||
{
|
||||
auto command = _allTabActions.GetAt(i);
|
||||
|
||||
command.Action().Args().as<implementation::SwitchToTabArgs>()->TabIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a tab switching command based on the given tab object and insert/update the command
|
||||
// at the given index. The command will call a SwitchToTab action on the given idx.
|
||||
// Arguments:
|
||||
// - idx: The index to insert or update the tab switch command.
|
||||
// - tab: The tab object to refer to when creating the tab switch command.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::GenerateCommandForTab(const uint32_t idx, bool inserted, TerminalApp::Tab& tab)
|
||||
{
|
||||
auto focusTabAction = winrt::make_self<implementation::ActionAndArgs>();
|
||||
auto args = winrt::make_self<implementation::SwitchToTabArgs>();
|
||||
args->TabIndex(idx);
|
||||
|
||||
focusTabAction->Action(ShortcutAction::SwitchToTab);
|
||||
focusTabAction->Args(*args);
|
||||
|
||||
auto command = winrt::make_self<implementation::Command>();
|
||||
command->Action(*focusTabAction);
|
||||
command->Name(tab.Title());
|
||||
command->IconSource(tab.IconSource());
|
||||
|
||||
// Listen for changes to the Tab so we can update this Command's attributes accordingly.
|
||||
auto weakThis{ get_weak() };
|
||||
auto weakCommand{ command->get_weak() };
|
||||
command->propertyChangedRevoker = tab.PropertyChanged(winrt::auto_revoke, [weakThis, weakCommand, tab](auto&&, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args) {
|
||||
auto palette{ weakThis.get() };
|
||||
auto command{ weakCommand.get() };
|
||||
|
||||
if (palette && command)
|
||||
{
|
||||
if (args.PropertyName() == L"Title")
|
||||
{
|
||||
if (command->Name() != tab.Title())
|
||||
{
|
||||
command->Name(tab.Title());
|
||||
}
|
||||
}
|
||||
if (args.PropertyName() == L"IconSource")
|
||||
{
|
||||
if (command->IconSource() != tab.IconSource())
|
||||
{
|
||||
command->IconSource(tab.IconSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (inserted)
|
||||
{
|
||||
_allTabActions.InsertAt(idx, *command);
|
||||
}
|
||||
else
|
||||
{
|
||||
_allTabActions.SetAt(idx, *command);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPalette::EnableTabSwitcherMode(const bool searchMode, const uint32_t startIdx)
|
||||
{
|
||||
_switcherStartIdx = startIdx;
|
||||
|
||||
@@ -20,9 +20,9 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
CommandPalette();
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Model::Command> FilteredActions();
|
||||
Windows::Foundation::Collections::IObservableVector<TerminalApp::Command> FilteredActions();
|
||||
|
||||
void SetCommands(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> const& actions);
|
||||
void SetCommands(Windows::Foundation::Collections::IVector<TerminalApp::Command> const& actions);
|
||||
void SetKeyBindings(Microsoft::Terminal::TerminalControl::IKeyBindings bindings);
|
||||
|
||||
void EnableCommandPaletteMode();
|
||||
@@ -39,24 +39,21 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, PrefixCharacter, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, SearchBoxText, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
|
||||
|
||||
private:
|
||||
friend struct CommandPaletteT<CommandPalette>; // for Xaml to bind events
|
||||
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _allCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _currentNestedCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Model::Command> _filteredActions{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _nestedActionStack{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<TerminalApp::Command> _allCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<TerminalApp::Command> _currentNestedCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<TerminalApp::Command> _filteredActions{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<TerminalApp::Command> _nestedActionStack{ nullptr };
|
||||
|
||||
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
|
||||
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _commandsToFilter();
|
||||
|
||||
bool _lastFilterTextWasEmpty{ true };
|
||||
Windows::Foundation::Collections::IVector<TerminalApp::Command> _commandsToFilter();
|
||||
|
||||
void _filterTextChanged(Windows::Foundation::IInspectable const& sender,
|
||||
Windows::UI::Xaml::RoutedEventArgs const& args);
|
||||
@@ -67,9 +64,6 @@ namespace winrt::TerminalApp::implementation
|
||||
void _keyUpHandler(Windows::Foundation::IInspectable const& sender,
|
||||
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
|
||||
|
||||
void _selectedCommandChanged(Windows::Foundation::IInspectable const& sender,
|
||||
Windows::UI::Xaml::RoutedEventArgs const& args);
|
||||
|
||||
void _updateUIForStackChange();
|
||||
|
||||
void _rootPointerPressed(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
@@ -79,7 +73,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _updateFilteredActions();
|
||||
|
||||
std::vector<Microsoft::Terminal::Settings::Model::Command> _collectFilteredActions();
|
||||
std::vector<winrt::TerminalApp::Command> _collectFilteredActions();
|
||||
|
||||
static int _getWeight(const winrt::hstring& searchText, const winrt::hstring& name);
|
||||
void _close();
|
||||
@@ -87,19 +81,21 @@ namespace winrt::TerminalApp::implementation
|
||||
CommandPaletteMode _currentMode;
|
||||
void _switchToMode(CommandPaletteMode mode);
|
||||
|
||||
std::wstring _getTrimmedInput();
|
||||
void _evaluatePrefix();
|
||||
std::wstring _getPostPrefixInput();
|
||||
|
||||
Microsoft::Terminal::TerminalControl::IKeyBindings _bindings;
|
||||
|
||||
// Tab Switcher
|
||||
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _allTabActions{ nullptr };
|
||||
void GenerateCommandForTab(const uint32_t idx, bool inserted, winrt::TerminalApp::Tab& tab);
|
||||
void UpdateTabIndices(const uint32_t startIdx);
|
||||
Windows::Foundation::Collections::IVector<TerminalApp::Command> _allTabActions{ nullptr };
|
||||
uint32_t _switcherStartIdx;
|
||||
void _anchorKeyUpHandler();
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker;
|
||||
|
||||
void _dispatchCommand(const Microsoft::Terminal::Settings::Model::Command& command);
|
||||
void _dispatchCommand(const TerminalApp::Command& command);
|
||||
void _dispatchCommandline();
|
||||
void _dismissPalette();
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "Command.idl";
|
||||
import "IDirectKeyListener.idl";
|
||||
import "ShortcutActionDispatch.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
@@ -11,14 +11,13 @@ namespace TerminalApp
|
||||
CommandPalette();
|
||||
|
||||
String NoMatchesText { get; };
|
||||
String SearchBoxPlaceholderText { get; };
|
||||
String PrefixCharacter { get; };
|
||||
String SearchBoxText { get; };
|
||||
String ControlName { get; };
|
||||
String ParentCommandName { get; };
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.Command> FilteredActions { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<Command> FilteredActions { get; };
|
||||
|
||||
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
|
||||
void SetCommands(Windows.Foundation.Collections.IVector<Command> actions);
|
||||
void SetKeyBindings(Microsoft.Terminal.TerminalControl.IKeyBindings bindings);
|
||||
void EnableCommandPaletteMode();
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ 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"
|
||||
xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)"
|
||||
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
|
||||
TabNavigation="Cycle"
|
||||
IsTabStop="True"
|
||||
AllowFocusOnInteraction="True"
|
||||
@@ -31,7 +30,6 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<local:EmptyStringVisibilityConverter x:Key="CommandKeyChordVisibilityConverter"/>
|
||||
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter"/>
|
||||
<local:HasNestedCommandsVisibilityConverter x:Key="HasNestedCommandsVisibilityConverter"/>
|
||||
<local:IconPathConverter x:Key="IconSourceConverter"/>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
@@ -60,7 +58,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Style x:Key="KeyChordBorderStyle" TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock">
|
||||
@@ -91,7 +89,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Style x:Key="KeyChordBorderStyle" TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle" TargetType="TextBlock">
|
||||
@@ -135,27 +133,27 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
to receive clicks _anywhere_ in its bounds. -->
|
||||
|
||||
<Grid
|
||||
x:Name="_shadowBackdrop"
|
||||
Background="Transparent"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
x:Name="_shadowBackdrop"
|
||||
Background="Transparent"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.RowSpan="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
x:Name="_backdrop"
|
||||
Style="{ThemeResource CommandPaletteBackground}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
PointerPressed="_backdropPointerPressed"
|
||||
Margin="8"
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Windows10version1903:Shadow="{StaticResource CommandPaletteShadow}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top">
|
||||
x:Name="_backdrop"
|
||||
Style="{ThemeResource CommandPaletteBackground}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||
PointerPressed="_backdropPointerPressed"
|
||||
Margin="8"
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Windows10version1903:Shadow="{StaticResource CommandPaletteShadow}"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@@ -164,28 +162,15 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Row="0"
|
||||
x:Name="_searchBox"
|
||||
Margin="8"
|
||||
Padding="18,8,8,8"
|
||||
IsSpellCheckEnabled="False"
|
||||
TextChanged="_filterTextChanged"
|
||||
PlaceholderText="{x:Bind SearchBoxPlaceholderText, Mode=OneWay}"
|
||||
Text="">
|
||||
Grid.Row="0"
|
||||
x:Name="_searchBox"
|
||||
Margin="8"
|
||||
IsSpellCheckEnabled="False"
|
||||
TextChanged="_filterTextChanged"
|
||||
PlaceholderText="{x:Bind SearchBoxText, Mode=OneWay}"
|
||||
Text="">
|
||||
</TextBox>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
x:Name="_prefixCharacter"
|
||||
Margin="16,16,0,-8"
|
||||
FontSize="14"
|
||||
Visibility="{x:Bind PrefixCharacter,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource ParentCommandVisibilityConverter}}"
|
||||
Text="{x:Bind PrefixCharacter, Mode=OneWay}"
|
||||
>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Padding="16, 0, 16, 4"
|
||||
x:Name="_parentCommandText"
|
||||
@@ -207,20 +192,20 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</TextBlock>
|
||||
|
||||
<ListView
|
||||
Grid.Row="2"
|
||||
x:Name="_filteredActionsView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
SelectionMode="Single"
|
||||
CanReorderItems="False"
|
||||
AllowDrop="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="_listItemClicked"
|
||||
PreviewKeyDown="_keyDownHandler"
|
||||
ItemsSource="{x:Bind FilteredActions}">
|
||||
Grid.Row="2"
|
||||
x:Name="_filteredActionsView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
SelectionMode="Single"
|
||||
CanReorderItems="False"
|
||||
AllowDrop="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="_listItemClicked"
|
||||
PreviewKeyDown="_keyDownHandler"
|
||||
ItemsSource="{x:Bind FilteredActions}">
|
||||
|
||||
<ItemsControl.ItemTemplate >
|
||||
<DataTemplate x:DataType="SettingsModel:Command">
|
||||
<DataTemplate x:DataType="local:Command">
|
||||
|
||||
<!-- This HorizontalContentAlignment="Stretch" is important
|
||||
to make sure it takes the entire width of the line -->
|
||||
@@ -241,9 +226,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
IconSource="{x:Bind Icon,
|
||||
Mode=OneWay,
|
||||
Converter={StaticResource IconSourceConverter}}"/>
|
||||
IconSource="{x:Bind IconSource, Mode=OneWay}"/>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "DebugTapConnection.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace ::winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace ::winrt::Windows::Foundation;
|
||||
@@ -93,13 +94,13 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
||||
|
||||
void DebugTapConnection::_OutputHandler(const hstring str)
|
||||
{
|
||||
_TerminalOutputHandlers(til::visualize_control_codes(str));
|
||||
_TerminalOutputHandlers(VisualizeControlCodes(str));
|
||||
}
|
||||
|
||||
// Called by the DebugInputTapConnection to print user input
|
||||
void DebugTapConnection::_PrintInput(const hstring& str)
|
||||
{
|
||||
auto clean{ til::visualize_control_codes(str) };
|
||||
auto clean{ VisualizeControlCodes(str) };
|
||||
auto formatted{ wil::str_printf<std::wstring>(L"\x1b[91m%ls\x1b[m", clean.data()) };
|
||||
_TerminalOutputHandlers(formatted);
|
||||
}
|
||||
|
||||
@@ -15,18 +15,19 @@ static constexpr std::wstring_view PACKAGED_PROFILE_ICON_EXTENSION{ L".png" };
|
||||
// - name: the name of the new profile.
|
||||
// Return Value:
|
||||
// - A Profile, ready to be filled in
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile CreateDefaultProfile(const std::wstring_view name)
|
||||
TerminalApp::Profile CreateDefaultProfile(const std::wstring_view name)
|
||||
{
|
||||
const winrt::guid profileGuid{ Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID,
|
||||
gsl::as_bytes(gsl::make_span(name))) };
|
||||
auto newProfile = winrt::make<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile>(profileGuid);
|
||||
newProfile.Name(name);
|
||||
const auto profileGuid{ Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID,
|
||||
gsl::as_bytes(gsl::make_span(name))) };
|
||||
TerminalApp::Profile newProfile{ profileGuid };
|
||||
|
||||
newProfile.SetName(name);
|
||||
|
||||
std::wstring iconPath{ PACKAGED_PROFILE_ICON_PATH };
|
||||
iconPath.append(Microsoft::Console::Utils::GuidToString(profileGuid));
|
||||
iconPath.append(PACKAGED_PROFILE_ICON_EXTENSION);
|
||||
|
||||
newProfile.Icon(iconPath);
|
||||
newProfile.SetIconPath(iconPath);
|
||||
|
||||
return newProfile;
|
||||
}
|
||||
@@ -20,4 +20,4 @@ Author(s):
|
||||
// uuidv5 properties: name format is UTF-16LE bytes
|
||||
static constexpr GUID TERMINAL_PROFILE_NAMESPACE_GUID = { 0x2bde4a90, 0xd05f, 0x401c, { 0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8 } };
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile CreateDefaultProfile(const std::wstring_view name);
|
||||
TerminalApp::Profile CreateDefaultProfile(const std::wstring_view name);
|
||||
@@ -5,13 +5,12 @@
|
||||
#include "GlobalAppSettings.h"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
#include "GlobalAppSettings.g.cpp"
|
||||
|
||||
using namespace Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace winrt::Microsoft::UI::Xaml::Controls;
|
||||
@@ -38,7 +37,6 @@ static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
|
||||
static constexpr std::string_view EnableStartupTaskKey{ "startOnUserLogin" };
|
||||
static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" };
|
||||
static constexpr std::string_view UseTabSwitcherKey{ "useTabSwitcher" };
|
||||
static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" };
|
||||
|
||||
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
|
||||
|
||||
@@ -53,42 +51,73 @@ static constexpr bool debugFeaturesDefault{ false };
|
||||
#endif
|
||||
|
||||
GlobalAppSettings::GlobalAppSettings() :
|
||||
_keymap{ winrt::make_self<KeyMapping>() },
|
||||
_keybindings{ winrt::make_self<winrt::TerminalApp::implementation::AppKeyBindings>() },
|
||||
_keybindingsWarnings{},
|
||||
_unparsedDefaultProfile{},
|
||||
_colorSchemes{},
|
||||
_unparsedDefaultProfile{ std::nullopt },
|
||||
_defaultProfile{},
|
||||
_InitialRows{ DEFAULT_ROWS },
|
||||
_InitialCols{ DEFAULT_COLS },
|
||||
_WordDelimiters{ DEFAULT_WORD_DELIMITERS },
|
||||
_DebugFeaturesEnabled{ debugFeaturesDefault }
|
||||
{
|
||||
_commands = winrt::single_threaded_map<winrt::hstring, Model::Command>();
|
||||
_colorSchemes = winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>();
|
||||
_commands = winrt::single_threaded_map<winrt::hstring, winrt::TerminalApp::Command>();
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::ColorScheme> GlobalAppSettings::ColorSchemes() noexcept
|
||||
GlobalAppSettings::~GlobalAppSettings()
|
||||
{
|
||||
return _colorSchemes.GetView();
|
||||
}
|
||||
|
||||
void GlobalAppSettings::DefaultProfile(const winrt::guid& defaultProfile) noexcept
|
||||
std::unordered_map<std::wstring, ColorScheme>& GlobalAppSettings::GetColorSchemes() noexcept
|
||||
{
|
||||
_unparsedDefaultProfile.clear();
|
||||
return _colorSchemes;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::wstring, ColorScheme>& GlobalAppSettings::GetColorSchemes() const noexcept
|
||||
{
|
||||
return _colorSchemes;
|
||||
}
|
||||
|
||||
void GlobalAppSettings::DefaultProfile(const GUID defaultProfile) noexcept
|
||||
{
|
||||
_unparsedDefaultProfile.reset();
|
||||
_defaultProfile = defaultProfile;
|
||||
}
|
||||
|
||||
winrt::guid GlobalAppSettings::DefaultProfile() const
|
||||
GUID GlobalAppSettings::DefaultProfile() const
|
||||
{
|
||||
// If we have an unresolved default profile, we should likely explode.
|
||||
THROW_HR_IF(E_INVALIDARG, !_unparsedDefaultProfile.empty());
|
||||
THROW_HR_IF(E_INVALIDARG, _unparsedDefaultProfile.has_value());
|
||||
return _defaultProfile;
|
||||
}
|
||||
|
||||
winrt::hstring GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
std::optional<std::wstring> GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
{
|
||||
return _unparsedDefaultProfile;
|
||||
}
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::KeyMapping GlobalAppSettings::KeyMap() const noexcept
|
||||
AppKeyBindings GlobalAppSettings::GetKeybindings() const noexcept
|
||||
{
|
||||
return *_keymap;
|
||||
return *_keybindings;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Applies appropriate settings from the globals into the given TerminalSettings.
|
||||
// Arguments:
|
||||
// - settings: a TerminalSettings object to add global property values to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexcept
|
||||
{
|
||||
settings.KeyBindings(GetKeybindings());
|
||||
settings.InitialRows(_InitialRows);
|
||||
settings.InitialCols(_InitialCols);
|
||||
|
||||
settings.WordDelimiters(_WordDelimiters);
|
||||
settings.CopyOnSelect(_CopyOnSelect);
|
||||
settings.ForceFullRepaintRendering(_ForceFullRepaintRendering);
|
||||
settings.SoftwareRendering(_SoftwareRendering);
|
||||
settings.ForceVTInput(_ForceVTInput);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -97,10 +126,10 @@ winrt::Microsoft::Terminal::Settings::Model::KeyMapping GlobalAppSettings::KeyMa
|
||||
// - json: an object which should be a serialization of a GlobalAppSettings object.
|
||||
// Return Value:
|
||||
// - a new GlobalAppSettings instance created from the values in `json`
|
||||
winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::FromJson(const Json::Value& json)
|
||||
GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
|
||||
{
|
||||
auto result = winrt::make_self<GlobalAppSettings>();
|
||||
result->LayerJson(json);
|
||||
GlobalAppSettings result;
|
||||
result.LayerJson(json);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -154,15 +183,13 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
|
||||
JsonUtils::GetValueForKey(json, UseTabSwitcherKey, _UseTabSwitcher);
|
||||
|
||||
JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations);
|
||||
|
||||
// This is a helper lambda to get the keybindings and commands out of both
|
||||
// and array of objects. We'll use this twice, once on the legacy
|
||||
// `keybindings` key, and again on the newer `bindings` key.
|
||||
auto parseBindings = [this, &json](auto jsonKey) {
|
||||
if (auto bindings{ json[JsonKey(jsonKey)] })
|
||||
{
|
||||
auto warnings = _keymap->LayerJson(bindings);
|
||||
auto warnings = _keybindings->LayerJson(bindings);
|
||||
// It's possible that the user provided keybindings have some warnings
|
||||
// in them - problems that we should alert the user to, but we can
|
||||
// recover from. Most of these warnings cannot be detected later in the
|
||||
@@ -172,7 +199,9 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
_keybindingsWarnings.insert(_keybindingsWarnings.end(), warnings.begin(), warnings.end());
|
||||
|
||||
// Now parse the array again, but this time as a list of commands.
|
||||
warnings = implementation::Command::LayerJson(_commands, bindings);
|
||||
warnings = winrt::TerminalApp::implementation::Command::LayerJson(_commands, bindings);
|
||||
// It's possible that the user provided commands have some warnings
|
||||
// in them, similar to the keybindings.
|
||||
}
|
||||
};
|
||||
parseBindings(LegacyKeybindingsKey);
|
||||
@@ -185,9 +214,10 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
// - scheme: the color scheme to add
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void GlobalAppSettings::AddColorScheme(const Model::ColorScheme& scheme)
|
||||
void GlobalAppSettings::AddColorScheme(ColorScheme scheme)
|
||||
{
|
||||
_colorSchemes.Insert(scheme.Name(), scheme);
|
||||
std::wstring name{ scheme.Name() };
|
||||
_colorSchemes[name] = std::move(scheme);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -199,12 +229,17 @@ void GlobalAppSettings::AddColorScheme(const Model::ColorScheme& scheme)
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
std::vector<winrt::Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> GlobalAppSettings::KeybindingsWarnings() const
|
||||
std::vector<TerminalApp::SettingsLoadWarnings> GlobalAppSettings::GetKeybindingsWarnings() const
|
||||
{
|
||||
return _keybindingsWarnings;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::Command> GlobalAppSettings::Commands() noexcept
|
||||
const winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& GlobalAppSettings::GetCommands() const noexcept
|
||||
{
|
||||
return _commands.GetView();
|
||||
return _commands;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& GlobalAppSettings::GetCommands() noexcept
|
||||
{
|
||||
return _commands;
|
||||
}
|
||||
99
src/cascadia/TerminalApp/GlobalAppSettings.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- CascadiaSettings.hpp
|
||||
|
||||
Abstract:
|
||||
- This class encapsulates all of the settings that are global to the app, and
|
||||
not a part of any particular profile.
|
||||
|
||||
Author(s):
|
||||
- Mike Griese - March 2019
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include "AppKeyBindings.h"
|
||||
#include "Command.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
#include "ColorScheme.g.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class SettingsTests;
|
||||
class ColorSchemeTests;
|
||||
};
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
class GlobalAppSettings;
|
||||
};
|
||||
|
||||
class TerminalApp::GlobalAppSettings final
|
||||
{
|
||||
public:
|
||||
GlobalAppSettings();
|
||||
~GlobalAppSettings();
|
||||
|
||||
std::unordered_map<std::wstring, winrt::TerminalApp::ColorScheme>& GetColorSchemes() noexcept;
|
||||
const std::unordered_map<std::wstring, winrt::TerminalApp::ColorScheme>& GetColorSchemes() const noexcept;
|
||||
void AddColorScheme(winrt::TerminalApp::ColorScheme scheme);
|
||||
|
||||
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
|
||||
|
||||
static GlobalAppSettings FromJson(const Json::Value& json);
|
||||
void LayerJson(const Json::Value& json);
|
||||
|
||||
void ApplyToSettings(winrt::TerminalApp::TerminalSettings& settings) const noexcept;
|
||||
|
||||
std::vector<TerminalApp::SettingsLoadWarnings> GetKeybindingsWarnings() const;
|
||||
|
||||
const winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& GetCommands() const noexcept;
|
||||
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command>& GetCommands() noexcept;
|
||||
|
||||
// These are implemented manually to handle the string/GUID exchange
|
||||
// by higher layers in the app.
|
||||
void DefaultProfile(const GUID defaultProfile) noexcept;
|
||||
GUID DefaultProfile() const;
|
||||
std::optional<std::wstring> UnparsedDefaultProfile() const;
|
||||
|
||||
GETSET_PROPERTY(int32_t, InitialRows); // default value set in constructor
|
||||
GETSET_PROPERTY(int32_t, InitialCols); // default value set in constructor
|
||||
GETSET_PROPERTY(bool, AlwaysShowTabs, true);
|
||||
GETSET_PROPERTY(bool, ShowTitleInTitlebar, true);
|
||||
GETSET_PROPERTY(bool, ConfirmCloseAllTabs, true);
|
||||
GETSET_PROPERTY(winrt::Windows::UI::Xaml::ElementTheme, Theme, winrt::Windows::UI::Xaml::ElementTheme::Default);
|
||||
GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal);
|
||||
GETSET_PROPERTY(bool, ShowTabsInTitlebar, true);
|
||||
GETSET_PROPERTY(std::wstring, WordDelimiters); // default value set in constructor
|
||||
GETSET_PROPERTY(bool, CopyOnSelect, false);
|
||||
GETSET_PROPERTY(winrt::Microsoft::Terminal::TerminalControl::CopyFormat, CopyFormatting, 0);
|
||||
GETSET_PROPERTY(bool, WarnAboutLargePaste, true);
|
||||
GETSET_PROPERTY(bool, WarnAboutMultiLinePaste, true);
|
||||
GETSET_PROPERTY(LaunchPosition, InitialPosition);
|
||||
GETSET_PROPERTY(winrt::TerminalApp::LaunchMode, LaunchMode, winrt::TerminalApp::LaunchMode::DefaultMode);
|
||||
GETSET_PROPERTY(bool, SnapToGridOnResize, true);
|
||||
GETSET_PROPERTY(bool, ForceFullRepaintRendering, false);
|
||||
GETSET_PROPERTY(bool, SoftwareRendering, false);
|
||||
GETSET_PROPERTY(bool, ForceVTInput, false);
|
||||
GETSET_PROPERTY(bool, DebugFeaturesEnabled); // default value set in constructor
|
||||
GETSET_PROPERTY(bool, StartOnUserLogin, false);
|
||||
GETSET_PROPERTY(bool, AlwaysOnTop, false);
|
||||
GETSET_PROPERTY(bool, UseTabSwitcher, true);
|
||||
|
||||
private:
|
||||
std::optional<std::wstring> _unparsedDefaultProfile;
|
||||
GUID _defaultProfile;
|
||||
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::AppKeyBindings> _keybindings;
|
||||
std::vector<::TerminalApp::SettingsLoadWarnings> _keybindingsWarnings;
|
||||
|
||||
std::unordered_map<std::wstring, winrt::TerminalApp::ColorScheme> _colorSchemes;
|
||||
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::TerminalApp::Command> _commands;
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
};
|
||||
@@ -22,16 +22,16 @@ Author(s):
|
||||
#pragma once
|
||||
#include "Profile.h"
|
||||
|
||||
namespace Microsoft::Terminal::Settings::Model
|
||||
namespace TerminalApp
|
||||
{
|
||||
class IDynamicProfileGenerator;
|
||||
};
|
||||
|
||||
class Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator
|
||||
class TerminalApp::IDynamicProfileGenerator
|
||||
{
|
||||
public:
|
||||
virtual ~IDynamicProfileGenerator() = 0;
|
||||
virtual std::wstring_view GetNamespace() = 0;
|
||||
virtual std::vector<winrt::Microsoft::Terminal::Settings::Model::Profile> GenerateProfiles() = 0;
|
||||
virtual std::vector<TerminalApp::Profile> GenerateProfiles() = 0;
|
||||
};
|
||||
inline Microsoft::Terminal::Settings::Model::IDynamicProfileGenerator::~IDynamicProfileGenerator() {}
|
||||
inline TerminalApp::IDynamicProfileGenerator::~IDynamicProfileGenerator() {}
|
||||
@@ -1,207 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "IconPathConverter.h"
|
||||
#include "IconPathConverter.g.cpp"
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace winrt::Windows;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// These are templates that help us figure out which BitmapIconSource/FontIconSource to use for a given IconSource.
|
||||
// We have to do this because some of our code still wants to use WUX/MUX IconSources.
|
||||
#pragma region BitmapIconSource
|
||||
template<typename TIconSource>
|
||||
struct BitmapIconSource
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct BitmapIconSource<winrt::Microsoft::UI::Xaml::Controls::IconSource>
|
||||
{
|
||||
using type = winrt::Microsoft::UI::Xaml::Controls::BitmapIconSource;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct BitmapIconSource<winrt::Windows::UI::Xaml::Controls::IconSource>
|
||||
{
|
||||
using type = winrt::Windows::UI::Xaml::Controls::BitmapIconSource;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region FontIconSource
|
||||
template<typename TIconSource>
|
||||
struct FontIconSource
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FontIconSource<winrt::Microsoft::UI::Xaml::Controls::IconSource>
|
||||
{
|
||||
using type = winrt::Microsoft::UI::Xaml::Controls::FontIconSource;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FontIconSource<winrt::Windows::UI::Xaml::Controls::IconSource>
|
||||
{
|
||||
using type = winrt::Windows::UI::Xaml::Controls::FontIconSource;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
// Method Description:
|
||||
// - Creates an IconSource for the given path. The icon returned is a colored
|
||||
// icon. If we couldn't create the icon for any reason, we return an empty
|
||||
// IconElement.
|
||||
// Template Types:
|
||||
// - <TIconSource>: The type of IconSource (MUX, WUX) to generate.
|
||||
// Arguments:
|
||||
// - path: the full, expanded path to the icon.
|
||||
// Return Value:
|
||||
// - An IconElement with its IconSource set, if possible.
|
||||
template<typename TIconSource>
|
||||
TIconSource _getColoredBitmapIcon(const winrt::hstring& path)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
try
|
||||
{
|
||||
winrt::Windows::Foundation::Uri iconUri{ path };
|
||||
BitmapIconSource<TIconSource>::type iconSource;
|
||||
// Make sure to set this to false, so we keep the RGB data of the
|
||||
// image. Otherwise, the icon will be white for all the
|
||||
// non-transparent pixels in the image.
|
||||
iconSource.ShowAsMonochrome(false);
|
||||
iconSource.UriSource(iconUri);
|
||||
return iconSource;
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates an IconSource for the given path.
|
||||
// * If the icon is a path to an image, we'll use that.
|
||||
// * If it isn't, then we'll try and use the text as a FontIcon. If the
|
||||
// character is in the range of symbols reserved for the Segoe MDL2
|
||||
// Asserts, well treat it as such. Otherwise, we'll default to a Sego
|
||||
// UI icon, so things like emoji will work.
|
||||
// * If we couldn't create the icon for any reason, we return an empty
|
||||
// IconElement.
|
||||
// Template Types:
|
||||
// - <TIconSource>: The type of IconSource (MUX, WUX) to generate.
|
||||
// Arguments:
|
||||
// - path: the unprocessed path to the icon.
|
||||
// Return Value:
|
||||
// - An IconElement with its IconSource set, if possible.
|
||||
template<typename TIconSource>
|
||||
TIconSource _getIconSource(const winrt::hstring& iconPath)
|
||||
{
|
||||
TIconSource iconSource{ nullptr };
|
||||
|
||||
if (iconPath.size() != 0)
|
||||
{
|
||||
const auto expandedIconPath{ _expandIconPath(iconPath) };
|
||||
iconSource = _getColoredBitmapIcon<TIconSource>(expandedIconPath);
|
||||
|
||||
// If we fail to set the icon source using the "icon" as a path,
|
||||
// let's try it as a symbol/emoji.
|
||||
//
|
||||
// Anything longer than 2 wchar_t's _isn't_ an emoji or symbol, so
|
||||
// don't do this if it's just an invalid path.
|
||||
if (!iconSource && iconPath.size() <= 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
FontIconSource<TIconSource>::type icon;
|
||||
const wchar_t ch = iconPath[0];
|
||||
|
||||
// The range of MDL2 Icons isn't explicitly defined, but
|
||||
// we're using this based off the table on:
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font
|
||||
const bool isMDL2Icon = ch >= L'\uE700' && ch <= L'\uF8FF';
|
||||
if (isMDL2Icon)
|
||||
{
|
||||
icon.FontFamily(winrt::Windows::UI::Xaml::Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: you _do_ need to manually set the font here.
|
||||
icon.FontFamily(winrt::Windows::UI::Xaml::Media::FontFamily{ L"Segoe UI" });
|
||||
}
|
||||
icon.FontSize(12);
|
||||
icon.Glyph(iconPath);
|
||||
iconSource = icon;
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
if (!iconSource)
|
||||
{
|
||||
// Set the default IconSource to a BitmapIconSource with a null source
|
||||
// (instead of just nullptr) because there's a really weird crash when swapping
|
||||
// data bound IconSourceElements in a ListViewTemplate (i.e. CommandPalette).
|
||||
// Swapping between nullptr IconSources and non-null IconSources causes a crash
|
||||
// to occur, but swapping between IconSources with a null source and non-null IconSources
|
||||
// work perfectly fine :shrug:.
|
||||
BitmapIconSource<TIconSource>::type icon;
|
||||
icon.UriSource(nullptr);
|
||||
iconSource = icon;
|
||||
}
|
||||
|
||||
return iconSource;
|
||||
}
|
||||
|
||||
static winrt::hstring _expandIconPath(hstring iconPath)
|
||||
{
|
||||
if (iconPath.empty())
|
||||
{
|
||||
return iconPath;
|
||||
}
|
||||
winrt::hstring envExpandedPath{ wil::ExpandEnvironmentStringsW<std::wstring>(iconPath.c_str()) };
|
||||
return envExpandedPath;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to convert something into another type. For the
|
||||
// IconPathConverter, we support a variety of icons:
|
||||
// * If the icon is a path to an image, we'll use that.
|
||||
// * If it isn't, then we'll try and use the text as a FontIcon. If the
|
||||
// character is in the range of symbols reserved for the Segoe MDL2
|
||||
// Asserts, well treat it as such. Otherwise, we'll default to a Sego
|
||||
// UI icon, so things like emoji will work.
|
||||
// - MUST BE CALLED ON THE UI THREAD.
|
||||
// Arguments:
|
||||
// - value: the input object to attempt to convert into an IconSource.
|
||||
// Return Value:
|
||||
// - Visible if the object was a string and wasn't the empty string.
|
||||
Foundation::IInspectable IconPathConverter::Convert(Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
|
||||
Foundation::IInspectable const& /* parameter */,
|
||||
hstring const& /* language */)
|
||||
{
|
||||
const auto& iconPath = winrt::unbox_value_or<winrt::hstring>(value, L"");
|
||||
return _getIconSource<Controls::IconSource>(iconPath);
|
||||
}
|
||||
|
||||
// unused for one-way bindings
|
||||
Foundation::IInspectable IconPathConverter::ConvertBack(Foundation::IInspectable const& /* value */,
|
||||
Windows::UI::Xaml::Interop::TypeName const& /* targetType */,
|
||||
Foundation::IInspectable const& /* parameter */,
|
||||
hstring const& /* language */)
|
||||
{
|
||||
throw hresult_not_implemented();
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Controls::IconSource IconPathConverter::IconSourceWUX(hstring path)
|
||||
{
|
||||
return _getIconSource<Windows::UI::Xaml::Controls::IconSource>(path);
|
||||
}
|
||||
|
||||
Microsoft::UI::Xaml::Controls::IconSource IconPathConverter::IconSourceMUX(hstring path)
|
||||
{
|
||||
return _getIconSource<Microsoft::UI::Xaml::Controls::IconSource>(path);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "IconPathConverter.g.h"
|
||||
#include "..\inc\cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct IconPathConverter : IconPathConverterT<IconPathConverter>
|
||||
{
|
||||
IconPathConverter() = default;
|
||||
|
||||
Windows::Foundation::IInspectable Convert(Windows::Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& targetType,
|
||||
Windows::Foundation::IInspectable const& parameter,
|
||||
hstring const& language);
|
||||
|
||||
Windows::Foundation::IInspectable ConvertBack(Windows::Foundation::IInspectable const& value,
|
||||
Windows::UI::Xaml::Interop::TypeName const& targetType,
|
||||
Windows::Foundation::IInspectable const& parameter,
|
||||
hstring const& language);
|
||||
|
||||
static Windows::UI::Xaml::Controls::IconSource IconSourceWUX(hstring path);
|
||||
static Microsoft::UI::Xaml::Controls::IconSource IconSourceMUX(hstring path);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(IconPathConverter);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
// See https://docs.microsoft.com/en-us/windows/uwp/data-binding/data-binding-quickstart
|
||||
|
||||
// We use the default attribute to declare IValueConverter as the default
|
||||
// interface. In the listing, IconPathConverter has only a
|
||||
// constructor, and no methods, so no default interface is generated for it.
|
||||
// The default attribute is optimal if you won't be adding instance members
|
||||
// to IconPathConverter, because no QueryInterface will be
|
||||
// required to call the IValueConverter methods
|
||||
runtimeclass IconPathConverter : [default] Windows.UI.Xaml.Data.IValueConverter
|
||||
{
|
||||
IconPathConverter();
|
||||
|
||||
static Windows.UI.Xaml.Controls.IconSource IconSourceWUX(String path);
|
||||
static Microsoft.UI.Xaml.Controls.IconSource IconSourceMUX(String path);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@ Module Name:
|
||||
- JsonUtils.h
|
||||
|
||||
Abstract:
|
||||
- Helpers for the Terminal Settings Model project
|
||||
- Helpers for the TerminalApp project
|
||||
Author(s):
|
||||
- Mike Griese - August 2019
|
||||
- Dustin Howett - January 2020
|
||||
@@ -31,21 +31,7 @@ namespace winrt
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a std::string from a string_view. We do this because we can't look
|
||||
// up a key in a Json::Value with a string_view directly, so instead we'll use
|
||||
// this helper. Should a string_view lookup ever be added to jsoncpp, we can
|
||||
// remove this entirely.
|
||||
// Arguments:
|
||||
// - key: the string_view to build a string from
|
||||
// Return Value:
|
||||
// - a std::string to use for looking up a value from a Json::Value
|
||||
inline std::string JsonKey(const std::string_view key)
|
||||
{
|
||||
return static_cast<std::string>(key);
|
||||
}
|
||||
|
||||
namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
namespace TerminalApp::JsonUtils
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
@@ -81,13 +67,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct DeduceOptional<::winrt::hstring>
|
||||
{
|
||||
using Type = typename ::winrt::hstring;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
}
|
||||
|
||||
class DeserializationError : public std::runtime_error
|
||||
@@ -117,8 +96,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
T FromJson(const Json::Value&);
|
||||
bool CanConvert(const Json::Value& json);
|
||||
|
||||
Json::Value ToJson(const T& val);
|
||||
|
||||
std::string TypeDescription() const { return "<unknown>"; }
|
||||
};
|
||||
|
||||
@@ -135,11 +112,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isString();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const std::string& val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "string";
|
||||
@@ -159,11 +131,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isString();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const std::wstring& val)
|
||||
{
|
||||
return til::u16u8(val);
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "string";
|
||||
@@ -179,11 +146,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
{
|
||||
return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) };
|
||||
}
|
||||
|
||||
Json::Value ToJson(const winrt::hstring& val)
|
||||
{
|
||||
return til::u16u8(val);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -200,11 +162,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isBool();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const bool val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "true | false";
|
||||
@@ -224,11 +181,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isInt();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const int& val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "number";
|
||||
@@ -248,11 +200,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isUInt();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const unsigned int& val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "number (>= 0)";
|
||||
@@ -272,11 +219,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isNumeric();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const float& val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "number";
|
||||
@@ -296,11 +238,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isNumeric();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const double& val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "number";
|
||||
@@ -326,41 +263,16 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return string.length() == 38 && string.front() == '{' && string.back() == '}';
|
||||
}
|
||||
|
||||
Json::Value ToJson(const GUID& val)
|
||||
{
|
||||
return til::u16u8(::Microsoft::Console::Utils::GuidToString(val));
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "guid";
|
||||
}
|
||||
};
|
||||
|
||||
// GUID and winrt::guid are mutually convertible,
|
||||
// but IReference<winrt::guid> throws some of this off
|
||||
// (GUID and winrt::guid are mutually convertible!)
|
||||
template<>
|
||||
struct ConversionTrait<winrt::guid>
|
||||
struct ConversionTrait<winrt::guid> : public ConversionTrait<GUID>
|
||||
{
|
||||
winrt::guid FromJson(const Json::Value& json) const
|
||||
{
|
||||
return static_cast<winrt::guid>(ConversionTrait<GUID>{}.FromJson(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json) const
|
||||
{
|
||||
return ConversionTrait<GUID>{}.CanConvert(json);
|
||||
}
|
||||
|
||||
Json::Value ToJson(const winrt::guid& val)
|
||||
{
|
||||
return ConversionTrait<GUID>{}.ToJson(val);
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return ConversionTrait<GUID>{}.TypeDescription();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -382,43 +294,12 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return (string.length() == 7 || string.length() == 4) && string.front() == '#';
|
||||
}
|
||||
|
||||
Json::Value ToJson(const til::color& val)
|
||||
{
|
||||
return til::u16u8(val.ToHexString(true));
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return "color (#rrggbb, #rgb)";
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WINRT_Windows_UI_H
|
||||
template<>
|
||||
struct ConversionTrait<winrt::Windows::UI::Color>
|
||||
{
|
||||
winrt::Windows::UI::Color FromJson(const Json::Value& json) const
|
||||
{
|
||||
return static_cast<winrt::Windows::UI::Color>(ConversionTrait<til::color>{}.FromJson(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json) const
|
||||
{
|
||||
return ConversionTrait<til::color>{}.CanConvert(json);
|
||||
}
|
||||
|
||||
Json::Value ToJson(const winrt::Windows::UI::Color& val)
|
||||
{
|
||||
return ConversionTrait<til::color>{}.ToJson(val);
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
return ConversionTrait<til::color>{}.TypeDescription();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename T, typename TBase>
|
||||
struct EnumMapper
|
||||
{
|
||||
@@ -446,18 +327,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return json.isString();
|
||||
}
|
||||
|
||||
Json::Value ToJson(const T& val)
|
||||
{
|
||||
for (const auto& pair : TBase::mappings)
|
||||
{
|
||||
if (pair.second == val)
|
||||
{
|
||||
return { pair.first.data() };
|
||||
}
|
||||
}
|
||||
FAIL_FAST();
|
||||
}
|
||||
|
||||
std::string TypeDescription() const
|
||||
{
|
||||
std::vector<std::string_view> names;
|
||||
@@ -515,35 +384,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
return AllClear;
|
||||
}
|
||||
|
||||
Json::Value ToJson(const T& val)
|
||||
{
|
||||
if (val == AllClear)
|
||||
{
|
||||
return BaseEnumMapper::ToJson(AllClear);
|
||||
}
|
||||
else if (val == AllSet)
|
||||
{
|
||||
return BaseEnumMapper::ToJson(AllSet);
|
||||
}
|
||||
else if (WI_IsSingleFlagSet(val))
|
||||
{
|
||||
return BaseEnumMapper::ToJson(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
Json::Value json{ Json::ValueType::arrayValue };
|
||||
for (const auto& pair : TBase::mappings)
|
||||
{
|
||||
if (pair.second != AllClear &&
|
||||
(val & pair.second) == pair.second)
|
||||
{
|
||||
json.append(BaseEnumMapper::ToJson(pair.second));
|
||||
}
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isArray();
|
||||
@@ -691,32 +531,17 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
|
||||
GetValueForKey(json, key1, val1);
|
||||
GetValuesForKeys(json, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// SetValueForKey, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
void SetValueForKey(Json::Value& json, std::string_view key, const T& target, Converter&& conv)
|
||||
{
|
||||
// demand guarantees that it will return a value or throw an exception
|
||||
*json.demand(&*key.cbegin(), (&*key.cbegin()) + key.size()) = conv.ToJson(target);
|
||||
}
|
||||
|
||||
// SetValueForKey, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
void SetValueForKey(Json::Value& json, std::string_view key, const T& target)
|
||||
{
|
||||
SetValueForKey(json, key, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
};
|
||||
|
||||
#define JSON_ENUM_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::Microsoft::Terminal::Settings::Model::JsonUtils::EnumMapper<__VA_ARGS__, ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
#define JSON_ENUM_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_FLAG_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::Microsoft::Terminal::Settings::Model::JsonUtils::FlagMapper<__VA_ARGS__, ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
#define JSON_FLAG_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::FlagMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_MAPPINGS(Count) \
|
||||
static constexpr std::array<pair_type, Count> mappings
|
||||
@@ -1,227 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Jumplist.h"
|
||||
|
||||
#include <ShObjIdl.h>
|
||||
#include <Propkey.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
// This property key isn't already defined in propkey.h, but is used by UWP Jumplist to determine the icon of the jumplist item.
|
||||
// IShellLink's SetIconLocation isn't going to read "ms-appx://" icon paths, so we'll need to use this to set the icon.
|
||||
DEFINE_PROPERTYKEY(PKEY_AppUserModel_DestListLogoUri, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 29);
|
||||
#define INIT_PKEY_AppUserModel_DestListLogoUri \
|
||||
{ \
|
||||
{ 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 }, 29 \
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - This function guesses whether a string is a file path.
|
||||
static constexpr bool _isProbableFilePath(std::wstring_view path)
|
||||
{
|
||||
// "C:X", "C:\X", "\\?", "\\."
|
||||
// _this function rejects \??\ as a path_
|
||||
if (path.size() >= 3)
|
||||
{
|
||||
const auto firstColon{ path.find(L':') };
|
||||
if (firstColon == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto prefix{ path.substr(0, 2) };
|
||||
return prefix == LR"(//)" || prefix == LR"(\\)";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - DestListLogoUri cannot take paths that are separated by / unless they're URLs.
|
||||
// This function uses std::filesystem to normalize strings that appear to be file
|
||||
// paths to have the "correct" slash direction.
|
||||
static std::wstring _normalizeIconPath(std::wstring_view path)
|
||||
{
|
||||
const auto fullPath{ wil::ExpandEnvironmentStringsW<std::wstring>(path.data()) };
|
||||
if (_isProbableFilePath(fullPath))
|
||||
{
|
||||
std::filesystem::path asPath{ fullPath };
|
||||
return asPath.make_preferred().wstring();
|
||||
}
|
||||
return std::wstring{ fullPath };
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for getting the path to the appropriate executable to use
|
||||
// for this instance of the jumplist. For the dev build, it should be `wtd.exe`,
|
||||
// but if we're preview or release, we want to make sure to get the correct
|
||||
// `wt.exe` that corresponds to _us_.
|
||||
// - If we're unpackaged, this needs to get us `WindowsTerminal.exe`, because
|
||||
// the `wt*exe` alias won't have been installed for this install.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the full path to the exe, one of `wt.exe`, `wtd.exe`, or `WindowsTerminal.exe`.
|
||||
static std::wstring_view _getExePath()
|
||||
{
|
||||
static constexpr std::wstring_view WtExe{ L"wt.exe" };
|
||||
static constexpr std::wstring_view WindowsTerminalExe{ L"WindowsTerminal.exe" };
|
||||
static constexpr std::wstring_view WtdExe{ L"wtd.exe" };
|
||||
|
||||
static constexpr std::wstring_view LocalAppDataAppsPath{ L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\" };
|
||||
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static const std::wstring exePath = []() -> std::wstring {
|
||||
// First, check a packaged location for the exe. If we've got a package
|
||||
// family name, that means we're one of the packaged Dev build, packaged
|
||||
// Release build, or packaged Preview build.
|
||||
//
|
||||
// If we're the preview or release build, there's no way of knowing if the
|
||||
// `wt.exe` on the %PATH% is us or not. Fortunately, _our_ execution alias
|
||||
// is located in "%LOCALAPPDATA%\Microsoft\WindowsApps\<our package family
|
||||
// name>", _always_, so we can use that to look up the exe easier.
|
||||
try
|
||||
{
|
||||
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
|
||||
const auto id{ package.Id() };
|
||||
const std::wstring pfn{ id.FamilyName() };
|
||||
const auto isDevPackage{ pfn.rfind(L"WindowsTerminalDev") == 0 };
|
||||
if (!pfn.empty())
|
||||
{
|
||||
const std::filesystem::path windowsAppsPath{ wil::ExpandEnvironmentStringsW<std::wstring>(LocalAppDataAppsPath.data()) };
|
||||
const std::filesystem::path wtPath{ windowsAppsPath / pfn / (isDevPackage ? WtdExe : WtExe) };
|
||||
return wtPath;
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
// If we're here, then we couldn't resolve our exe from the package. This
|
||||
// means we're running unpackaged. We should just use the
|
||||
// WindowsTerminal.exe that's sitting in the directory next to us.
|
||||
try
|
||||
{
|
||||
std::filesystem::path module{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
|
||||
module.replace_filename(WindowsTerminalExe);
|
||||
return module;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
return std::wstring{ WtExe };
|
||||
}();
|
||||
return exePath;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates the items of the Jumplist based on the given settings.
|
||||
// Arguments:
|
||||
// - settings - The settings object to update the jumplist with.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
HRESULT Jumplist::UpdateJumplist(const CascadiaSettings& settings) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
auto jumplistInstance = winrt::create_instance<ICustomDestinationList>(CLSID_DestinationList, CLSCTX_ALL);
|
||||
|
||||
// Start the Jumplist edit transaction
|
||||
uint32_t slots;
|
||||
winrt::com_ptr<IObjectCollection> jumplistItems;
|
||||
jumplistItems.capture(jumplistInstance, &ICustomDestinationList::BeginList, &slots);
|
||||
|
||||
// It's easier to clear the list and re-add everything. The settings aren't
|
||||
// updated often, and there likely isn't a huge amount of items to add.
|
||||
RETURN_IF_FAILED(jumplistItems->Clear());
|
||||
|
||||
// Update the list of profiles.
|
||||
RETURN_IF_FAILED(_updateProfiles(jumplistItems.get(), settings.Profiles().GetView()));
|
||||
|
||||
// TODO GH#1571: Add items from the future customizable new tab dropdown as well.
|
||||
// This could either replace the default profiles, or be added alongside them.
|
||||
|
||||
// Add the items to the jumplist Task section.
|
||||
// The Tasks section is immutable by the user, unlike the destinations
|
||||
// section that can have its items pinned and removed.
|
||||
RETURN_IF_FAILED(jumplistInstance->AddUserTasks(jumplistItems.get()));
|
||||
|
||||
RETURN_IF_FAILED(jumplistInstance->CommitList());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates and adds a ShellLink object to the Jumplist for each profile.
|
||||
// Arguments:
|
||||
// - jumplistItems - The jumplist item list
|
||||
// - profiles - The profiles to add to the jumplist
|
||||
// Return Value:
|
||||
// - S_OK or HRESULT failure code.
|
||||
[[nodiscard]] HRESULT Jumplist::_updateProfiles(IObjectCollection* jumplistItems, winrt::Windows::Foundation::Collections::IVectorView<Profile> profiles) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
for (const auto& profile : profiles)
|
||||
{
|
||||
// Craft the arguments following "wt.exe"
|
||||
auto args = fmt::format(L"-p {}", to_hstring(profile.Guid()));
|
||||
|
||||
// Create the shell link object for the profile
|
||||
winrt::com_ptr<IShellLinkW> shLink;
|
||||
const auto normalizedIconPath{ _normalizeIconPath(profile.Icon()) };
|
||||
RETURN_IF_FAILED(_createShellLink(profile.Name(), normalizedIconPath, args, shLink.put()));
|
||||
|
||||
RETURN_IF_FAILED(jumplistItems->AddObject(shLink.get()));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a ShellLink object. Each item in a jumplist is a ShellLink, which is sort of
|
||||
// like a shortcut. It requires the path to the application (wt.exe), the arguments to pass,
|
||||
// and the path to the icon for the jumplist item. The path to the application isn't passed
|
||||
// into this function, as we'll determine it with _getExePath
|
||||
// Arguments:
|
||||
// - name: The name of the item displayed in the jumplist.
|
||||
// - path: The path to the icon for the jumplist item.
|
||||
// - args: The arguments to pass along with wt.exe
|
||||
// - shLink: The shell link object to return.
|
||||
// Return Value:
|
||||
// - S_OK or HRESULT failure code.
|
||||
[[nodiscard]] HRESULT Jumplist::_createShellLink(const std::wstring_view name,
|
||||
const std::wstring_view path,
|
||||
const std::wstring_view args,
|
||||
IShellLinkW** shLink) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
auto sh = winrt::create_instance<IShellLinkW>(CLSID_ShellLink, CLSCTX_ALL);
|
||||
|
||||
const auto module{ _getExePath() };
|
||||
RETURN_IF_FAILED(sh->SetPath(module.data()));
|
||||
RETURN_IF_FAILED(sh->SetArguments(args.data()));
|
||||
|
||||
PROPVARIANT titleProp;
|
||||
titleProp.vt = VT_LPWSTR;
|
||||
titleProp.pwszVal = const_cast<wchar_t*>(name.data());
|
||||
|
||||
PROPVARIANT iconProp;
|
||||
iconProp.vt = VT_LPWSTR;
|
||||
iconProp.pwszVal = const_cast<wchar_t*>(path.data());
|
||||
|
||||
auto propStore{ sh.as<IPropertyStore>() };
|
||||
RETURN_IF_FAILED(propStore->SetValue(PKEY_Title, titleProp));
|
||||
RETURN_IF_FAILED(propStore->SetValue(PKEY_AppUserModel_DestListLogoUri, iconProp));
|
||||
|
||||
RETURN_IF_FAILED(propStore->Commit());
|
||||
|
||||
*shLink = sh.detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
}
|
||||