mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 06:09:50 +00:00
Compare commits
81 Commits
dev/pabhoj
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d41b4fdc1 | ||
|
|
1b362864a5 | ||
|
|
45b1cde0bc | ||
|
|
03378690d8 | ||
|
|
e7a79d9c9e | ||
|
|
784e4ec93a | ||
|
|
d2b77509f1 | ||
|
|
67662e1a6a | ||
|
|
5926cc29e2 | ||
|
|
2f58711ee6 | ||
|
|
2c6cdc281f | ||
|
|
a17f18a920 | ||
|
|
67f6b29d63 | ||
|
|
9ae68cca1e | ||
|
|
bc79867b38 | ||
|
|
56016c92f7 | ||
|
|
dc161d8018 | ||
|
|
ae9532fb41 | ||
|
|
574a72b505 | ||
|
|
5f3222b993 | ||
|
|
91bf709ca6 | ||
|
|
7abed9f6fb | ||
|
|
b4a52c847f | ||
|
|
3f21996589 | ||
|
|
fd74d59c6c | ||
|
|
04478d1df0 | ||
|
|
589286a357 | ||
|
|
d74b66a0a7 | ||
|
|
32379c29f0 | ||
|
|
cd2166aedf | ||
|
|
66f4f9d9ea | ||
|
|
bbc570d107 | ||
|
|
238b5c2e22 | ||
|
|
55dd4af9cb | ||
|
|
2970017243 | ||
|
|
1faf67fba3 | ||
|
|
024b9fc0f4 | ||
|
|
aa4e9f5414 | ||
|
|
76b00e3b31 | ||
|
|
07d58a800c | ||
|
|
16028dee8b | ||
|
|
c6b67aad4b | ||
|
|
bd403dc8e7 | ||
|
|
f025c53dba | ||
|
|
66ecb0bd63 | ||
|
|
95a19624a4 | ||
|
|
86dfefa690 | ||
|
|
478c2c3613 | ||
|
|
a579566a1d | ||
|
|
f1b6b17c58 | ||
|
|
7b8a53c030 | ||
|
|
d6a9e9ffb6 | ||
|
|
eb1d6b599e | ||
|
|
2cd2351deb | ||
|
|
843e61aa8a | ||
|
|
f785168aac | ||
|
|
8fba292bd7 | ||
|
|
9e8427d81e | ||
|
|
059b53bf94 | ||
|
|
4d48f305a1 | ||
|
|
06e2317012 | ||
|
|
4071df73ba | ||
|
|
8962f88f90 | ||
|
|
72a4e936cc | ||
|
|
1ac7fe16ae | ||
|
|
ff7a632f0f | ||
|
|
24a53d4968 | ||
|
|
0b97c7b5ca | ||
|
|
6436e712e7 | ||
|
|
08c2f350e6 | ||
|
|
a5fb91dd4c | ||
|
|
848314ef17 | ||
|
|
deffbbc7f5 | ||
|
|
f12ee745ef | ||
|
|
9eb191d545 | ||
|
|
bfd910c4ca | ||
|
|
715844b01c | ||
|
|
538285cc45 | ||
|
|
a59e3d0c57 | ||
|
|
f73da456fa | ||
|
|
9ccd6ecd74 |
15
.github/actions/spelling/README.md
vendored
Normal file
15
.github/actions/spelling/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# check-spelling/check-spelling configuration
|
||||
|
||||
File | Purpose | Format | Info
|
||||
-|-|-|-
|
||||
[allow/*.txt](allow/) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
|
||||
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
|
||||
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
|
||||
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
|
||||
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
|
||||
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
|
||||
|
||||
Note: you can replace any of these files with a directory by the same name (minus the suffix)
|
||||
and then include multiple files inside that directory (with that suffix) to merge multiple files together.
|
||||
38
.github/actions/spelling/advice.md
vendored
38
.github/actions/spelling/advice.md
vendored
@@ -1,4 +1,4 @@
|
||||
<!-- markdownlint-disable MD033 MD041 -->
|
||||
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
|
||||
|
||||
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
|
||||
:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files.
|
||||
|
||||
If the listed items are:
|
||||
|
||||
@@ -20,31 +20,29 @@ See the `README.md` in each directory for more information.
|
||||
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
<details><summary>:clamp: If you see a bunch of garbage</summary>
|
||||
|
||||
If it relates to a ...
|
||||
<details><summary>well-formed pattern</summary>
|
||||
<details><summary>If the flagged items are :exploding_head: false positives</summary>
|
||||
|
||||
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
|
||||
If items relate to a ...
|
||||
* binary file (or some other file you wouldn't want to check at all).
|
||||
|
||||
If not, try writing one and adding it to a `patterns/{file}.txt`.
|
||||
Please add a file path to the `excludes.txt` file matching the containing file.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
</details>
|
||||
<details><summary>binary-ish string</summary>
|
||||
|
||||
Please add a file path to the `excludes.txt` file instead of just accepting the garbage.
|
||||
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
|
||||
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
../tree/HEAD/README.md) (on whichever branch you're using).
|
||||
</details>
|
||||
|
||||
|
||||
* well-formed pattern.
|
||||
|
||||
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
|
||||
try adding it to the `patterns.txt` file.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
20
.github/actions/spelling/allow/allow.txt
vendored
20
.github/actions/spelling/allow/allow.txt
vendored
@@ -1,20 +1,21 @@
|
||||
admins
|
||||
apc
|
||||
allcolors
|
||||
Apc
|
||||
bsd
|
||||
apc
|
||||
breadcrumb
|
||||
breadcrumbs
|
||||
bsd
|
||||
calt
|
||||
CMMI
|
||||
ccmp
|
||||
changelog
|
||||
clickable
|
||||
clig
|
||||
CMMI
|
||||
copyable
|
||||
cybersecurity
|
||||
dalet
|
||||
dcs
|
||||
Dcs
|
||||
dcs
|
||||
dialytika
|
||||
dje
|
||||
downside
|
||||
@@ -33,10 +34,12 @@ gantt
|
||||
gcc
|
||||
geeksforgeeks
|
||||
ghe
|
||||
github
|
||||
gje
|
||||
godbolt
|
||||
hostname
|
||||
hostnames
|
||||
https
|
||||
hyperlink
|
||||
hyperlinking
|
||||
hyperlinks
|
||||
@@ -53,13 +56,14 @@ Llast
|
||||
llvm
|
||||
Lmid
|
||||
locl
|
||||
lol
|
||||
lorem
|
||||
Lorigin
|
||||
maxed
|
||||
minimalistic
|
||||
mkmk
|
||||
mnt
|
||||
mru
|
||||
noreply
|
||||
nje
|
||||
noreply
|
||||
ogonek
|
||||
@@ -80,13 +84,16 @@ runtimes
|
||||
shcha
|
||||
slnt
|
||||
Sos
|
||||
ssh
|
||||
timeline
|
||||
timelines
|
||||
timestamped
|
||||
TLDR
|
||||
tokenizes
|
||||
tonos
|
||||
toolset
|
||||
tshe
|
||||
ubuntu
|
||||
uiatextrange
|
||||
UIs
|
||||
und
|
||||
@@ -95,6 +102,7 @@ versioned
|
||||
vsdevcmd
|
||||
We'd
|
||||
wildcards
|
||||
XBox
|
||||
YBox
|
||||
yeru
|
||||
zhe
|
||||
allcolors
|
||||
|
||||
22
.github/actions/spelling/allow/apis.txt
vendored
22
.github/actions/spelling/allow/apis.txt
vendored
@@ -5,6 +5,7 @@ aclapi
|
||||
alignas
|
||||
alignof
|
||||
APPLYTOSUBMENUS
|
||||
appxrecipe
|
||||
bitfield
|
||||
bitfields
|
||||
BUILDBRANCH
|
||||
@@ -14,6 +15,7 @@ BYCOMMAND
|
||||
BYPOSITION
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
CLOSEAPP
|
||||
cmdletbinding
|
||||
COLORPROPERTY
|
||||
colspan
|
||||
@@ -28,9 +30,14 @@ dataobject
|
||||
dcomp
|
||||
DERR
|
||||
dlldata
|
||||
DNE
|
||||
DONTADDTORECENT
|
||||
DWMSBT
|
||||
DWMWA
|
||||
DWMWA
|
||||
DWORDLONG
|
||||
endfor
|
||||
ENDSESSION
|
||||
enumset
|
||||
environstrings
|
||||
EXPCMDFLAGS
|
||||
@@ -70,6 +77,7 @@ IDirect
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
IGraphics
|
||||
IInheritable
|
||||
IMap
|
||||
IMonarch
|
||||
@@ -84,6 +92,7 @@ istream
|
||||
IStringable
|
||||
ITab
|
||||
ITaskbar
|
||||
itow
|
||||
IUri
|
||||
IVirtual
|
||||
KEYSELECT
|
||||
@@ -95,12 +104,15 @@ lround
|
||||
Lsa
|
||||
lsass
|
||||
LSHIFT
|
||||
LTGRAY
|
||||
MAINWINDOW
|
||||
memchr
|
||||
memicmp
|
||||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUITEMINFOW
|
||||
MENUINFO
|
||||
MENUITEMINFOW
|
||||
mmeapi
|
||||
MOUSELEAVE
|
||||
mov
|
||||
mptt
|
||||
@@ -136,16 +148,18 @@ OUTLINETEXTMETRICW
|
||||
overridable
|
||||
PACL
|
||||
PAGESCROLL
|
||||
PATINVERT
|
||||
PEXPLICIT
|
||||
PICKFOLDERS
|
||||
pmr
|
||||
ptstr
|
||||
QUERYENDSESSION
|
||||
rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
ROOTOWNER
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
@@ -175,6 +189,8 @@ Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
syscall
|
||||
SYSTEMBACKDROP
|
||||
TABROW
|
||||
TASKBARCREATED
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
@@ -194,6 +210,8 @@ UOI
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
USEROBJECTFLAGS
|
||||
Viewbox
|
||||
virtualalloc
|
||||
wcsstr
|
||||
wcstoui
|
||||
winmain
|
||||
|
||||
2
.github/actions/spelling/allow/names.txt
vendored
2
.github/actions/spelling/allow/names.txt
vendored
@@ -69,6 +69,8 @@ Rincewind
|
||||
rprichard
|
||||
Schoonover
|
||||
shadertoy
|
||||
Shomnipotence
|
||||
simioni
|
||||
Somuah
|
||||
sonph
|
||||
sonpham
|
||||
|
||||
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
523
.github/actions/spelling/candidate.patterns
vendored
Normal file
@@ -0,0 +1,523 @@
|
||||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# patch hunk comments
|
||||
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
|
||||
# git index header
|
||||
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# cid urls
|
||||
(['"])cid:.*?\g{-1}
|
||||
|
||||
# data url in parens
|
||||
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
|
||||
# data url in quotes
|
||||
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
|
||||
# data url
|
||||
data:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# magnet urls
|
||||
magnet:[?=:\w]+
|
||||
|
||||
# magnet urls
|
||||
"magnet:[^"]+"
|
||||
|
||||
# obs:
|
||||
"obs:[^"]*"
|
||||
|
||||
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
|
||||
# In this examples content, I'm using a number of different ways to match things to show various approaches
|
||||
# asciinema
|
||||
\basciinema\.org/a/[0-9a-zA-Z]+
|
||||
|
||||
# apple
|
||||
\bdeveloper\.apple\.com/[-\w?=/]+
|
||||
# Apple music
|
||||
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
|
||||
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
# appveyor project
|
||||
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
|
||||
|
||||
# Amazon
|
||||
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
# AWS S3
|
||||
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS execute-api
|
||||
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
|
||||
# AWS ELB
|
||||
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
|
||||
# AWS SNS
|
||||
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
# YouTube music
|
||||
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
|
||||
# YouTube tag
|
||||
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
|
||||
# YouTube image
|
||||
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
|
||||
# Google Accounts
|
||||
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
|
||||
# Google Analytics
|
||||
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
|
||||
# Google APIs
|
||||
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
|
||||
# Google Storage
|
||||
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
|
||||
# Google Calendar
|
||||
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
|
||||
\w+\@group\.calendar\.google\.com\b
|
||||
# Google DataStudio
|
||||
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
|
||||
# The leading `/` here is as opposed to the `\b` above
|
||||
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
|
||||
# Google Docs
|
||||
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
|
||||
# Google Drive
|
||||
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
|
||||
# Google Groups
|
||||
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
# Google themes
|
||||
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
# Google CDN
|
||||
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
|
||||
# Goo.gl
|
||||
/goo\.gl/[a-zA-Z0-9]+
|
||||
# Google Chrome Store
|
||||
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
|
||||
# Google Books
|
||||
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
|
||||
# Google Fonts
|
||||
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
|
||||
# Google Forms
|
||||
\bforms\.gle/\w+
|
||||
# Google Scholar
|
||||
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
|
||||
# Google Colab Research Drive
|
||||
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
|
||||
|
||||
# GitHub SHAs (api)
|
||||
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
# GitHub SHAs
|
||||
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
|
||||
# GitHub wiki
|
||||
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
# githubassets
|
||||
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
# git.io
|
||||
\bgit\.io/[0-9a-zA-Z]+
|
||||
# GitHub JSON
|
||||
"node_id": "[-a-zA-Z=;:/0-9+]*"
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
# GHSA
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
# GitLab merge requests
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
|
||||
# GitLab uploads
|
||||
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
|
||||
# GitLab commits
|
||||
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
|
||||
|
||||
# binanace
|
||||
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
|
||||
# bitbucket diff
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
|
||||
# bitbucket repositories commits
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
# bitbucket commits
|
||||
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
|
||||
# bit.ly
|
||||
\bbit\.ly/\w+
|
||||
|
||||
# bitrise
|
||||
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
|
||||
|
||||
# bootstrapcdn.com
|
||||
\bbootstrapcdn\.com/[-./\w]+
|
||||
|
||||
# cdn.cloudflare.com
|
||||
\bcdnjs\.cloudflare\.com/[./\w]+
|
||||
|
||||
# circleci
|
||||
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
|
||||
|
||||
# gitter
|
||||
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
|
||||
|
||||
# gravatar
|
||||
\bgravatar\.com/avatar/[0-9a-f]+
|
||||
|
||||
# ibm
|
||||
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
|
||||
|
||||
# imgur
|
||||
\bimgur\.com/[^.]+
|
||||
|
||||
# Internet Archive
|
||||
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
|
||||
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# Disqus
|
||||
\bdisqus\.com/[-\w/%.()!?&=_]*
|
||||
|
||||
# medium link
|
||||
\blink\.medium\.com/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/\@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
|
||||
# microsoft store
|
||||
\bmicrosoft\.com/store/apps/\w+
|
||||
|
||||
# mvnrepository.com
|
||||
\bmvnrepository\.com/[-0-9a-z./]+
|
||||
|
||||
# now.sh
|
||||
/[0-9a-z-.]+\.now\.sh\b
|
||||
|
||||
# oracle
|
||||
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
|
||||
|
||||
# chromatic.com
|
||||
/\S+.chromatic.com\S*[")]
|
||||
|
||||
# codacy
|
||||
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
|
||||
|
||||
# compai
|
||||
\bcompai\.pub/v1/png/[0-9a-f]+
|
||||
|
||||
# mailgun api
|
||||
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
|
||||
# mailgun
|
||||
\b[0-9a-z]+.mailgun.org
|
||||
|
||||
# /message-id/
|
||||
/message-id/[-\w@./%]+
|
||||
|
||||
# Reddit
|
||||
\breddit\.com/r/[/\w_]*
|
||||
|
||||
# requestb.in
|
||||
\brequestb\.in/[0-9a-z]+
|
||||
|
||||
# sched
|
||||
\b[a-z0-9]+\.sched\.com\b
|
||||
|
||||
# Slack url
|
||||
slack://[a-zA-Z0-9?&=]+
|
||||
# Slack
|
||||
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
|
||||
# Slack edge
|
||||
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
|
||||
# Slack images
|
||||
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
|
||||
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# Sentry
|
||||
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
|
||||
|
||||
# Twitter markdown
|
||||
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
|
||||
# Twitter hashtag
|
||||
\btwitter\.com/hashtag/[\w?_=&]*
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
# Twitter profile images
|
||||
\btwimg\.com/profile_images/[_\w./]*
|
||||
# Twitter media
|
||||
\btwimg\.com/media/[-_\w./?=]*
|
||||
# Twitter link shortened
|
||||
\bt\.co/\w+
|
||||
|
||||
# facebook
|
||||
\bfburl\.com/[0-9a-z_]+
|
||||
# facebook CDN
|
||||
\bfbcdn\.net/[\w/.,]*
|
||||
# facebook watch
|
||||
\bfb\.watch/[0-9A-Za-z]+
|
||||
|
||||
# dropbox
|
||||
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
|
||||
|
||||
# ipfs protocol
|
||||
ipfs://[0-9a-z]*
|
||||
# ipfs url
|
||||
/ipfs/[0-9a-z]*
|
||||
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# loom
|
||||
\bloom\.com/embed/[0-9a-f]+
|
||||
|
||||
# regex101
|
||||
\bregex101\.com/r/[^/\s"]+/\d+
|
||||
|
||||
# figma
|
||||
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
|
||||
|
||||
# freecodecamp.org
|
||||
\bfreecodecamp\.org/[-\w/.]+
|
||||
|
||||
# image.tmdb.org
|
||||
\bimage\.tmdb\.org/[/\w.]+
|
||||
|
||||
# mermaid
|
||||
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
|
||||
|
||||
# Wikipedia
|
||||
\ben\.wikipedia\.org/wiki/[-\w%.#]+
|
||||
|
||||
# gitweb
|
||||
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
|
||||
|
||||
# HyperKitty lists
|
||||
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
|
||||
|
||||
# lists
|
||||
/thread\.html/[^"\s]+
|
||||
|
||||
# list-management
|
||||
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
|
||||
|
||||
# kubectl.kubernetes.io/last-applied-configuration
|
||||
"kubectl.kubernetes.io/last-applied-configuration": ".*"
|
||||
|
||||
# pgp
|
||||
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
|
||||
|
||||
# Spotify
|
||||
\bopen\.spotify\.com/embed/playlist/\w+
|
||||
|
||||
# Mastodon
|
||||
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
|
||||
|
||||
# scastie
|
||||
\bscastie\.scala-lang\.org/[^/]+/\w+
|
||||
|
||||
# images.unsplash.com
|
||||
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
|
||||
|
||||
# pastebin
|
||||
\bpastebin\.com/[\w/]+
|
||||
|
||||
# heroku
|
||||
\b\w+\.heroku\.com/source/archive/\w+
|
||||
|
||||
# quip
|
||||
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
|
||||
|
||||
# badgen.net
|
||||
\bbadgen\.net/badge/[^")\]'\s]+
|
||||
|
||||
# statuspage.io
|
||||
\w+\.statuspage\.io\b
|
||||
|
||||
# media.giphy.com
|
||||
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
|
||||
|
||||
# tinyurl
|
||||
\btinyurl\.com/\w+
|
||||
|
||||
# getopts
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
# IPv6
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
|
||||
|
||||
# PGP
|
||||
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
|
||||
# GPG keys
|
||||
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
|
||||
# Well known gpg keys
|
||||
.well-known/openpgpkey/[\w./]+
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
# integrity
|
||||
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
|
||||
|
||||
# https://www.gnu.org/software/groff/manual/groff.html
|
||||
# man troff content
|
||||
\\f[BCIPR]
|
||||
# '
|
||||
\\\(aq
|
||||
|
||||
# .desktop mime types
|
||||
^MimeTypes?=.*$
|
||||
# .desktop localized entries
|
||||
^[A-Z][a-z]+\[[a-z]+\]=.*$
|
||||
# Localized .desktop content
|
||||
Name\[[^\]]+\]=.*
|
||||
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# crypt
|
||||
"\$2[ayb]\$.{56}"
|
||||
|
||||
# scrypt / argon
|
||||
\$(?:scrypt|argon\d+[di]*)\$\S+
|
||||
|
||||
# Input to GitHub JSON
|
||||
content: "[-a-zA-Z=;:/0-9+]*="
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.*/[gim]*\.test\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]*/[gim]*\s*
|
||||
# javascript match regex
|
||||
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
|
||||
# javascript regex
|
||||
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
# javascript replace regex
|
||||
\.replace\(/[^/\s"]*/[gim]*\s*,
|
||||
|
||||
# Go regular expressions
|
||||
regexp?\.MustCompile\(`[^`]*`\)
|
||||
|
||||
# sed regular expressions
|
||||
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
|
||||
|
||||
# go install
|
||||
go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
|
||||
# kubectl - pods in CrashLoopBackOff
|
||||
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
|
||||
|
||||
# kubernetes object suffix
|
||||
-[0-9a-f]{10}-\w{5}\s
|
||||
|
||||
# posthog secrets
|
||||
posthog\.init\((['"])phc_[^"',]+\g{-1},
|
||||
|
||||
# xcode
|
||||
|
||||
# xcodeproject scenes
|
||||
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
|
||||
|
||||
# xcode api botches
|
||||
customObjectInstantitationMethod
|
||||
|
||||
# font awesome classes
|
||||
\.fa-[-a-z0-9]+
|
||||
|
||||
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
|
||||
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
|
||||
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
|
||||
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
|
||||
## You could manually change `(?i)X...` to use `[Xx]...`
|
||||
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
|
||||
# Lorem
|
||||
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
|
||||
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
|
||||
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
|
||||
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
# Note that the next example is no longer necessary if you are using
|
||||
# to match a string starting with a `#`, use a character-class:
|
||||
[#]backwards
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
# Compiler flags (Scala)
|
||||
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags
|
||||
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
|
||||
44
.github/actions/spelling/excludes.txt
vendored
44
.github/actions/spelling/excludes.txt
vendored
@@ -1,28 +1,39 @@
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
|
||||
(?:(?i)\.png$)
|
||||
(?:^|/)(?i)COPYRIGHT
|
||||
(?:^|/)(?i)LICEN[CS]E
|
||||
(?:^|/)3rdparty/
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package-lock\.json$
|
||||
(?:^|/)package(?:-lock|)\.json$
|
||||
(?:^|/)sources(?:|\.dep)$
|
||||
SUMS$
|
||||
(?:^|/)vendor/
|
||||
\.a$
|
||||
\.ai$
|
||||
\.avi$
|
||||
\.bmp$
|
||||
\.bz2$
|
||||
\.cer$
|
||||
\.class$
|
||||
\.crl$
|
||||
\.crt$
|
||||
\.csr$
|
||||
\.dll$
|
||||
\.docx?$
|
||||
\.drawio$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.eps$
|
||||
\.exe$
|
||||
\.gif$
|
||||
\.gitattributes$
|
||||
\.graffle$
|
||||
\.gz$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jks$
|
||||
\.jpeg$
|
||||
\.jpg$
|
||||
\.key$
|
||||
@@ -30,28 +41,52 @@ SUMS$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mod$
|
||||
\.mp3$
|
||||
\.mp4$
|
||||
\.o$
|
||||
\.ocf$
|
||||
\.otf$
|
||||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.pyc$
|
||||
\.runsettings$
|
||||
\.s$
|
||||
\.sig$
|
||||
\.so$
|
||||
\.svg$
|
||||
\.svgz$
|
||||
\.svgz?$
|
||||
\.tar$
|
||||
\.tgz$
|
||||
\.tiff?$
|
||||
\.ttf$
|
||||
\.vsdx$
|
||||
\.wav$
|
||||
\.webm$
|
||||
\.webp$
|
||||
\.woff
|
||||
\.woff2?$
|
||||
\.xcf$
|
||||
\.xls
|
||||
\.xlsx?$
|
||||
\.xpm$
|
||||
\.yml$
|
||||
\.zip$
|
||||
^\.github/actions/spelling/
|
||||
^\.github/fabricbot.json$
|
||||
^\.gitignore$
|
||||
^\Q.git-blame-ignore-revs\E$
|
||||
^\Q.github/workflows/spelling.yml\E$
|
||||
^\Qdoc/reference/windows-terminal-logo.ans\E$
|
||||
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
|
||||
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
|
||||
^\Qsrc/host/ft_host/chafa.txt\E$
|
||||
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
|
||||
^\XamlStyler.json$
|
||||
^build/config/
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
@@ -78,6 +113,5 @@ SUMS$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^tools/ReleaseEngineering/ServicingPipeline.ps1$
|
||||
^\.github/actions/spelling/
|
||||
^\.gitignore$
|
||||
^\XamlStyler.json$
|
||||
ignore$
|
||||
SUMS$
|
||||
|
||||
8
.github/actions/spelling/expect/alphabet.txt
vendored
8
.github/actions/spelling/expect/alphabet.txt
vendored
@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
ABCDEFG
|
||||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCG
|
||||
ABE
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
CCE
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
@@ -33,7 +26,6 @@ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
|
||||
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
|
||||
qrstuvwxyz
|
||||
qwerty
|
||||
QWERTYUIOP
|
||||
qwertyuiopasdfg
|
||||
YYYYYYYDDDDDDDDDDD
|
||||
ZAAZZ
|
||||
|
||||
881
.github/actions/spelling/expect/expect.txt
vendored
881
.github/actions/spelling/expect/expect.txt
vendored
File diff suppressed because it is too large
Load Diff
23
.github/actions/spelling/expect/web.txt
vendored
23
.github/actions/spelling/expect/web.txt
vendored
@@ -1,29 +1,6 @@
|
||||
http
|
||||
www
|
||||
easyrgb
|
||||
php
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
freedesktop
|
||||
ycombinator
|
||||
robertelder
|
||||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
winui
|
||||
appshellintegration
|
||||
mdtauk
|
||||
cppreference
|
||||
gfycat
|
||||
Guake
|
||||
azurewebsites
|
||||
askubuntu
|
||||
dostips
|
||||
viewtopic
|
||||
rosettacode
|
||||
Rexx
|
||||
tldp
|
||||
HOWTO
|
||||
uwspace
|
||||
uwaterloo
|
||||
|
||||
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
|
||||
# \bm_data\b
|
||||
|
||||
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
|
||||
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
|
||||
# to use this:
|
||||
#\bfit\(
|
||||
|
||||
# s.b. GitHub
|
||||
\bGithub\b
|
||||
|
||||
# s.b. GitLab
|
||||
\bGitlab\b
|
||||
|
||||
# s.b. JavaScript
|
||||
\bJavascript\b
|
||||
|
||||
# s.b. Microsoft
|
||||
\bMicroSoft\b
|
||||
|
||||
# s.b. another
|
||||
\ban[- ]other\b
|
||||
|
||||
# s.b. greater than
|
||||
\bgreater then\b
|
||||
|
||||
# s.b. into
|
||||
#\sin to\s
|
||||
|
||||
# s.b. opt-in
|
||||
\sopt in\s
|
||||
|
||||
# s.b. less than
|
||||
\bless then\b
|
||||
|
||||
# s.b. otherwise
|
||||
\bother[- ]wise\b
|
||||
|
||||
# s.b. nonexistent
|
||||
\bnon existing\b
|
||||
\b[Nn]o[nt][- ]existent\b
|
||||
|
||||
# s.b. preexisting
|
||||
[Pp]re[- ]existing
|
||||
|
||||
# s.b. preempt
|
||||
[Pp]re[- ]empt\b
|
||||
|
||||
# s.b. preemptively
|
||||
[Pp]re[- ]emptively
|
||||
|
||||
# s.b. reentrancy
|
||||
[Rr]e[- ]entrancy
|
||||
|
||||
# s.b. reentrant
|
||||
[Rr]e[- ]entrant
|
||||
|
||||
# s.b. workaround(s)
|
||||
#\bwork[- ]arounds?\b
|
||||
|
||||
# Reject duplicate words
|
||||
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s
|
||||
82
.github/actions/spelling/patterns/patterns.txt
vendored
82
.github/actions/spelling/patterns/patterns.txt
vendored
@@ -1,11 +1,6 @@
|
||||
https://(?:(?:[-a-zA-Z0-9?&=]*\.|)microsoft\.com)/[-a-zA-Z0-9?&=_#\/.]*
|
||||
https://aka\.ms/[-a-zA-Z0-9?&=\/_]*
|
||||
https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
|
||||
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
|
||||
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
https://(?:[a-z-]+\.|)github(?:usercontent|)\.com/[-a-zA-Z0-9?%&=_\/.+]*
|
||||
https://www.xfree86.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
|
||||
|
||||
https?://\S+
|
||||
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
|
||||
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
|
||||
(?:0[Xx]|\\x|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
|
||||
@@ -28,3 +23,74 @@ vcvars\w*
|
||||
ROY\sG\.\sBIV
|
||||
!(?:(?i)ESC)!\[
|
||||
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'
|
||||
|
||||
# Automatically suggested patterns
|
||||
# hit-count: 3831 file-count: 582
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# hit-count: 71 file-count: 35
|
||||
# Compiler flags
|
||||
(?:^|[\t ,"'`=(])-[D](?=[A-Z]{2,}|[A-Z][a-z])
|
||||
(?:^|[\t ,"'`=(])-[X](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# hit-count: 41 file-count: 28
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
|
||||
# hit-count: 20 file-count: 9
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
|
||||
# hit-count: 10 file-count: 7
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
|
||||
# hit-count: 4 file-count: 4
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# hit-count: 4 file-count: 1
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# acceptable duplicates
|
||||
# ls directory listings
|
||||
[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+
|
||||
# C/idl types + English ...
|
||||
\s(Guid|long|LONG|that) \g{-1}\s
|
||||
|
||||
# javadoc / .net
|
||||
(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# Commit message -- Signed-off-by and friends
|
||||
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$
|
||||
|
||||
# Autogenerated revert commit message
|
||||
^This reverts commit [0-9a-f]{40}\.$
|
||||
|
||||
# vtmode
|
||||
--vtmode\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
|
||||
28
.github/actions/spelling/reject.txt
vendored
28
.github/actions/spelling/reject.txt
vendored
@@ -1,22 +1,12 @@
|
||||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
^spae$
|
||||
^spaebook$
|
||||
^spaecraft$
|
||||
^spaed$
|
||||
^spaedom$
|
||||
^spaeing$
|
||||
^spaeings$
|
||||
^spae-man$
|
||||
^spaeman$
|
||||
^spaer$
|
||||
^Spaerobee$
|
||||
^spaes$
|
||||
^spaewife$
|
||||
^spaewoman$
|
||||
^spaework$
|
||||
^spaewright$
|
||||
^wether$
|
||||
^wethers$
|
||||
^wetherteg$
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^oer$
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^untill$
|
||||
^untilling$
|
||||
^wether.*
|
||||
|
||||
3156
.github/fabricbot.json
vendored
Normal file
3156
.github/fabricbot.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
132
.github/workflows/spelling2.yml
vendored
132
.github/workflows/spelling2.yml
vendored
@@ -1,20 +1,134 @@
|
||||
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
|
||||
name: Spell checking
|
||||
|
||||
# Comment management is handled through a secondary job, for details see:
|
||||
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
|
||||
#
|
||||
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare)
|
||||
# it needs `contents: write` in order to add a comment.
|
||||
#
|
||||
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
|
||||
# it needs `pull-requests: write` in order to manipulate those comments.
|
||||
|
||||
# Updating pull request branches is managed via comment handling.
|
||||
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
|
||||
#
|
||||
# These elements work together to make it happen:
|
||||
#
|
||||
# `on.issue_comment`
|
||||
# This event listens to comments by users asking to update the metadata.
|
||||
#
|
||||
# `jobs.update`
|
||||
# This job runs in response to an issue_comment and will push a new commit
|
||||
# to update the spelling metadata.
|
||||
#
|
||||
# `with.experimental_apply_changes_via_bot`
|
||||
# Tells the action to support and generate messages that enable it
|
||||
# to make a commit to update the spelling metadata.
|
||||
#
|
||||
# `with.ssh_key`
|
||||
# In order to trigger workflows when the commit is made, you can provide a
|
||||
# secret (typically, a write-enabled github deploy key).
|
||||
#
|
||||
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- "**"
|
||||
tags-ignore:
|
||||
- "**"
|
||||
types:
|
||||
- 'opened'
|
||||
- 'reopened'
|
||||
- 'synchronize'
|
||||
issue_comment:
|
||||
types:
|
||||
- 'created'
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Spell checking
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
actions: read
|
||||
outputs:
|
||||
followup: ${{ steps.spelling.outputs.followup }}
|
||||
runs-on: ubuntu-latest
|
||||
if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'"
|
||||
concurrency:
|
||||
group: spelling-${{ github.event.pull_request.number || github.ref }}
|
||||
# note: If you use only_check_changed_files, you do not want cancel-in-progress
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: checkout-merge
|
||||
if: "contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
ref: refs/pull/${{github.event.pull_request.number}}/merge
|
||||
- name: checkout
|
||||
if: "!contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2
|
||||
- uses: check-spelling/check-spelling@v0.0.19
|
||||
suppress_push_for_open_pull_request: 1
|
||||
checkout: true
|
||||
check_file_names: 1
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
post_comment: 0
|
||||
use_magic_file: 1
|
||||
extra_dictionary_limit: 10
|
||||
extra_dictionaries:
|
||||
cspell:software-terms/src/software-terms.txt
|
||||
cspell:python/src/python/python-lib.txt
|
||||
cspell:node/node.txt
|
||||
cspell:cpp/src/stdlib-c.txt
|
||||
cspell:cpp/src/stdlib-cpp.txt
|
||||
cspell:fullstack/fullstack.txt
|
||||
cspell:filetypes/filetypes.txt
|
||||
cspell:html/html.txt
|
||||
cspell:cpp/src/compiler-msvc.txt
|
||||
cspell:python/src/common/extra.txt
|
||||
cspell:powershell/powershell.txt
|
||||
cspell:aws/aws.txt
|
||||
cspell:cpp/src/lang-keywords.txt
|
||||
cspell:npm/npm.txt
|
||||
cspell:dotnet/dotnet.txt
|
||||
cspell:python/src/python/python.txt
|
||||
cspell:css/css.txt
|
||||
cspell:cpp/src/stdlib-cmath.txt
|
||||
check_extra_dictionaries: ''
|
||||
|
||||
comment-push:
|
||||
name: Report (Push)
|
||||
# If your workflow isn't running on push, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
contents: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
comment-pr:
|
||||
name: Report (PR)
|
||||
# If you workflow isn't running on pull_request*, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@v0.0.21
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@prerelease
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
@@ -400,8 +400,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Control", "src\ca
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests", "src\cascadia\WindowsTerminal_UIATests\WindowsTerminal.UIA.Tests.csproj", "{F19DACD5-0C6E-40DC-B6E4-767A3200542C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-ms-win-core-synch-l1-2-0", "src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj", "{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererAtlas", "src\renderer\atlas\atlas.vcxproj", "{8222900C-8B6C-452A-91AC-BE95DB04B95F}"
|
||||
@@ -3300,49 +3298,6 @@ Global
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.Build.0 = Release|x64
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.Build.0 = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x64.Build.0 = AuditMode|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x64Test.Build.0 = Debug|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x86Test.Build.0 = Debug|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x64.Build.0 = Debug|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x86.Build.0 = Debug|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x86.Build.0 = Fuzzing|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x64Test.ActiveCfg = Release|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x64Test.Build.0 = Release|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x86Test.Build.0 = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x64.ActiveCfg = Release|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x64.Build.0 = Release|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.ActiveCfg = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.Build.0 = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
@@ -3591,7 +3546,6 @@ Global
|
||||
{05D9052F-D78F-478F-968A-2DE38A6DB996} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
{06EC74CB-9A12-428C-B551-8537EC964726} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
|
||||
|
||||
@@ -55,8 +55,9 @@ Copy-Item "build\helix\runtests.cmd" $payloadDir
|
||||
Copy-Item "build\helix\InstallTestAppDependencies.ps1" "$payloadDir"
|
||||
Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
|
||||
|
||||
# Copy the APPX package from the 'drop' artifact dir
|
||||
# Copy the APPX package from the 'drop' artifact dir and Windows Kits
|
||||
Copy-Item "$repoDirectory\Artifacts\$ArtifactName\appx\CascadiaPackage_0.0.1.0_$Platform.msix" $payloadDir\CascadiaPackage.zip
|
||||
Copy-Item "C:\program files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.VCLibs.Desktop\14.0\Appx\Retail\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" $payloadDir\VCLibs.zip
|
||||
|
||||
# Rename it to extension of ZIP because Expand-Archive is real sassy on the build machines
|
||||
# and refuses to unzip it because of its file extension while on a desktop, it just
|
||||
@@ -64,3 +65,4 @@ Copy-Item "$repoDirectory\Artifacts\$ArtifactName\appx\CascadiaPackage_0.0.1.0_$
|
||||
|
||||
# Extract the APPX package
|
||||
Expand-Archive -LiteralPath $payloadDir\CascadiaPackage.zip -DestinationPath $payloadDir\appx
|
||||
Expand-Archive -LiteralPath $payloadDir\VCLibs.zip -DestinationPath $payloadDir\appx -Force
|
||||
|
||||
@@ -5,4 +5,6 @@
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<!-- This cannot be included in another project that depends on XAML (as it would be a duplicate package ID) -->
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.0" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
|
||||
<package id="Microsoft.Debugging.Tools.SrcTool" version="20220617.1556.0" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
62
build/pipelines/daily-loc-submission.yml
Normal file
62
build/pipelines/daily-loc-submission.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
schedules:
|
||||
- cron: "0 3 * * 2-6" # Run at 03:00 UTC Tuesday through Saturday (After the work day in Pacific, Mon-Fri)
|
||||
displayName: "Nightly Localization Build"
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
always: false # only run if there's code changes!
|
||||
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: self
|
||||
type: git
|
||||
ref: main
|
||||
- repository: internal
|
||||
type: git
|
||||
name: Terminal.Internal
|
||||
ref: main
|
||||
|
||||
steps:
|
||||
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: false
|
||||
fetchDepth: 1 # Don't need a deep checkout for loc files!
|
||||
persistCredentials: true
|
||||
path: s # Adding a second repo made Azure DevOps change where we're checked out.
|
||||
|
||||
- checkout: internal
|
||||
clean: true
|
||||
submodules: false
|
||||
fetchDepth: 1
|
||||
persistCredentials: true
|
||||
path: s/Terminal.Internal
|
||||
|
||||
- task: MicrosoftTDBuild.tdbuild-task.tdbuild-task.TouchdownBuildTask@1
|
||||
displayName: 'Touchdown Build - 7105, PRODEXT'
|
||||
inputs:
|
||||
teamId: 7105
|
||||
authId: '$(TouchdownApplicationID)'
|
||||
authKey: '$(TouchdownApplicationKey)'
|
||||
resourceFilePath: |
|
||||
**\en-US\*.resw
|
||||
Terminal.Internal\PDPs\Stable\PDPs\en-us\PDP.xml
|
||||
Terminal.Internal\PDPs\Preview\PDPs\en-us\PDP.xml
|
||||
outputDirectoryRoot: LocOutput
|
||||
appendRelativeDir: true
|
||||
pseudoSetting: Included
|
||||
|
||||
# Saving one of these makes it really easy to inspect the loc output...
|
||||
- powershell: 'tar czf LocOutput.tar.gz LocOutput'
|
||||
displayName: 'Archive Loc Output for Submission'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: LocOutput'
|
||||
inputs:
|
||||
PathtoPublish: LocOutput.tar.gz
|
||||
ArtifactName: LocOutput
|
||||
@@ -2,9 +2,9 @@
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
pool:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
parameters:
|
||||
- name: branding
|
||||
@@ -134,8 +134,6 @@ jobs:
|
||||
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- template: .\templates\restore-nuget-steps.yml
|
||||
# Pull the Windows SDK for the developer tools like the debuggers so we can index sources later
|
||||
- template: .\templates\install-winsdk-steps.yml
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
@@ -195,7 +193,6 @@ jobs:
|
||||
condition: true
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -222,7 +219,6 @@ jobs:
|
||||
displayName: Build solution **\OpenConsole.sln for PublicTerminalCore
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -231,7 +227,6 @@ jobs:
|
||||
displayName: Build solution **\OpenConsole.sln for ConPTY
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Conhost\Host_EXE;Conhost\winconpty_DLL
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -241,6 +236,7 @@ jobs:
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
errorActionPreference: silentlyContinue
|
||||
pwsh: true
|
||||
- task: PowerShell@2
|
||||
displayName: Run Unit Tests
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
@@ -303,8 +299,6 @@ jobs:
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/PublicTerminalCore.dll
|
||||
|
||||
**/api-ms-win-core-synch-l1-2-0.dll
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/wpf
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
@@ -327,7 +321,7 @@ jobs:
|
||||
|
||||
- ${{ if eq(parameters.runCompliance, true) }}:
|
||||
- template: ./templates/build-console-compliance-job.yml
|
||||
|
||||
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- job: BundleAndSign
|
||||
strategy:
|
||||
@@ -546,7 +540,6 @@ jobs:
|
||||
displayName: Build solution **\OpenConsole.sln for WPF Control
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalReleaseBuild=$(UseReleaseBranding);Version=$(XES_PACKAGEVERSIONNUMBER) /t:Pack
|
||||
platform: Any CPU
|
||||
configuration: $(BuildConfiguration)
|
||||
@@ -612,6 +605,8 @@ jobs:
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
|
||||
- template: .\templates\restore-nuget-steps.yml
|
||||
|
||||
# Download the appx-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- ${{ each windowsVersion in parameters.buildWindowsVersions }}:
|
||||
@@ -634,13 +629,12 @@ jobs:
|
||||
}
|
||||
displayName: Extract symbols for public consumption
|
||||
|
||||
# Pull the Windows SDK for the developer tools like the debuggers so we can index sources later
|
||||
- template: .\templates\install-winsdk-steps.yml
|
||||
- task: PowerShell@2
|
||||
displayName: Source Index PDBs (the public ones)
|
||||
inputs:
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)/appxsym-temp' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
pwsh: true
|
||||
|
||||
# Publish the app symbols to the public MSDL symbol server
|
||||
# accessible via https://msdl.microsoft.com/download/symbols
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
@@ -27,7 +27,6 @@ jobs:
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: ${{ parameters.additionalBuildArguments }}
|
||||
|
||||
@@ -12,12 +12,12 @@ jobs:
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
WindowsTerminalBranding: ${{ parameters.branding }}
|
||||
EnableRichCodeNavigation: true
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- steps: restore-nuget-steps.yml
|
||||
- template: restore-nuget-steps.yml
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
# 1ES Component Governance onboarding (Detects open source components). See https://docs.opensource.microsoft.com/tools/cg.html
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: Component Detection
|
||||
|
||||
|
||||
# # PREfast and PoliCheck need Node. Install that first.
|
||||
- task: NodeTool@0
|
||||
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
# step instead that builds the code normally before calling them.
|
||||
# Also... PREfast will rebuild anyway so that's why we're not running a normal build first.
|
||||
# Waste of time to build twice.
|
||||
# PREfast. See https://www.1eswiki.com/wiki/SDL_Native_Rules_Build_Task
|
||||
# PREfast. See https://www.1eswiki.com/wiki/SDL_Native_Rules_Build_Task
|
||||
|
||||
# The following 1ES tasks all operate completely differently and have a different syntax for usage.
|
||||
# Most notable is every one of them has a different way of excluding things.
|
||||
@@ -98,7 +98,9 @@ jobs:
|
||||
displayName: 'Run the PREfast SDL Native Rules for MSBuild'
|
||||
condition: succeededOrFailed()
|
||||
inputs:
|
||||
msBuildCommandline: msbuild.exe /nologo /m /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /p:platform=$(BuildPlatform) /p:configuration=$(BuildConfiguration) /t:Terminal\Window\WindowsTerminal /p:VisualStudioVersion=16.0 $(Build.SourcesDirectory)\OpenConsole.sln
|
||||
setupCommandlines: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsMSBuildCmd.bat"'
|
||||
msBuildCommandline: msbuild.exe /nologo /m /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /p:platform=$(BuildPlatform) /p:configuration=$(BuildConfiguration) /t:Terminal\Window\WindowsTerminal /p:VisualStudioVersion=17.0 $(Build.SourcesDirectory)\OpenConsole.sln
|
||||
msBuildVersion: "17.0"
|
||||
|
||||
# Copies output from PREfast SDL Native Rules task to expected location for consumption by PkgESSecComp
|
||||
- task: CopyFiles@1
|
||||
@@ -146,7 +148,7 @@ jobs:
|
||||
continueOnError: true
|
||||
|
||||
# Set XES_SERIALPOSTBUILDREADY to run Security and Compliance task once per build
|
||||
- powershell: Write-Host “##vso[task.setvariable variable=XES_SERIALPOSTBUILDREADY;]true”
|
||||
- powershell: Write-Host "##vso[task.setvariable variable=XES_SERIALPOSTBUILDREADY;]true"
|
||||
displayName: 'Set XES_SERIALPOSTBUILDREADY Vars'
|
||||
|
||||
# https://www.osgwiki.com/wiki/Package_ES_Security_and_Compliance
|
||||
@@ -156,12 +158,12 @@ jobs:
|
||||
# (PREfast, PoliCheck, Credscan)
|
||||
# - Runs Windows-specific compliance tasks inside the task
|
||||
# + CheckCFlags - ensures that compiler and linker flags meet Windows standards
|
||||
# + CFGCheck/XFGCheck - ensures that Control Flow Guard (CFG) or
|
||||
# + CFGCheck/XFGCheck - ensures that Control Flow Guard (CFG) or
|
||||
# eXtended Flow Guard (XFG) are enabled on binaries
|
||||
# NOTE: CFG is deprecated and XFG isn't fully ready yet.
|
||||
# NOTE2: CFG fails on an XFG'd binary
|
||||
# - Brokers all security/compliance task logs to "Trust Services Automation (TSA)" (https://aka.ms/tsa)
|
||||
# which is a system that maps all errors into the appropriate bug database
|
||||
# which is a system that maps all errors into the appropriate bug database
|
||||
# template for each organization since they all vary. It should also suppress
|
||||
# new bugs when one already exists for the product.
|
||||
# This one is set up to go to the OS repository and use the given parameters
|
||||
@@ -198,4 +200,4 @@ jobs:
|
||||
- toolName: CredScan
|
||||
enable: false
|
||||
- toolName: XFGCheck
|
||||
enable: false
|
||||
enable: false
|
||||
|
||||
@@ -9,12 +9,12 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
@@ -32,12 +32,11 @@ jobs:
|
||||
echo VCToolsInstallDir = %VCToolsInstallDir%
|
||||
echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir%
|
||||
displayName: 'Retrieve VC tools directory'
|
||||
|
||||
|
||||
- task: VSBuild@1
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }}"
|
||||
@@ -88,4 +87,4 @@ jobs:
|
||||
displayName: 'Publish All Build Artifacts'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
ArtifactName: 'fuzzingBuildOutput'
|
||||
ArtifactName: 'fuzzingBuildOutput'
|
||||
|
||||
@@ -12,12 +12,12 @@ jobs:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Instrument'
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- template: build-console-steps.yml
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
configuration: ${{ parameters.configuration }}
|
||||
platform: ${{ parameters.platform }}
|
||||
rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }}
|
||||
|
||||
|
||||
- template: helix-processtestresults-job.yml
|
||||
parameters:
|
||||
name: 'ProcessTestResults'
|
||||
|
||||
@@ -28,7 +28,6 @@ steps:
|
||||
displayName: 'Build solution **\OpenConsole.sln'
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
platform: '$(BuildPlatform)'
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }} /p:PGOBuildMode=$(PGOBuildMode) /bl:$(Build.SourcesDirectory)\\msbuild.binlog"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
parameters:
|
||||
sdkVersion: 18362
|
||||
steps:
|
||||
- task: powershell@2
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: build\scripts\Install-WindowsSdkISO.ps1
|
||||
arguments: ${{ parameters.sdkVersion }}
|
||||
displayName: 'Install Windows SDK (${{ parameters.sdkVersion }})'
|
||||
@@ -11,12 +11,12 @@ jobs:
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
pool:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPoolOSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: WinDevPool-L
|
||||
demands: ImageOverride -equals WinDevVS16-latest
|
||||
demands: ImageOverride -equals WinDevVS17-latest
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
|
||||
@@ -8,10 +8,11 @@ Param(
|
||||
[switch]$recursive
|
||||
)
|
||||
|
||||
$debuggerPath = (Get-ItemProperty -path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots" -name WindowsDebuggersRoot10).WindowsDebuggersRoot10
|
||||
$srcsrvPath = Join-Path $debuggerPath "x64\srcsrv"
|
||||
$srctoolExe = Join-Path $srcsrvPath "srctool.exe"
|
||||
$pdbstrExe = Join-Path $srcsrvPath "pdbstr.exe"
|
||||
$pdbStrPackage = ([xml](Get-Content "$SourceRoot\build\packages.config")).packages.package | Where-Object id -like "*PdbStr*"
|
||||
# This assumes that we rev PdbStr and SrcTool at the same time.
|
||||
$debugPackageVersions = $pdbStrPackage.version
|
||||
$srctoolExe = Join-Path $SourceRoot "packages" "Microsoft.Debugging.Tools.SrcTool.$debugPackageVersions" "content" "amd64" "srctool.exe"
|
||||
$pdbstrExe = Join-Path $SourceRoot "packages" "Microsoft.Debugging.Tools.PdbStr.$debugPackageVersions" "content" "amd64" "pdbstr.exe"
|
||||
|
||||
$fileTable = @{}
|
||||
foreach ($gitFile in & git ls-files)
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
param([Parameter(Mandatory=$true, Position=0)]
|
||||
[string]$buildNumber)
|
||||
|
||||
# Ensure the error action preference is set to the default for PowerShell3, 'Stop'
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Constants
|
||||
$WindowsSDKOptions = @("OptionId.UWPCpp", "OptionId.DesktopCPPx64", "OptionId.DesktopCPPx86", "OptionId.DesktopCPPARM64", "OptionId.DesktopCPPARM", "OptionId.WindowsDesktopDebuggers")
|
||||
$WindowsSDKRegPath = "HKLM:\Software\WOW6432Node\Microsoft\Windows Kits\Installed Roots"
|
||||
$WindowsSDKRegRootKey = "KitsRoot10"
|
||||
$WindowsSDKVersion = "10.0.$buildNumber.0"
|
||||
$WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options"
|
||||
$StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification"
|
||||
$PublicKeyTokens = @("31bf3856ad364e35")
|
||||
|
||||
if ($buildNumber -notmatch "^\d{5,}$")
|
||||
{
|
||||
Write-Host "ERROR: '$buildNumber' doesn't look like a windows build number"
|
||||
Write-Host
|
||||
Exit 1
|
||||
}
|
||||
|
||||
function Download-File
|
||||
{
|
||||
param ([string] $outDir,
|
||||
[string] $downloadUrl,
|
||||
[string] $downloadName)
|
||||
|
||||
$downloadPath = Join-Path $outDir "$downloadName.download"
|
||||
$downloadDest = Join-Path $outDir $downloadName
|
||||
$downloadDestTemp = Join-Path $outDir "$downloadName.tmp"
|
||||
|
||||
Write-Host -NoNewline "Downloading $downloadName..."
|
||||
|
||||
$retries = 10
|
||||
$downloaded = $false
|
||||
while (-not $downloaded)
|
||||
{
|
||||
try
|
||||
{
|
||||
$webclient = new-object System.Net.WebClient
|
||||
$webclient.DownloadFile($downloadUrl, $downloadPath)
|
||||
$downloaded = $true
|
||||
}
|
||||
catch [System.Net.WebException]
|
||||
{
|
||||
Write-Host
|
||||
Write-Warning "Failed to fetch updated file from $downloadUrl : $($error[0])"
|
||||
if (!(Test-Path $downloadDest))
|
||||
{
|
||||
if ($retries -gt 0)
|
||||
{
|
||||
Write-Host "$retries retries left, trying download again"
|
||||
$retries--
|
||||
start-sleep -Seconds 10
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "$downloadName was not found at $downloadDest"
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Warning "$downloadName may be out of date"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Unblock-File $downloadPath
|
||||
|
||||
$downloadDestTemp = $downloadPath;
|
||||
|
||||
# Delete and rename to final dest
|
||||
Write-Host "testing $downloadDest"
|
||||
if (Test-Path $downloadDest)
|
||||
{
|
||||
Write-Host "Deleting: $downloadDest"
|
||||
Remove-Item $downloadDest -Force
|
||||
}
|
||||
|
||||
Move-Item -Force $downloadDestTemp $downloadDest
|
||||
Write-Host "Done"
|
||||
|
||||
return $downloadDest
|
||||
}
|
||||
|
||||
function Get-ISODriveLetter
|
||||
{
|
||||
param ([string] $isoPath)
|
||||
|
||||
$diskImage = Get-DiskImage -ImagePath $isoPath
|
||||
if ($diskImage)
|
||||
{
|
||||
$volume = Get-Volume -DiskImage $diskImage
|
||||
|
||||
if ($volume)
|
||||
{
|
||||
$driveLetter = $volume.DriveLetter
|
||||
if ($driveLetter)
|
||||
{
|
||||
$driveLetter += ":"
|
||||
return $driveLetter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Mount-ISO
|
||||
{
|
||||
param ([string] $isoPath)
|
||||
|
||||
# Check if image is already mounted
|
||||
$isoDrive = Get-ISODriveLetter $isoPath
|
||||
|
||||
if (!$isoDrive)
|
||||
{
|
||||
Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null
|
||||
}
|
||||
|
||||
$isoDrive = Get-ISODriveLetter $isoPath
|
||||
Write-Verbose "$isoPath mounted to ${isoDrive}:"
|
||||
}
|
||||
|
||||
function Dismount-ISO
|
||||
{
|
||||
param ([string] $isoPath)
|
||||
|
||||
$isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter
|
||||
|
||||
if ($isoDrive)
|
||||
{
|
||||
Write-Verbose "$isoPath dismounted"
|
||||
Dismount-DiskImage -ImagePath $isoPath | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Disable-StrongName
|
||||
{
|
||||
param ([string] $publicKeyToken = "*")
|
||||
|
||||
reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
|
||||
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
|
||||
{
|
||||
reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Test-Admin
|
||||
{
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal $identity
|
||||
$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
function Test-RegistryPathAndValue
|
||||
{
|
||||
param (
|
||||
[parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $path,
|
||||
[parameter(Mandatory=$true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string] $value)
|
||||
|
||||
try
|
||||
{
|
||||
if (Test-Path $path)
|
||||
{
|
||||
Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
|
||||
return $true
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
function Test-InstallWindowsSDK
|
||||
{
|
||||
$retval = $true
|
||||
|
||||
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey)
|
||||
{
|
||||
# A Windows SDK is installed
|
||||
# Is an SDK of our version installed with the options we need?
|
||||
$allRequiredSdkOptionsInstalled = $true
|
||||
foreach($sdkOption in $WindowsSDKOptions)
|
||||
{
|
||||
if (!(Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value $sdkOption))
|
||||
{
|
||||
$allRequiredSdkOptionsInstalled = $false
|
||||
}
|
||||
}
|
||||
|
||||
if($allRequiredSdkOptionsInstalled)
|
||||
{
|
||||
# It appears we have what we need. Double check the disk
|
||||
$sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
|
||||
if ($sdkRoot)
|
||||
{
|
||||
if (Test-Path $sdkRoot)
|
||||
{
|
||||
$refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion"
|
||||
if (Test-Path $refPath)
|
||||
{
|
||||
$umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion"
|
||||
if (Test-Path $umdPath)
|
||||
{
|
||||
# Pretty sure we have what we need
|
||||
$retval = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $retval
|
||||
}
|
||||
|
||||
function Test-InstallStrongNameHijack
|
||||
{
|
||||
foreach($publicKeyToken in $PublicKeyTokens)
|
||||
{
|
||||
$key = "$StrongNameRegPath\*,$publicKeyToken"
|
||||
if (!(Test-Path $key))
|
||||
{
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..."
|
||||
$InstallWindowsSDK = Test-InstallWindowsSDK
|
||||
if ($InstallWindowsSDK)
|
||||
{
|
||||
Write-Host "Installation required"
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host "INSTALLED"
|
||||
}
|
||||
|
||||
$StrongNameHijack = Test-InstallStrongNameHijack
|
||||
Write-Host -NoNewline "Checking if StrongName bypass required..."
|
||||
|
||||
if ($StrongNameHijack)
|
||||
{
|
||||
Write-Host "REQUIRED"
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host "Done"
|
||||
}
|
||||
|
||||
if ($StrongNameHijack -or $InstallWindowsSDK)
|
||||
{
|
||||
if (!(Test-Admin))
|
||||
{
|
||||
Write-Host
|
||||
throw "ERROR: Elevation required"
|
||||
}
|
||||
}
|
||||
|
||||
if ($InstallWindowsSDK)
|
||||
{
|
||||
# Static(ish) link for Windows SDK
|
||||
# Note: there is a delay from Windows SDK announcements to availability via the static link
|
||||
$uri = "https://software-download.microsoft.com/download/sg/Windows_InsiderPreview_SDK_en-us_$($buildNumber)_1.iso";
|
||||
|
||||
if ($env:TEMP -eq $null)
|
||||
{
|
||||
$env:TEMP = Join-Path $env:SystemDrive 'temp'
|
||||
}
|
||||
|
||||
$winsdkTempDir = Join-Path (Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName())) "WindowsSDK"
|
||||
|
||||
if (![System.IO.Directory]::Exists($winsdkTempDir))
|
||||
{
|
||||
[void][System.IO.Directory]::CreateDirectory($winsdkTempDir)
|
||||
}
|
||||
|
||||
$file = "winsdk_$buildNumber.iso"
|
||||
|
||||
Write-Verbose "Getting WinSDK from $uri"
|
||||
$downloadFile = Download-File $winsdkTempDir $uri $file
|
||||
Write-Verbose "File is at $downloadFile"
|
||||
$downloadFileItem = Get-Item $downloadFile
|
||||
|
||||
# Check to make sure the file is at least 10 MB.
|
||||
if ($downloadFileItem.Length -lt 10*1024*1024)
|
||||
{
|
||||
Write-Host
|
||||
Write-Host "ERROR: Downloaded file doesn't look large enough to be an ISO. The requested version may not be on microsoft.com yet."
|
||||
Write-Host
|
||||
Exit 1
|
||||
}
|
||||
|
||||
# TODO Check if zip, exe, iso, etc.
|
||||
try
|
||||
{
|
||||
Write-Host -NoNewline "Mounting ISO $file..."
|
||||
Mount-ISO $downloadFile
|
||||
Write-Host "Done"
|
||||
|
||||
$isoDrive = Get-ISODriveLetter $downloadFile
|
||||
|
||||
if (Test-Path $isoDrive)
|
||||
{
|
||||
Write-Host -NoNewLine "Installing WinSDK..."
|
||||
|
||||
$setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe"
|
||||
Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q"
|
||||
Write-Host "Done"
|
||||
}
|
||||
else
|
||||
{
|
||||
throw "Could not find mounted ISO at ${isoDrive}"
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Write-Host -NoNewline "Dismounting ISO $file..."
|
||||
Dismount-ISO $downloadFile
|
||||
Write-Host "Done"
|
||||
}
|
||||
}
|
||||
|
||||
if ($StrongNameHijack)
|
||||
{
|
||||
Write-Host -NoNewline "Disabling StrongName for Windows SDK..."
|
||||
|
||||
foreach($key in $PublicKeyTokens)
|
||||
{
|
||||
Disable-StrongName $key
|
||||
}
|
||||
|
||||
Write-Host "Done"
|
||||
}
|
||||
@@ -26,7 +26,6 @@
|
||||
"/doc/user-docs/",
|
||||
"/src/tools/MonarchPeasantSample/",
|
||||
"/src/tools/MonarchPeasantPackage/",
|
||||
"/src/api-ms-win-core-synch-l1-2-0/",
|
||||
"/src/tools/ansi-color/",
|
||||
"/src/tools/ColorTool/",
|
||||
"/scratch/",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2022</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>15</VersionMinor>
|
||||
<VersionMinor>16</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<!-- Managed packages -->
|
||||
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
|
||||
<package id="Castle.Core" version="4.1.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
|
||||
<package id="Selenium.Support" version="3.5.0" targetFramework="net45" />
|
||||
<package id="Selenium.WebDriver" version="3.5.0" targetFramework="net45" />
|
||||
</packages>
|
||||
|
||||
@@ -110,7 +110,7 @@ This takes quite some time, and only generates an `msix`. It does not install th
|
||||
|
||||
```powershell
|
||||
# If you haven't already:
|
||||
Import-Module tools\OpenConsole.psm1;
|
||||
Import-Module .\tools\OpenConsole.psm1;
|
||||
Set-MsBuildDevEnvironment;
|
||||
|
||||
# The Set-MsBuildDevEnvironment call is needed for finding the path to
|
||||
@@ -121,7 +121,7 @@ if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) {
|
||||
Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe'
|
||||
};
|
||||
New-Item ..\loose -Type Directory -Force;
|
||||
makeappx unpack /v /o /p .\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\Loose\;
|
||||
makeappx unpack /v /o /p .\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\loose\;
|
||||
Add-AppxPackage -Path ..\loose\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown
|
||||
```
|
||||
|
||||
|
||||
@@ -350,9 +350,11 @@
|
||||
"switchToTab",
|
||||
"tabSearch",
|
||||
"toggleAlwaysOnTop",
|
||||
"toggleBlockSelection",
|
||||
"toggleFocusMode",
|
||||
"selectAll",
|
||||
"setFocusMode",
|
||||
"switchSelectionEndpoint",
|
||||
"toggleFullscreen",
|
||||
"setFullScreen",
|
||||
"setMaximized",
|
||||
@@ -364,6 +366,10 @@
|
||||
"quit",
|
||||
"adjustOpacity",
|
||||
"restoreLastClosed",
|
||||
"addMark",
|
||||
"scrollToMark",
|
||||
"clearMark",
|
||||
"clearAllMarks",
|
||||
"unbound"
|
||||
],
|
||||
"type": "string"
|
||||
@@ -383,6 +389,15 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ScrollToMarkDirection": {
|
||||
"enum": [
|
||||
"previous",
|
||||
"next",
|
||||
"first",
|
||||
"last"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ResizeDirection": {
|
||||
"enum": [
|
||||
"left",
|
||||
@@ -732,6 +747,30 @@
|
||||
"direction"
|
||||
]
|
||||
},
|
||||
"ScrollToMarkAction": {
|
||||
"description": "Arguments corresponding to a Scroll to Mark Action",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ShortcutAction"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"const": "scrollToMark"
|
||||
},
|
||||
"direction": {
|
||||
"$ref": "#/$defs/ScrollToMarkDirection",
|
||||
"default": "previous",
|
||||
"description": "The direction to scroll to a mark."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
"direction"
|
||||
]
|
||||
},
|
||||
"SendInputAction": {
|
||||
"description": "Arguments corresponding to a Send Input Action",
|
||||
"allOf": [
|
||||
@@ -839,6 +878,27 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"AddMarkAction": {
|
||||
"description": "Arguments corresponding to an Add Mark Action",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/ShortcutAction"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"const": "addMark"
|
||||
},
|
||||
"color": {
|
||||
"$ref": "#/$defs/Color",
|
||||
"default": null,
|
||||
"description": "If provided, will set the mark's color to the given value."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"SetColorSchemeAction": {
|
||||
"description": "Arguments corresponding to a Set Color Scheme Action",
|
||||
"allOf": [
|
||||
@@ -1667,6 +1727,16 @@
|
||||
"description": "When set to true, URLs will be detected by the Terminal. This will cause URLs to underline on hover and be clickable by pressing Ctrl.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.autoMarkPrompts": {
|
||||
"default": false,
|
||||
"description": "When set to true, prompts will automatically be marked.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.showMarksOnScrollbar": {
|
||||
"default": false,
|
||||
"description": "When set to true, marks added to the buffer via the addMark action will appear on the scrollbar.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableAnimations": {
|
||||
"default": false,
|
||||
"description": "When set to `true`, visual animations will be disabled across the application.",
|
||||
@@ -1898,6 +1968,21 @@
|
||||
"useAnyExisting"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"newTabPosition": {
|
||||
"default": "atTheEnd",
|
||||
"description": "Specifies position of the new tab. Possible values are \"atTheEnd\" and \"afterCurrentTab\".",
|
||||
"enum": [
|
||||
"atTheEnd",
|
||||
"afterCurrentTab"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"autoHideWindow": {
|
||||
"default": false,
|
||||
"description": "If enabled, Terminal window will be hidden as soon as it loses focus.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -2011,6 +2096,10 @@
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound, flash the taskbar icon (if the terminal window is not in focus) and flash the window. An array of specific behaviors can also be used. Supported array values include `audible`, `window` and `taskbar`. When set to \"none\", nothing will happen.",
|
||||
"$ref": "#/$defs/BellStyle"
|
||||
},
|
||||
"bellSound": {
|
||||
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
|
||||
"$ref": "#/$defs/BellSound"
|
||||
},
|
||||
"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.",
|
||||
|
||||
243
doc/specs/drafts/#3327 - Application Theming/#10509 - Mica.md
Normal file
243
doc/specs/drafts/#3327 - Application Theming/#10509 - Mica.md
Normal file
@@ -0,0 +1,243 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2022-02-15
|
||||
last updated: 2022-04-25
|
||||
issue id: #10509
|
||||
---
|
||||
|
||||
|
||||
# Mica in the Terminal
|
||||
|
||||
## Abstract
|
||||
|
||||
This document serves as a companion doc to the [Theming Spec], rather than a
|
||||
spec on it's own. The context of broader application-level theming support is
|
||||
necessary to understand the big picture of the designs in this discussion.
|
||||
|
||||
|
||||
This spec is intended to help understand the problem space of adding [Mica] to
|
||||
the Windows Terminal. Introduced in Windows 11, Mica is a new type of material
|
||||
that incorporates theme and desktop wallpaper to paint the background of
|
||||
windows. The effect results in a blurred, transparency-like effect, quite
|
||||
similar to [Acrylic]. However, the technical limitations of Mica make it more
|
||||
complicated to integrate seamlessly with the Terminal experience.
|
||||
|
||||
## Background
|
||||
|
||||
Mica is a material that can only be applied to the root of the UI tree, and
|
||||
applies to the entire background surface. It's recommended to be used at the
|
||||
`Page` level, in place of a solid brush like
|
||||
`ApplicationPageBackgroundThemeBrush`. If the developer wants a surface within
|
||||
the page to have a Mica background, they need to make sure to have that element
|
||||
(and all elements behind it up until the `Page`) have a `Transparent`
|
||||
background, so that Mica will be visible through the elements.
|
||||
|
||||
This is contrasted with something like Acrylic, where the acrylic effect is
|
||||
specified at the Element layer itself. An element can request having a
|
||||
`HostBackdrop` brush for its background, and the element will have the Acrylic
|
||||
effect regardless of the structure of the rest of the elements in the UI tree.
|
||||
|
||||
Another important use case here is "Vintage Transparency" (or "unblurred
|
||||
transparency"), which is an unblurred transparency effect for the Terminal
|
||||
window. This is achieved with the `TransparentBackground` API, which enables the
|
||||
Terminal to disable the emergency backstop of the XAML Island. When that's
|
||||
enabled, controls that are transparent will be blended, unblurred, with whatever
|
||||
is visible behind the window. This works because the entire tree of the Terminal
|
||||
window underneath the `TermControl`s are `Transparent`, all the way up to the
|
||||
window itself.
|
||||
|
||||
Right now, the Terminal exposes three settings<sup>[[1]](#footnote-1)</sup>:
|
||||
* Background color
|
||||
* Background Opacity
|
||||
* Whether the user would like to enable acrylic or not
|
||||
|
||||
These settings are exposed at the "Profile"<sup>[[2]](#footnote-2)</sup> level.
|
||||
Properties on a profile are roughly considered to be "what the terminal control
|
||||
will look like when I run this settings profile". Users can have one profile
|
||||
with acrylic, one without, and open [Panes] with these profiles side-by-side in
|
||||
the Terminal. It's entirely possible that a user would have both a pane with and
|
||||
acrylic background, and one with an unblurred background in the same window.
|
||||
|
||||
### User Stories
|
||||
|
||||
* The Terminal should be able to have Mica in the title bar, behind the tabs.
|
||||
* Users will want Mica in the control area, as well as in the titlebar
|
||||
* Users may want Mica in the control, but with a solid titlebar, or an accent
|
||||
colored title bar, or an acrylic one...
|
||||
* Users will want mica in the titlebar with other effects (acrylic, vintage
|
||||
transparency) in the control area
|
||||
|
||||
This is where things get complicated. Given that a control can choose what type
|
||||
of material it has now, users would likely expect to be able to choose between
|
||||
acrylic, unblurred transparency, or Mica. However, Mica can only be applied at
|
||||
the root of the window. It's applied behind everything else in the window. From
|
||||
an implementation standpoint, Mica is a window-level property, not a control
|
||||
level one. If we want to have Mica under one control, we need to enable it for
|
||||
the _whole window_. If we enable Mica for the whole window, that would
|
||||
simultaneously prevent Vintage Transparency from working as expected. This is
|
||||
because the semi-transparent controls would no longer have a fully transparent
|
||||
window background to sit on top of - they'd be blended instead with the Mica
|
||||
background behind the window.
|
||||
|
||||
## Solution design
|
||||
|
||||
### Mica for `TermControl`s
|
||||
If we make enabling Mica for the control a per-profile setting, I believe that
|
||||
will lead to greater user confusion. It would result in "spooky action at a
|
||||
distance", where creating any pane with Mica would force the entire window to
|
||||
have a Mica background. This would change the appearance of any other unblurred
|
||||
transparent panes in the window, causing them to also be subjected to the Mica
|
||||
treatment as well.
|
||||
|
||||
**Proposal**: create a window-level theme property `window.background.useMica`
|
||||
(or similar), which will enable Mica for the entire window. When enabled, users
|
||||
can use a fully transparent, unblurred background for their profile to achieve
|
||||
the Mica effect within the control. When enabled, users **won't** be able to see
|
||||
through to the desktop with any vintage opacity settings.
|
||||
|
||||
By nesting Mica usage under `"window.background"`, it will be clearer that it's
|
||||
something that applies to the whole window, as the background for everything.
|
||||
I believe this is the most acceptable way to expose Mica to our users without
|
||||
"spooky action at a distance".
|
||||
|
||||
An example of what mica in the control area might look like:
|
||||
|
||||

|
||||
|
||||
### Mica in the titlebar
|
||||
|
||||
To achieve Mica in the titlebar, we'll similarly need to allow users to set the
|
||||
titlebar area to totally transparent, to allow the mica behind the window to be
|
||||
visible. A simple theme to achieve that might look like:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"theme": "My Mica Titlebar Theme",
|
||||
"themes": [
|
||||
{
|
||||
"name": "My Mica Titlebar Theme",
|
||||
"window":{
|
||||
"background.useMica": true, // Use mica behind the window
|
||||
},
|
||||
"tabRow":{
|
||||
"background": "#00000000", // Make the TabView Transparent
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
As noted before, due to the intentional limitations of the Mica material, if the
|
||||
user wants Mica in the tab row, they won't be able to use vintage opacity in any
|
||||
controls. The vintage opacity of the controls will show through to the Mica
|
||||
layer, instead of transparent through the whole window.
|
||||
|
||||
## Considered implementations
|
||||
|
||||
* We experimented with a new DWM API in SV2 which should enable us to set the
|
||||
background of our window to Mica. This did seem to work for the root window.
|
||||
It however, did not seem to work for the "drag window", the child HWND which
|
||||
we use to intercept nonclient messages in our titlebar area. Apparently, that
|
||||
API does not work at all for `WS_CHILD` windows, by design. This unfortunately
|
||||
prevents us from allowing Mica only in the titlebar area, without also
|
||||
applying it to the rest of the main window.
|
||||
* We considered rolling our own Mica brush to allow us to style individual
|
||||
elements. This didn't seem terribly hard at face value, considering [the Mica
|
||||
recipe] is basically open-source. However, it seems to rely on some
|
||||
closed-source `IGraphicsEffect`s from the `Composition.Effects` namespace.
|
||||
Apparently, those effects are only [implemented in Win2D]. It's unclear at
|
||||
this time if the Terminal would be capable of ingesting Win2D now or in the
|
||||
future. It does seem as though [Win2D is available via cppwinrt], so that's of
|
||||
some solace.
|
||||
|
||||
**However**, that does leave us in the world where we're rolling our own
|
||||
custom Mica brush, and would need to keep it up to date with the OS
|
||||
implementation. We'd be firmly off the rails of the recommended Mica usage,
|
||||
and our support would be likely very minimal. This is reason alone alone to
|
||||
avoid this path, and instead push for a platform-supported solution.
|
||||
|
||||
**TODO!**: We should make sure to have a Future Considerations section on how
|
||||
we'd implement per-element Mica, should that option be available to us in the
|
||||
future.
|
||||
|
||||
## Potential Issues
|
||||
|
||||
This is not a particularly ergonomic design. From a UX perspective, the user
|
||||
needs to enable one setting in the UI to enable Mica, and then go to profile
|
||||
settings to set the profile to _transparent_ for each of the profiles they want
|
||||
with Mica. That's not very intuitive by any means.
|
||||
|
||||
Mica would also impact our ability to enable the "acrylic on an unfocused
|
||||
window" functionality, requested in [#7158]. To support that feature, we need to
|
||||
have a transparent window backdrop, so that the in-app acrylic will magically
|
||||
use the desktop background. With Mica and this setting enabled, the acrylic
|
||||
would apply on top of the Mica layer. Either warnings would need to be
|
||||
displayed, or one setting would have to disable the other. At the very least,
|
||||
the documentation needs to be very clear about this.
|
||||
|
||||
In [this comment], the poster shows a pretty cool mockup of what a background
|
||||
image for the _window_ might look like. This kinda fills a similar problem space
|
||||
as Mica. If there were a background image for the whole Terminal window, then
|
||||
in-app acrylic would apply on top of the image. Vintage transparency wouldn't
|
||||
work through the image, it would apply on _top_ of the image. Perhaps, because
|
||||
of these related concerns, there should be a singular `window.background`
|
||||
property, that contains an object of settings. Or, to follow the solely dot
|
||||
notation from before
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"theme": "My Background Theme",
|
||||
"themes": [
|
||||
{
|
||||
"name": "My Background Theme",
|
||||
"window":{
|
||||
"background.useMica": true, // Use mica behind the window
|
||||
"background.image": "some/path/to/image", // Use a BG image
|
||||
"background.imageOpacity": 25, // Image is mostly transparent
|
||||
},
|
||||
"tabRow":{
|
||||
"background": "#00000000", // Make the TabView Transparent
|
||||
}
|
||||
}
|
||||
],
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"opacity": 0 // Panes are all totally transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Future considerations
|
||||
|
||||
* I believe Insiders builds have support for two different variants of Mica -
|
||||
standard Mica, and "Thin" Mica. We may need to support a future enum value
|
||||
here, with values `"none", "original", "thin"`. For compatibility with the
|
||||
boolean values, `true=="original"`, `false=="none"`.
|
||||
|
||||
## Resources
|
||||
|
||||
|
||||
### Footnotes
|
||||
|
||||
<a name="footnote-1"><a>[1]: For simplicity of the spec, I'm ignoring the
|
||||
background image settings. I'm also ignoring the small quirk where (at the time
|
||||
of writing), vintage opacity doesn't work on Windows 10. That creates some weird
|
||||
quirks where acrylic is always enabled if the user wants transparency on Windows
|
||||
10. A full discussion of this would only serve to complicate what is
|
||||
fundamentally a Windows 11-centric discussion.
|
||||
|
||||
<a name="footnote-2"><a>[2]: We're also gonna leave out a discussion of focused
|
||||
& unfocused "appearance" setting objects, again for brevity.
|
||||
|
||||
[Theming Spec]: ./%233327%20-%20Application%20Theming.md
|
||||
[Mica]: https://docs.microsoft.com/en-us/windows/apps/design/style/mica
|
||||
[Acrylic]: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic
|
||||
[Panes]: https://docs.microsoft.com/en-us/windows/terminal/panes
|
||||
[#3327]: https://github.com/microsoft/terminal/issues/3327
|
||||
[#10509]: https://github.com/microsoft/terminal/issues/10509
|
||||
[#7158]: https://github.com/microsoft/terminal/issues/7158
|
||||
[this comment]: https://github.com/microsoft/terminal/issues/3327#issuecomment-765493313
|
||||
[the Mica recipe]: https://github.com/microsoft/microsoft-ui-xaml/blob/0db5d0398cb38d69b06e26ef734cbbdebdebe774/dev/Materials/Backdrop/SystemBackdropBrushFactory.cpp#L8-L54
|
||||
[implemented in Win2D]: https://docs.microsoft.com/en-us/uwp/api/windows.ui.composition.compositioneffectbrush?view=winrt-22000#remarks
|
||||
[Win2D is available via cppwinrt]: https://stackoverflow.com/questions/49342164/is-win2d-yet-available-in-c-winrt
|
||||
@@ -0,0 +1,729 @@
|
||||
---
|
||||
author: Mike Griese @zadjii-msft
|
||||
created on: 2019-12-13
|
||||
last updated: 2022-04-25
|
||||
issue id: #3327
|
||||
---
|
||||
|
||||
|
||||
TODO!S:
|
||||
* How do themes play with different window title settings? (different themes for different windows. `_quake` esp.))
|
||||
* any clever ideas for elevated themes?
|
||||
* Reconcile with global `experimental.useBackgroundImageForWindow` from [#12893]
|
||||
|
||||
# Application Theming
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec outlines how the Windows Terminal will enable users to create custom
|
||||
"themes" for the application, enabling further customization of the window.
|
||||
These themes will be implemented as objects containing a group of UI-specific
|
||||
properties, so users can quickly apply a group of properties atomically.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Much of the inspiration for this feature comes from VsCode and its themes. These
|
||||
themes can be more than just different color palettes for the editor - these
|
||||
themes can control the appearance of a variety of UI elements of the VsCode
|
||||
window.
|
||||
|
||||
## Solution Design
|
||||
|
||||
### Requested Functionality ("User Stories")
|
||||
|
||||
The following is a long list of ideas of elements of the window that the user
|
||||
should be able to customize:
|
||||
|
||||
* [ ] Pane Border colors (both the background, and the "focused" color) ([#3061])
|
||||
* [ ] Pane border width ([#3062])
|
||||
* [ ] Tab _Row_ and _Item_ Background color ([#702]/[#1337]/[#2994]/[#3774]/[#1963])
|
||||
- Some users want to set these to the accent color
|
||||
- Some users want to set these to a specific custom color
|
||||
- Some users want this to use the color straight from the active Terminal,
|
||||
allowing the tab or titlebar to "blend into" the terminal
|
||||
* [ ] Feature Request: Setting to hide/remove close ("x") button from tabs ([#3335])
|
||||
* [ ] Various different tab sizing modes
|
||||
- the current sizing, which is `SizeToContent`
|
||||
- Setting a min/max width on tabs
|
||||
- Configuring tabs to split the available space
|
||||
|
||||
Other lower-priority ideas:
|
||||
* [ ] Enable hiding the tab icon altogether
|
||||
* [ ] Enable forcing tab icons to monochrome
|
||||
* [ ] Tab row height
|
||||
* [ ] Tab row font size, font face
|
||||
* [ ] Tab corner radius
|
||||
* [ ] Margin between tabs? Padding within the tab?
|
||||
* [ ] Left-justify / Center / right-justify tab text, when tabs are wider than
|
||||
their text?
|
||||
* [ ] Control colors for light vs dark vs high-contrast modes
|
||||
* [ ] Enable/disable a shadow underneath the tab row, between tabs and content
|
||||
* [ ] Enable/disable a shadow cast by terminals on pane borders or a shadow cast
|
||||
by pane borders on Terminal panes
|
||||
* [ ] Similarly to the tabs, styling the Status Bar ([#3459])
|
||||
- Maybe enable it to have the same color as the active TermControl, causing
|
||||
the same "seamless" effect (see [this
|
||||
comment](https://github.com/microsoft/terminal/issues/3459#issuecomment-550501577))
|
||||
- Change font size, face, colors
|
||||
- Control the borders on the status bar - no top border would give the
|
||||
impression it's "seamless"
|
||||
|
||||
Additionally, the user should be able to easily switch from one installed theme
|
||||
to another. The user should be able to copy a simple blob of settings from the
|
||||
web and paste it into their settings to be able to easily try the theme out.
|
||||
|
||||
### Difference between "Themes" and "Schemes"
|
||||
|
||||
The avid follower of the Windows Terminal might know that the Terminal already
|
||||
contains support for "color schemes". What makes themes different from these
|
||||
schemes, and why should they be separate objects?
|
||||
|
||||
**Color Schemes** are objects that generally control the appearance of the
|
||||
Terminal Control itself (the proverbial "black rectangle with text in it").
|
||||
Primarily, color schemes are used for setting the "color table" of a terminal
|
||||
instance, setting the values for each of the 16 colors in the terminal's color
|
||||
table, and the default foreground and background colors. These are properties
|
||||
that only apply to the contents of the terminal itself, and not necessarily the
|
||||
entire application. Individual terminal control instances can have different
|
||||
color schemes. Furthermore, these schemes are largely in-line with schemes
|
||||
available on other platform's terminals. These schemes were heavily inspired by
|
||||
the great work done at [iTerm2-Color-Schemes].
|
||||
|
||||
Alternatively, **Themes** are sets of properties that apply primarily to the
|
||||
window of the application itself, but not necessarily the terminal content.
|
||||
These properties apply globally to the entire window, as opposed to controlling
|
||||
the appearance of individual terminals. These properties include things such as
|
||||
the coloration and styling of the tabs in the tab row.
|
||||
|
||||
### Theme objects
|
||||
|
||||
Themes will be implemented largely similar to the color schemes implementation.
|
||||
Currently, the terminal contains a list of available color schemes, and profiles
|
||||
can chose to apply a scheme from the list of schemes. We'll add a list of
|
||||
`themes`, and globally, the user will be able to specify one of these themes to
|
||||
apply.
|
||||
|
||||
Take for example the following settings excerpt:
|
||||
|
||||
```json
|
||||
{
|
||||
"theme": "My Boxy Theme",
|
||||
"themes": [
|
||||
{
|
||||
"name": "My Boxy Theme",
|
||||
"window":{
|
||||
"applicationTheme": "dark"
|
||||
},
|
||||
"tab": {
|
||||
"radius": 0,
|
||||
"padding": 5,
|
||||
"background": "terminalBackground",
|
||||
"textColor": "key:SystemAccentColorLight3",
|
||||
"icon": "outline",
|
||||
"closeButton": "hidden",
|
||||
},
|
||||
"tabRow":{
|
||||
"background": "accent",
|
||||
"shadows": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "My small light theme",
|
||||
"window":{
|
||||
"applicationTheme": "light"
|
||||
},
|
||||
"tab": {
|
||||
"background": "#80ff0000",
|
||||
"height": 8,
|
||||
"icon": "hidden",
|
||||
"closeButton": "hover"
|
||||
},
|
||||
"tabRow":{
|
||||
"background": "#ffffffff",
|
||||
"acrylicOpacity": 50,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In the above settings snippet, we see the following things:
|
||||
1. A list of `themes` that the user can pick from. Each theme has a `name`
|
||||
property used to identify the theme, and a group of properties for the theme.
|
||||
2. The user has set the `theme` to `"My Boxy Theme"`, the first theme
|
||||
in the list of themes. If the user wanted to switch to the other installed
|
||||
theme, `"My small light theme"`, they'd simply need to change this property.
|
||||
|
||||
> _note_: Initially, we had considered a `elementPropertyName`-like syntax as
|
||||
opposed to the object-grouped one above. We also considered a
|
||||
`element.propertyName`-like syntax. Overall, we liked the object based one best.
|
||||
>
|
||||
> **For simplicity, we'll be using `element.propertyName` syntax throughout to**
|
||||
> **refer to these properties, when grouped under `element` objects in the theme.**
|
||||
|
||||
These Theme objects are designed to make it simple for the user to be able to
|
||||
quickly download these as an extension in the future, and hot-switch between
|
||||
them. Imagine: an application that would provide a gallery of uploaded themes,
|
||||
and the user could install them as [fragment extensions].
|
||||
|
||||
### Exposed theme properties
|
||||
|
||||
Themes should be able to control a variety of elements of the Terminal UI. Some
|
||||
of these settings will be easier to implement than others. As such, below is a
|
||||
set of properties that seems appropriate to include as part of a "v1" theming
|
||||
implementation. In [Future Considerations](#future-considerations), we'll
|
||||
enumerate additional properties that could be added in the future to further
|
||||
control the UI.
|
||||
|
||||
#### Highest priority theming properties
|
||||
|
||||
These are the elements that have orders of magnitude more requests:
|
||||
* Customizing the titlebar color, including the unfocused titlebar color. This
|
||||
includes merging with the existing `useAcrylicInTabRow` setting.
|
||||
* Customizing the tab color
|
||||
* Enabling Mica for the window
|
||||
|
||||
These represent the most important asks of theming in the Terminal. Everything
|
||||
else that follows is merely "nice to have". The most important elements then
|
||||
are:
|
||||
|
||||
* Properties:
|
||||
- `tab.background`
|
||||
- `tabRow.background`
|
||||
- `tabRow.acrylicOpacity`
|
||||
- `tabRow.unfocusedBackground`
|
||||
- `window.background.useMica`
|
||||
* Theme color variants:
|
||||
- `"#rrggbb"` or `"#rrggbbaa"`
|
||||
- `"accent"`
|
||||
- `"terminalBackground"`
|
||||
|
||||
#### Additional theming v1 Properties
|
||||
|
||||
These are additional settings that seem of higher priority or would be easier to
|
||||
implement. They are categorized by the element of the Terminal they are
|
||||
controlling:
|
||||
|
||||
##### Individual Tabs
|
||||
|
||||
* `tab.cornerRadius`: Control the radius of the corners of the tab items.
|
||||
Accepts a `double`. If this is set to `0`, then the tabs will have squared-off
|
||||
corners. No particular limit is set on the max value accepted, though larger
|
||||
values might not be aesthetically pleasing.
|
||||
* `tab.bottomCornerRadius`: Control the radius of the bottom corners of the tab
|
||||
items. This can be used to make the tabs look like "buttons" in the tab row,
|
||||
instead of tabs.
|
||||
* `tab.closeButton`: Control the visibility of the close button for a tab item.
|
||||
Accepts the following values:
|
||||
- `visible`: The default behavior of the tab item close button - always
|
||||
visible.
|
||||
- `hover`: The close button on a tab item only appears when the tab is
|
||||
hovered.
|
||||
- `hidden`: The close button on a tab is always hidden.
|
||||
* `tab.icon`: Control the visibility, appearance of the tab icon
|
||||
- `visible`: The default behavior of the tab item icon - always visible, and
|
||||
in full color.
|
||||
- `outline`: The icon is always visible, but is only drawn as an outline,
|
||||
using `BitmapIconSource.ShowAsMonochrome(true)`
|
||||
- `hidden`: The icon is hidden
|
||||
* `tab.background`: Control the color of the background of tab items. See below
|
||||
for accepted colors.
|
||||
|
||||
##### Tab Row / "Titlebar"
|
||||
|
||||
* `tabRow.background`: Control the color of the background of the tab row. When
|
||||
tabs in the titlebar are enabled, this sets the color of the titlebar. See
|
||||
below for accepted colors.
|
||||
- Notably, this is named `tabRow.background`, **not** `titlebar.background`.
|
||||
Outside of tabs-in-titlebar mode, we can't control the window titlebar
|
||||
color.
|
||||
- This ignores any alpha and always uses 1.0 for the alpha channel. See
|
||||
[Titlebar complications](#Titlebar-complications) for details.
|
||||
* `tabRow.unfocusedBackground`: Control the color of the background of the tab
|
||||
row, when the window is unfocused. See below for accepted colors.
|
||||
- **TODO!** When omitted, should this default to the `tabRow.background`
|
||||
value (if set), or just the normal unfocused window color? "the normal
|
||||
unfocused window color" is a SUBSTANTIALLY easier implementation.
|
||||
- This ignores any alpha and always uses 1.0 for the alpha channel. See
|
||||
[Titlebar complications](#Titlebar-complications) for details.
|
||||
* `tabRow.acrylicOpacity`: Optional integer representation of an opacity
|
||||
(0-100). When provided, the `tabRow.background` color is treated as an acrylic
|
||||
brush, with the given `TintOpacity`. When omitted, `tabRow.background` is
|
||||
treated as a solid color.
|
||||
- This is to replace the original `useAcrylicInTabRow` setting.
|
||||
- This is NOT provided for the `tabRow.unfocusedBackground` setting. See
|
||||
[Titlebar complications](#Titlebar-complications) for details.
|
||||
|
||||
##### Panes
|
||||
|
||||
* `pane.borderColor`: Control the color of the border used to separate panes.
|
||||
This is the color of the inactive border between panes.
|
||||
* `pane.activeBorderColor`: Control the color of the border of the active pane
|
||||
* `pane.borderWidth`: Control the width of the borders used to separate panes.
|
||||
|
||||
##### Window Properties
|
||||
|
||||
* `window.applicationTheme`: If set, will set the XAML `RequestedTheme`
|
||||
property. This can be one of `light`, `dark` or `system`. This controls how
|
||||
XAML fundamentally styles UI elements. If not provided, will use the default
|
||||
value "system", which will use whatever the system's default theme is.
|
||||
* `window.roundedCorners`: A boolean, to control whether the window has rounded
|
||||
corners on Windows 11.
|
||||
* `window.background.useMica`: a boolean that enables/disables Mica. For more
|
||||
discussion, see [Mica Spec].
|
||||
* `window.background.image`: a path to an image to use as the background for the
|
||||
whole of the content of the Terminal, including in the tab row space.
|
||||
- Additional properties to control the sizing of this image (`padding`,
|
||||
`stretchMode`, `opacity`, etc) would also be exposed as
|
||||
`window.background.imagePadding`, a la the similar Profile settings.
|
||||
|
||||
#### Theme Colors
|
||||
|
||||
For properties like `tab.background` and `tabRow.background`, these colors can
|
||||
be one of:
|
||||
* an `#rrggbb`, `#aarrggbb` color. (Alpha is ignored for `tabRow.background`)
|
||||
* `accent` for the _titlebar_ version of the accent color. Notably, this is
|
||||
**not** just some `SystemAccentColor` value, it's apparently some other value.
|
||||
This has a different value depending on if the window is focused or not. Refer
|
||||
to Edge the the "use accent color on titlebars" setting enabled as a
|
||||
reference.
|
||||
* `terminalBackground` to use the default background color of the active
|
||||
terminal instance.
|
||||
* `terminalForeground` to use the default foreground color of the active
|
||||
terminal instance.
|
||||
* `key:SomeXamlKey` to try and look `SomeXamlKey` up from our resources as a
|
||||
`Color`, and use that color for the value.
|
||||
- `accent` is NOT the same thing as `key:SystemAccentColor`? If it is, is it a reasonable
|
||||
alias that we'd want to provide anyways?
|
||||
- **TODO! DISCUSSION**: PR[#5280] suggested `{ "key": "SomeResourceKey" }` for
|
||||
string resources, should we use that format for colors like this as well?
|
||||
|
||||
This will enable users to not only provide custom colors, but also use the
|
||||
dynamic color of the active terminal instance as well.
|
||||
|
||||
Using `terminalBackground` with multiple concurrent panes with different
|
||||
backgrounds could certainly lead to some odd behavior. The intention of the
|
||||
setting is to provide a way for the tab/titlebar to "seamlessly" connect to the
|
||||
terminal content. However, two panes side-by-side could have different
|
||||
background colors, which might have an unexpected appearance. Since the user
|
||||
must have opted in to this behavior, they'll need to decide personally if
|
||||
that's something that bothers them aesthetically. It's entirely possible that a
|
||||
user doesn't use panes, and this wouldn't even be a problem for them.
|
||||
|
||||
<!-- We could maybe mitigate this by providing the user a way of specifying the
|
||||
`tab.background` color as having both a "single pane" and "multiple pane" mode,
|
||||
though I'm not sure I'm in love with this:
|
||||
|
||||
```json
|
||||
"tab.background": {"single": "terminalBackground", "multiple": null},
|
||||
"tab.background": {"single": "terminalBackground", "multiple": "#ff0000"},
|
||||
"tab.background": [ "terminalBackground", null ]
|
||||
```
|
||||
|
||||
Also shown is an array based implementation, as an option. Overall I'm not
|
||||
happy with this, so I think it shouldn't be in the final draft of the spec, but
|
||||
I'm leaving it for now as an option.
|
||||
-->
|
||||
|
||||
### Implementation of theming
|
||||
|
||||
Largely, whenever possible, we should be able to implement this theming support
|
||||
by modifying our application's `ResourceDictionary` with custom values to
|
||||
control the appearance of UI elements.
|
||||
|
||||
For example, the `TabView` already exposes a number of XAML resources we can
|
||||
modify to adjust it's appearance.
|
||||
* `TabViewBackground` controls the appearance of the background of the tab view.
|
||||
In `showTabsInTitlebar: true` mode, this is the color of the titlebar.
|
||||
* `TabViewItemHeaderBackground` and `TabViewItemHeaderBackgroundSelected`
|
||||
control the appearance of an individual tab.
|
||||
|
||||
By modifying the values of these brushes, we can control the appearance of the
|
||||
tabs. So long as we only in-place modify the resources, XAML is smart enough to
|
||||
be able to update it's appearance automatically. We can do this by querying the
|
||||
`ResourceDictionary` for a given resource, and changing it's value, rather than
|
||||
`insert`ing a new value into the `ResourceDictionary` to replace the old one.
|
||||
|
||||
In addition to the above properties, I propose adding a couple of our own
|
||||
properties: `PaneBorderWidth`: To control the width of pane borders
|
||||
`PaneBorderBrush`: To control the appearance of _inactive_ pane borders
|
||||
`ActivePaneBorderBrush`: To control the appearance of _active_ pane borders
|
||||
|
||||
In order to respond to the live-updating of the `TermControl`'s background
|
||||
color, we'll need to add some more specific logic beyond simply updating a XAML
|
||||
resource when settings change. Whenever a `TermControl`'s background color
|
||||
changes, or the active pane in a tab changes:
|
||||
* If `tab.background == "terminalBackground"`:
|
||||
- If this control is the tab's active terminal control (and the tab doesn't
|
||||
have a custom color set by the color picker), update the tab's own
|
||||
`TabViewItem` with updated `TabViewItemHeaderBackground` and
|
||||
`TabViewItemHeaderBackgroundSelected` values.
|
||||
- Here, we _don't_ want to update the `App`'s resources, since those
|
||||
apply globally, and each tab might have a control with a different
|
||||
color.
|
||||
- The color set by the color picker should override the color from the
|
||||
theme (as the former is a run-time property set to override the
|
||||
latter).
|
||||
* If `tabRow.background == "terminalBackground"`:
|
||||
- If this control is the active terminal of the active `Tab`, then we need
|
||||
to raise an event to communicate this updated value up to the window
|
||||
layer. We'll raise a `"TabRowBackgroundBrush"` property changed event,
|
||||
that the app host can listen for and use to set the titlebar's color, if
|
||||
needed.
|
||||
- The `TerminalPage` also will need to set the Background of the
|
||||
`TabRowControl` to match.
|
||||
|
||||
The `tab.cornerRadius` might be a bit trickier to implement. Currently, there's
|
||||
not a XAML resource that controls this, nor is this something that's exposed by
|
||||
the TabView control. Fortunately, this is something that's exposed to us
|
||||
programmatically. We'll need to manually set that value on each `TabViewItem` as
|
||||
we create new tabs. When we reload settings, we'll need to make sure to come
|
||||
through and update those values manually.
|
||||
|
||||
> NOTE: [microsoft-ui-xaml#2201] suggested that this will be possible with a
|
||||
> future MUX version and changing the `OverlayCornerRadius`.
|
||||
|
||||
### Tab Background Color, Overline Color, and the Tab Color Picker
|
||||
|
||||
Concurrently with the writing of this spec, work is being done to add a "color
|
||||
picker" for tabs, that lets the user manually set a background color for tabs.
|
||||
This may in the future cause some complications with setting a color for tabs
|
||||
from the theme.
|
||||
|
||||
When both features are combined, the color set at runtime but the color picker
|
||||
should override whatever color the user has set in the theme. When the color
|
||||
picker "clears" the color it has set for the tab, it should revert to the color
|
||||
from the theme (if one is set).
|
||||
|
||||
Also mentioned in the implementation of the color picker feature was the ability
|
||||
to not set the entire color of the tab, but just the color of a tab "overline",
|
||||
similar to the way Firefox (by default) styles the focused tab.
|
||||
|
||||
Currently, the `TabView` doesn't support a tab "overline" like this, however, in
|
||||
the future where this is possible, we'd love to also support such an overline.
|
||||
However, the story of setting the tab color manually becomes a bit more
|
||||
confusing now.
|
||||
|
||||
* The user should be able to set both the `tab.background` and `tab.overline`
|
||||
colors in a theme.
|
||||
* The user should be able to configure whether the color picker sets the
|
||||
`background` or the `overline` color of the tab.
|
||||
|
||||
The second setting added above will allow the user to change what's controlled
|
||||
by the color picker. Similarly to how the color picker can set the background of
|
||||
the tab to override the background from the theme, the user could configure the
|
||||
color picker to be able to change the overline color, not the background color
|
||||
of the tab. Then, when the user uses the color picker, the overline color will
|
||||
be overridden by the color picker, instead of the tab background color.
|
||||
|
||||
**Other things to consider:**
|
||||
* Users might want to be able to set a tab color as a part of the profile. One
|
||||
could imagine wanting to set the tab background color for Windows PowerShell
|
||||
to `rgb(1, 36, 86)` automatically. If we make this property part of the
|
||||
Profile, then we should use the profile's value as the runtime-override (of
|
||||
the theme value) for this property. If the color picker is used to set the
|
||||
color of the tab, then it'll override the runtime color for that tab.
|
||||
- How does this interact with multiple Panes in a tab? Should the Tab
|
||||
override color be per-terminal instance? If the terminal has a tab color,
|
||||
then that would override the theme, but not the tab's own override color?
|
||||
- If that were the case, the order of precedence would be:
|
||||
1. A color set at runtime with the color picker
|
||||
2. A color from the active terminal within the tab, if it has one
|
||||
3. The tab color from the theme
|
||||
* Users might want to be able to configure the titlebar to use a color based off
|
||||
the active tab color. We might need an additional special value like
|
||||
`terminalBackground` that lets users say "I want to use the active tab color
|
||||
as the titlebar color".
|
||||
- During [#3789], there was a point where the terminal raised actually
|
||||
implemented something like this. In it's implementation, the titlebar color
|
||||
would be slightly lighter or darker than the tab color (to provide some
|
||||
contrast). We'd want to make sure that the user could specify both "I want
|
||||
to use the tab color with some contrast applied" or just literally "Use
|
||||
whatever the active tab's color is."
|
||||
|
||||
### Default Themes
|
||||
|
||||
Late in 1.0, we renamed the old property `requestedTheme` to just `theme`.
|
||||
Currently, the user can use that property to simply set the XAML
|
||||
`RequestedTheme` property, which controls the theming of all the XAML UI
|
||||
elements. Currently, they can set that value to one of `light`, `dark` or
|
||||
`system`.
|
||||
|
||||
To maintain backwards compatibility with that setting, we'll introduce _three_
|
||||
themes to `defaults.json`:
|
||||
|
||||
```json
|
||||
"themes": [
|
||||
{
|
||||
"name": "light",
|
||||
"window":{
|
||||
"applicationTheme": "light"
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "dark",
|
||||
"window":{
|
||||
"applicationTheme": "dark"
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"window":{
|
||||
"applicationTheme": "system"
|
||||
},
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Each of these themes will only define one property by default: the
|
||||
`window.applicationTheme` property, which is now responsible for setting the
|
||||
XAML `RequestedTheme` property. With these default themes, the user will still
|
||||
be able to use the old names seamlessly to get the same behavior.
|
||||
|
||||
Additionally, the user will NOT be able to override these built-in themes.
|
||||
Experience trying to not serialize the default color schemes has proven
|
||||
exceptionally tricky, so we're not going to allow that for the built-in themes.
|
||||
The user will always need to fork them to create a new theme. If they're found
|
||||
in the user settings file, we'll just ignore them.
|
||||
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
[TODO!]: # TODO: We should include more mockups here. That would be nice.
|
||||
|
||||

|
||||
_fig 1_: Using a tab color set to "terminalBackground". The Windows PowerShell
|
||||
tab has also set its color with the color picker.
|
||||
|
||||

|
||||
_fig 2_: Using an acrylic titlebar color, with a tab color set to
|
||||
"terminalBackground"
|
||||
|
||||

|
||||
_fig 3_: Using an acrylic terminal background, and the titlebar color is set to
|
||||
"terminalBackground"
|
||||
|
||||
 _fig
|
||||
4_: Using a single image as the background for the window, with a transparent
|
||||
tab row, and rounded bottoms on the TabViewItems. Courtesy of
|
||||
[@Shomnipotence](https://github.com/microsoft/terminal/issues/3327#issuecomment-765493313)
|
||||
|
||||
 _fig
|
||||
5_: Using a bottom corner radius to make tabs appear like buttons on the tab row. Courtesy of
|
||||
[@simioni](https://github.com/microsoft/terminal/issues/3774#issuecomment-609408305)
|
||||
|
||||
|
||||
[TODO!]: # TODO: Settings UI mocks? These pretty substantially affect the UI.
|
||||
<!-- We probably need to expose them in the UI in some way, and not just leave them as "power user settings" -->
|
||||
|
||||
## Potential Issues
|
||||
|
||||
It's totally possible for the user to set some sort of theme that just looks
|
||||
bad. This is absolutely a "beauty in the eye of the beholder" situation - not
|
||||
everyone is going to like the appearance of every theme. The goal of the
|
||||
Terminal is to provide a basic theme that's appropriate for anyone, but empower
|
||||
users to customize the terminal however they see fit. If the user chooses a
|
||||
theme that's not particularly appealing, they can always change it back.
|
||||
|
||||
### Accessibility
|
||||
|
||||
For people using the default theming, there should not be any particular
|
||||
regressions. However, this change does open up the Terminal to changes that
|
||||
might make the Terminal less accessible with certain theme configurations. As
|
||||
these themes would all be user-defined and controlled by the user, we're not
|
||||
concerned that this will be much of an issue. If a user finds one of their
|
||||
themes is less accessible, they can always change the theme to be more
|
||||
appropriate for them, or even switch to another theme.
|
||||
|
||||
Furthermore, this might _help_ certain accessibility stories. Users could pick
|
||||
themes with _even more_ contrast than the Terminal provides by default, or
|
||||
larger font sizes, which might help make parts of the Terminal _more_ visible
|
||||
than the default UI.
|
||||
|
||||
### Security
|
||||
|
||||
This should not introduce any _new_ security concerns. We're relying on the
|
||||
security of jsoncpp for parsing json. Adding new keys to the settings file
|
||||
will rely on jsoncpp's ability to securely parse those json values.
|
||||
|
||||
### Reliability
|
||||
|
||||
This change should not have any particular reliability concerns.
|
||||
|
||||
### Compatibility
|
||||
|
||||
The biggest compatibility concern is regarding the existing values for the
|
||||
`theme` property, which is addressed above.
|
||||
|
||||
#### `useAcrylicInTabRow` migration
|
||||
|
||||
[TODO!]: # TODO: Deprecating the current titlebar acrylic setting, or totally overriding in theme.
|
||||
|
||||
#### `experimental.useBackgroundImageForWindow` migration
|
||||
|
||||
[TODO!]: # TODO: Deprecating the current setting or migrating or whatever
|
||||
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
This change should not have any particular performance concerns. Additional
|
||||
acrylic usage might impact battery life. There's not much concern for any
|
||||
substantial new impacts, however.
|
||||
|
||||
### Branding
|
||||
|
||||
Are we concerned that by enabling theming, the appearance of the Terminal won't
|
||||
be as static, and won't necessarily have as specific a look? It might be harder
|
||||
for potential users see a screenshot of the Terminal and _know_ "Thats the
|
||||
Windows Terminal". Is this something we're really all that concerned about
|
||||
though? If this is something users want (it is), then shouldn't that be what
|
||||
matters?
|
||||
|
||||
### Titlebar complications
|
||||
|
||||
Unfortunately, the original User32 titlebar is actually always drawn underneath
|
||||
our titlebar. Even when the tabs are in the titlebar, that's actually just XAML
|
||||
content drawn on top of the original frame. The rest of the window is
|
||||
transparent, but the titlebar is there.
|
||||
|
||||
Our design to enable unfocused acrylic to work relies on in-app acrylic to allow
|
||||
the acrylic to blur with the transparent window contents. However, since the
|
||||
User32 titlebar is always there, in-app acrylic would end up always blurring
|
||||
_the original titlebar_, which looks ridiculous. This means we can't have
|
||||
unfocused acrylic without showing that titlebar. We'd rather remove that
|
||||
foot gun, and make it explicit that this setting does not exist.
|
||||
|
||||
### Light & dark mode theming
|
||||
|
||||
One request that comes up with frequency is the ability to change the color
|
||||
scheme of a profile automatically based on the system theme. Many users have
|
||||
scripts that automatically change between light and dark theme in the OS based
|
||||
on time of day.
|
||||
|
||||
One thing this design does not do well is account for such theme-switching
|
||||
scenarios. This design assumes a static set of colors for a whole Terminal
|
||||
theme, regardless of whatever `window.applicationTheme` is set to. Should the
|
||||
user leave `window.applicationTheme` set to `system`, it's entirely likely that
|
||||
they would like the rest of their colors to automatically update to match.
|
||||
|
||||
To address this, we'll allow the window-level `theme` property to not only allow
|
||||
a string for a name-based lookup in the list of themes, but als an object. That
|
||||
object will accept two properties: `light` and `dark`. Each of these accepts a
|
||||
string representing the name of a theme to use for that specific OS theme. These
|
||||
strings will default to `"light"` and `"dark"` respectively.
|
||||
```jsonc
|
||||
{
|
||||
"theme": {
|
||||
"light": "my light theme",
|
||||
"dark": "my dark theme"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Also considered: allow the user to set their own brushes as part of a theme, like:
|
||||
```jsonc
|
||||
{
|
||||
"name": "My theme aware theme",
|
||||
"brushes": {
|
||||
"light": {
|
||||
"Foo": "#ff0000"
|
||||
},
|
||||
"dark": {
|
||||
"Foo": "#00ff00"
|
||||
}
|
||||
},
|
||||
"window.applicationTheme": "system",
|
||||
"tabRow.background": "key:Foo",
|
||||
}
|
||||
```
|
||||
This seemed far too complicated to actually understand. -->
|
||||
|
||||
### Admin window themes
|
||||
|
||||
[TODO!]: # TODO! Any clever ideas?
|
||||
|
||||
Same idea as the light vs dark mode theme ideas. How should users be able to
|
||||
style admin vs regular windows?
|
||||
|
||||
## Addenda
|
||||
|
||||
This spec also has a follow-up spec which elaborates on the complexities of Mica
|
||||
in the Terminal. Please also refer to:
|
||||
|
||||
* [Mica in the Terminal]
|
||||
|
||||
## Future considerations
|
||||
|
||||
* Mentioned in [#7005] was the idea of shipping a default theme that had values
|
||||
aligned with the appearance of the Edge browser. Perhaps something like:
|
||||
```jsonc
|
||||
{
|
||||
"name": "Edge",
|
||||
"window":{
|
||||
"applicationTheme": "system"
|
||||
},
|
||||
"tab": {
|
||||
"background": "#whatever-color-edge-is" // Might need a "key:" resource here for light/dark theme switching
|
||||
},
|
||||
"tabRow":{
|
||||
"background": "accent",
|
||||
}
|
||||
},
|
||||
```
|
||||
* Applications should be able to install themes as fragments.
|
||||
- We probably shouldn't allow layering for fragment themes - don't want
|
||||
`foo.exe` installing a `light` theme that totally overrides the built-in
|
||||
one. Right? **TODO! DISCUSSION**
|
||||
* ~I don't think it's unreasonable to implement support for `theme` as either a
|
||||
string or an object. If `theme` is a string, then we can do a name-based
|
||||
lookup in a table of themes. If it's an object, we can just use that object
|
||||
immediately. Doing this might provide a simpler implementation plan whereby we
|
||||
allow `"default"|"light"|"dark"|{object}` at first, and then later add the
|
||||
list of themes.~
|
||||
- This was a cool idea, but ultimately discarded in favor of the OS light/dark
|
||||
theme switching, which needed the object version of `theme` to be reserved
|
||||
for the OS mode lookup.
|
||||
* A cool idea from discussion: `window.highContrastSchemes` as a theme member
|
||||
that controls a per-control property. This would override the color scheme of
|
||||
any pane with a high contrast version, ignoring any colors emitted by the
|
||||
client application. Details are left for a future spec.
|
||||
|
||||
#### Theming v2 Properties
|
||||
|
||||
* `tab.padding`: Control the padding _within_ a tab between the text and the
|
||||
"sides" of the tab
|
||||
* `tab.textColor`: Change the color of the text on a tab
|
||||
* `tabRow.shadows`: Enable/disable the tab "shadows"
|
||||
- note that they're enabled by default and already nearly impossible to see in
|
||||
dark mode.
|
||||
* `tabRow.height`: Change the height of the tab row.
|
||||
* `tabRow.underlineHeight`: Controls the height of a border placed between the
|
||||
tab row and the Terminal panes beneath it. This border doesn't exist
|
||||
currently.
|
||||
* `tabRow.underlineColor`: Controls the color of the aforementioned underline
|
||||
* `window.frameColor`: The `DWMWA_BORDER_COLOR` DWM attribute is [SUPER fun to
|
||||
play with], and trivial to set. We should definitely exposed it.
|
||||
|
||||
<!-- Footnotes -->
|
||||
|
||||
[iTerm2-Color-Schemes]: https://github.com/mbadolato/iTerm2-Color-Schemes
|
||||
[#3061]: https://github.com/microsoft/terminal/issues/3061
|
||||
[#3062]: https://github.com/microsoft/terminal/issues/3062
|
||||
[#702]: https://github.com/microsoft/terminal/issues/702
|
||||
[#1337]: https://github.com/microsoft/terminal/issues/1337
|
||||
[#2994]: https://github.com/microsoft/terminal/issues/2994
|
||||
[#3774]: https://github.com/microsoft/terminal/issues/3774
|
||||
[#3789]: https://github.com/microsoft/terminal/pull/3789
|
||||
[#1963]: https://github.com/microsoft/terminal/issues/1963
|
||||
[#3335]: https://github.com/microsoft/terminal/issues/3335
|
||||
[#3459]: https://github.com/microsoft/terminal/issues/3459
|
||||
[#7005]: https://github.com/microsoft/terminal/issues/7005
|
||||
|
||||
[#5280]: https://github.com/microsoft/terminal/pull/5280
|
||||
|
||||
[microsoft-ui-xaml#2201]: https://github.com/microsoft/microsoft-ui-xaml/pull/2201#issuecomment-606888293
|
||||
[#12893]: https://github.com/microsoft/terminal/pull/12893
|
||||
[Mica in the Terminal]: ./%2310509%20-%20Mica.md
|
||||
[Mica Spec]: ./%2310509%20-%20Mica.md
|
||||
[SUPER fun to play with]: https://github.com/microsoft/terminal/issues/12950
|
||||
[fragment extensions]: https://docs.microsoft.com/en-us/windows/terminal/json-fragment-extensions
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 401 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 779 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 905 KiB |
BIN
doc/specs/drafts/#3327 - Application Theming/tab-buttons-000.png
Normal file
BIN
doc/specs/drafts/#3327 - Application Theming/tab-buttons-000.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{9cf74355-f018-4c19-81ad-9dc6b7f2c6f5}</ProjectGuid>
|
||||
<RootNamespace>apimswincoresynchl120</RootNamespace>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="definitions.def" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>definitions.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="definitions.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,19 +0,0 @@
|
||||
LIBRARY
|
||||
EXPORTS
|
||||
DeleteSynchronizationBarrier = kernel32.DeleteSynchronizationBarrier
|
||||
EnterSynchronizationBarrier = kernel32.EnterSynchronizationBarrier
|
||||
InitOnceBeginInitialize = kernel32.InitOnceBeginInitialize
|
||||
InitOnceComplete = kernel32.InitOnceComplete
|
||||
InitOnceExecuteOnce = kernel32.InitOnceExecuteOnce
|
||||
InitOnceInitialize = kernel32.InitOnceInitialize
|
||||
InitializeConditionVariable = kernel32.InitializeConditionVariable
|
||||
InitializeSynchronizationBarrier = kernel32.InitializeSynchronizationBarrier
|
||||
SignalObjectAndWait = kernel32.SignalObjectAndWait
|
||||
Sleep = kernel32.Sleep
|
||||
SleepConditionVariableCS = kernel32.SleepConditionVariableCS
|
||||
SleepConditionVariableSRW = kernel32.SleepConditionVariableSRW
|
||||
WaitOnAddress
|
||||
WakeAllConditionVariable = kernel32.WakeAllConditionVariable
|
||||
WakeByAddressAll
|
||||
WakeByAddressSingle
|
||||
WakeConditionVariable = kernel32.WakeConditionVariable
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
//
|
||||
// The code in this file was adapted from the STL on the 2021-07-05. Commit:
|
||||
// https://github.com/microsoft/STL/blob/e745bad3b1d05b5b19ec652d68abb37865ffa454/stl/src/atomic_wait.cpp
|
||||
//
|
||||
// It backports the following Windows 8 functions to Windows 7:
|
||||
// * WaitOnAddress
|
||||
// * WakeByAddressSingle
|
||||
// * WakeByAddressAll
|
||||
//
|
||||
|
||||
#include <cstdint>
|
||||
#include <new>
|
||||
|
||||
#include <winsdkver.h>
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#include <sdkddkver.h>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <intrin.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class [[nodiscard]] SRWLockGuard
|
||||
{
|
||||
public:
|
||||
explicit SRWLockGuard(SRWLOCK& lock) noexcept :
|
||||
_lock(&lock)
|
||||
{
|
||||
AcquireSRWLockExclusive(_lock);
|
||||
}
|
||||
|
||||
~SRWLockGuard()
|
||||
{
|
||||
ReleaseSRWLockExclusive(_lock);
|
||||
}
|
||||
|
||||
SRWLockGuard(const SRWLockGuard&) = delete;
|
||||
SRWLockGuard& operator=(const SRWLockGuard&) = delete;
|
||||
|
||||
SRWLockGuard(SRWLockGuard&&) = delete;
|
||||
SRWLockGuard& operator=(SRWLockGuard&&) = delete;
|
||||
|
||||
private:
|
||||
SRWLOCK* _lock;
|
||||
};
|
||||
|
||||
struct WaitContext
|
||||
{
|
||||
const volatile void* address;
|
||||
WaitContext* next;
|
||||
WaitContext* prev;
|
||||
CONDITION_VARIABLE cv;
|
||||
};
|
||||
|
||||
struct [[nodiscard]] GuardedWaitContext : WaitContext
|
||||
{
|
||||
GuardedWaitContext(const volatile void* storage, WaitContext* head) noexcept :
|
||||
WaitContext{ storage, head, head->prev, CONDITION_VARIABLE_INIT }
|
||||
{
|
||||
prev->next = this;
|
||||
next->prev = this;
|
||||
}
|
||||
|
||||
~GuardedWaitContext()
|
||||
{
|
||||
const auto n = next;
|
||||
const auto p = prev;
|
||||
next->prev = p;
|
||||
prev->next = n;
|
||||
}
|
||||
|
||||
GuardedWaitContext(const GuardedWaitContext&) = delete;
|
||||
GuardedWaitContext& operator=(const GuardedWaitContext&) = delete;
|
||||
|
||||
GuardedWaitContext(GuardedWaitContext&&) = delete;
|
||||
GuardedWaitContext& operator=(GuardedWaitContext&&) = delete;
|
||||
};
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
|
||||
struct alignas(std::hardware_destructive_interference_size) WaitTableEntry
|
||||
{
|
||||
SRWLOCK lock = SRWLOCK_INIT;
|
||||
WaitContext head = { nullptr, &head, &head, CONDITION_VARIABLE_INIT };
|
||||
};
|
||||
#pragma warning(pop)
|
||||
|
||||
[[nodiscard]] WaitTableEntry& GetWaitTableEntry(const volatile void* const storage) noexcept
|
||||
{
|
||||
// A prime number for the hash table size was chosen to prevent collisions.
|
||||
constexpr size_t size = 251;
|
||||
constexpr std::hash<uintptr_t> hasher;
|
||||
|
||||
static WaitTableEntry table[size];
|
||||
#pragma warning(suppress : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator
|
||||
#pragma warning(suppress : 26482) // Only index into arrays using constant expressions
|
||||
#pragma warning(suppress : 26490) // Don't use reinterpret_cast
|
||||
return table[hasher(reinterpret_cast<uintptr_t>(storage)) % size];
|
||||
}
|
||||
|
||||
#pragma warning(suppress : 26429) // Symbol 'comparand' is never tested for nullness, it can be marked as not_null
|
||||
bool AreEqual(const volatile void* storage, const void* comparand, size_t size) noexcept
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
return __iso_volatile_load8(static_cast<const volatile __int8*>(storage)) == *static_cast<const __int8*>(comparand);
|
||||
case 2:
|
||||
return __iso_volatile_load16(static_cast<const volatile __int16*>(storage)) == *static_cast<const __int16*>(comparand);
|
||||
case 4:
|
||||
return __iso_volatile_load32(static_cast<const volatile __int32*>(storage)) == *static_cast<const __int32*>(comparand);
|
||||
case 8:
|
||||
return __iso_volatile_load64(static_cast<const volatile __int64*>(storage)) == *static_cast<const __int64*>(comparand);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} // unnamed namespace
|
||||
|
||||
extern "C" BOOL WINAPI WaitOnAddress(_In_reads_bytes_(AddressSize) volatile VOID* Address, _In_reads_bytes_(AddressSize) PVOID CompareAddress, _In_ SIZE_T AddressSize, _In_opt_ DWORD dwMilliseconds)
|
||||
{
|
||||
auto& entry = GetWaitTableEntry(Address);
|
||||
|
||||
SRWLockGuard guard{ entry.lock };
|
||||
GuardedWaitContext context{ Address, &entry.head };
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// NOTE: under lock to prevent lost wakes
|
||||
if (!AreEqual(Address, CompareAddress, AddressSize))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!SleepConditionVariableSRW(&context.cv, &entry.lock, dwMilliseconds, 0))
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (GetLastError() != ERROR_TIMEOUT)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dwMilliseconds != INFINITE)
|
||||
{
|
||||
// spurious wake to recheck the clock
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" VOID WINAPI WakeByAddressSingle(_In_ PVOID Address)
|
||||
{
|
||||
auto& entry = GetWaitTableEntry(Address);
|
||||
SRWLockGuard guard(entry.lock);
|
||||
|
||||
for (auto context = entry.head.next; context != &entry.head; context = context->next)
|
||||
{
|
||||
if (context->address == Address)
|
||||
{
|
||||
// Can't move wake outside SRWLOCKed section: SRWLOCK also protects the context itself
|
||||
WakeAllConditionVariable(&context->cv);
|
||||
// This break; is the difference between WakeByAddressSingle and WakeByAddressAll
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" VOID WINAPI WakeByAddressAll(_In_ PVOID Address)
|
||||
{
|
||||
auto& entry = GetWaitTableEntry(Address);
|
||||
SRWLockGuard guard(entry.lock);
|
||||
|
||||
for (auto context = entry.head.next; context != &entry.head; context = context->next)
|
||||
{
|
||||
if (context->address == Address)
|
||||
{
|
||||
// Can't move wake outside SRWLOCKed section: SRWLOCK also protects the context itself
|
||||
WakeAllConditionVariable(&context->cv);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
src/audio/dirs
Normal file
2
src/audio/dirs
Normal file
@@ -0,0 +1,2 @@
|
||||
DIRS=midi \
|
||||
|
||||
@@ -5,55 +5,30 @@
|
||||
#include "MidiAudio.hpp"
|
||||
#include "../terminal/parser/stateMachine.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
class MidiOut
|
||||
{
|
||||
public:
|
||||
static constexpr auto NOTE_OFF = 0x80;
|
||||
static constexpr auto NOTE_ON = 0x90;
|
||||
static constexpr auto PROGRAM_CHANGE = 0xC0;
|
||||
#include <dsound.h>
|
||||
|
||||
// We're using a square wave as an approximation of the sound that the
|
||||
// original VT525 terminals might have produced. This is probably not
|
||||
// quite right, but it works reasonably well.
|
||||
static constexpr auto SQUARE_WAVE_SYNTH = 80;
|
||||
|
||||
MidiOut() noexcept
|
||||
{
|
||||
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
|
||||
{
|
||||
midiOutOpen(&handle, MIDI_MAPPER, NULL, NULL, CALLBACK_NULL);
|
||||
OutputMessage(PROGRAM_CHANGE, SQUARE_WAVE_SYNTH);
|
||||
}
|
||||
}
|
||||
~MidiOut() noexcept
|
||||
{
|
||||
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
|
||||
{
|
||||
midiOutClose(handle);
|
||||
}
|
||||
}
|
||||
void OutputMessage(const int b1, const int b2, const int b3 = 0, const int b4 = 0) noexcept
|
||||
{
|
||||
if constexpr (Feature_DECPSViaMidiPlayer::IsEnabled())
|
||||
{
|
||||
midiOutShortMsg(handle, MAKELONG(MAKEWORD(b1, b2), MAKEWORD(b3, b4)));
|
||||
}
|
||||
}
|
||||
|
||||
MidiOut(const MidiOut&) = delete;
|
||||
MidiOut(MidiOut&&) = delete;
|
||||
MidiOut& operator=(const MidiOut&) = delete;
|
||||
MidiOut& operator=(MidiOut&&) = delete;
|
||||
|
||||
private:
|
||||
HMIDIOUT handle = nullptr;
|
||||
};
|
||||
}
|
||||
#pragma comment(lib, "dxguid.lib")
|
||||
#pragma comment(lib, "dsound.lib")
|
||||
|
||||
using Microsoft::WRL::ComPtr;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// The WAVE_DATA below is an 8-bit PCM encoding of a triangle wave form.
|
||||
// We just play this on repeat at varying frequencies to produce our notes.
|
||||
constexpr auto WAVE_SIZE = 16u;
|
||||
constexpr auto WAVE_DATA = std::array<byte, WAVE_SIZE>{ 128, 159, 191, 223, 255, 223, 191, 159, 128, 96, 64, 32, 0, 32, 64, 96 };
|
||||
|
||||
MidiAudio::MidiAudio(HWND windowHandle)
|
||||
{
|
||||
if (SUCCEEDED(DirectSoundCreate8(nullptr, &_directSound, nullptr)))
|
||||
{
|
||||
if (SUCCEEDED(_directSound->SetCooperativeLevel(windowHandle, DSSCL_NORMAL)))
|
||||
{
|
||||
_createBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MidiAudio::~MidiAudio() noexcept
|
||||
{
|
||||
try
|
||||
@@ -61,7 +36,7 @@ MidiAudio::~MidiAudio() noexcept
|
||||
#pragma warning(suppress : 26447)
|
||||
// We acquire the lock here so the class isn't destroyed while in use.
|
||||
// If this throws, we'll catch it, so the C26447 warning is bogus.
|
||||
_inUseMutex.lock();
|
||||
const auto lock = std::unique_lock{ _inUseMutex };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -103,13 +78,26 @@ void MidiAudio::Unlock()
|
||||
void MidiAudio::PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept
|
||||
try
|
||||
{
|
||||
// The MidiOut is a local static because we can only have one instance,
|
||||
// and we only want to construct it when it's actually needed.
|
||||
static MidiOut midiOut;
|
||||
|
||||
if (velocity)
|
||||
const auto& buffer = _buffers.at(_activeBufferIndex);
|
||||
if (velocity && buffer)
|
||||
{
|
||||
midiOut.OutputMessage(MidiOut::NOTE_ON, noteNumber, velocity);
|
||||
// The formula for frequency is 2^(n/12) * 440Hz, where n is zero for
|
||||
// the A above middle C (A4). In MIDI terms, A4 is note number 69,
|
||||
// which is why we subtract 69. We also need to multiply by the size
|
||||
// of the wave form to determine the frequency that the sound buffer
|
||||
// has to be played to achieve the equivalent note frequency.
|
||||
const auto frequency = std::pow(2.0, (noteNumber - 69.0) / 12.0) * 440.0 * WAVE_SIZE;
|
||||
buffer->SetFrequency(gsl::narrow_cast<DWORD>(frequency));
|
||||
// For the volume, we're using the formula defined in the the General
|
||||
// MIDI Level 2 specification: Gain in dB = 40 * log10(v/127). We need
|
||||
// to multiply by 4000, though, because the SetVolume method expects
|
||||
// the volume to be in hundredths of a decibel.
|
||||
const auto volume = 4000.0 * std::log10(velocity / 127.0);
|
||||
buffer->SetVolume(gsl::narrow_cast<LONG>(volume));
|
||||
// Resetting the buffer to a position that is slightly off from the
|
||||
// last position will help to produce a clearer separation between
|
||||
// tones when repeating sequences of the same note.
|
||||
buffer->SetCurrentPosition((_lastBufferPosition + 12) % WAVE_SIZE);
|
||||
}
|
||||
|
||||
// By waiting on the shutdown future with the duration of the note, we'll
|
||||
@@ -117,9 +105,48 @@ try
|
||||
// of the wait early if we've been shutdown.
|
||||
_shutdownFuture.wait_for(duration);
|
||||
|
||||
if (velocity)
|
||||
if (velocity && buffer)
|
||||
{
|
||||
midiOut.OutputMessage(MidiOut::NOTE_OFF, noteNumber, velocity);
|
||||
// When the note ends, we just turn the volume down instead of stopping
|
||||
// the sound buffer. This helps reduce unwanted static between notes.
|
||||
buffer->SetVolume(DSBVOLUME_MIN);
|
||||
buffer->GetCurrentPosition(&_lastBufferPosition, nullptr);
|
||||
}
|
||||
|
||||
// Cycling between multiple buffers can also help reduce the static.
|
||||
_activeBufferIndex = (_activeBufferIndex + 1) % _buffers.size();
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
void MidiAudio::_createBuffers() noexcept
|
||||
{
|
||||
auto waveFormat = WAVEFORMATEX{};
|
||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
waveFormat.nChannels = 1;
|
||||
waveFormat.nSamplesPerSec = 8000;
|
||||
waveFormat.wBitsPerSample = 8;
|
||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||
|
||||
auto bufferDescription = DSBUFFERDESC{};
|
||||
bufferDescription.dwSize = sizeof(DSBUFFERDESC);
|
||||
bufferDescription.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS;
|
||||
bufferDescription.dwBufferBytes = WAVE_SIZE;
|
||||
bufferDescription.lpwfxFormat = &waveFormat;
|
||||
|
||||
for (auto& buffer : _buffers)
|
||||
{
|
||||
if (SUCCEEDED(_directSound->CreateSoundBuffer(&bufferDescription, &buffer, nullptr)))
|
||||
{
|
||||
LPVOID bufferPtr;
|
||||
DWORD bufferSize;
|
||||
if (SUCCEEDED(buffer->Lock(0, 0, &bufferPtr, &bufferSize, nullptr, nullptr, DSBLOCK_ENTIREBUFFER)))
|
||||
{
|
||||
std::memcpy(bufferPtr, WAVE_DATA.data(), WAVE_DATA.size());
|
||||
buffer->Unlock(bufferPtr, bufferSize, nullptr, 0);
|
||||
}
|
||||
buffer->SetVolume(DSBVOLUME_MIN);
|
||||
buffer->Play(0, 0, DSBPLAY_LOOPING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,17 @@ Abstract:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
|
||||
struct IDirectSound8;
|
||||
struct IDirectSoundBuffer;
|
||||
|
||||
class MidiAudio
|
||||
{
|
||||
public:
|
||||
MidiAudio() = default;
|
||||
MidiAudio(HWND windowHandle);
|
||||
MidiAudio(const MidiAudio&) = delete;
|
||||
MidiAudio(MidiAudio&&) = delete;
|
||||
MidiAudio& operator=(const MidiAudio&) = delete;
|
||||
@@ -30,6 +34,12 @@ public:
|
||||
void PlayNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) noexcept;
|
||||
|
||||
private:
|
||||
void _createBuffers() noexcept;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDirectSound8> _directSound;
|
||||
std::array<Microsoft::WRL::ComPtr<IDirectSoundBuffer>, 2> _buffers;
|
||||
size_t _activeBufferIndex = 0;
|
||||
DWORD _lastBufferPosition = 0;
|
||||
std::promise<void> _shutdownPromise;
|
||||
std::future<void> _shutdownFuture;
|
||||
std::mutex _inUseMutex;
|
||||
|
||||
2
src/audio/midi/dirs
Normal file
2
src/audio/midi/dirs
Normal file
@@ -0,0 +1,2 @@
|
||||
DIRS=lib \
|
||||
|
||||
8
src/audio/midi/lib/sources
Normal file
8
src/audio/midi/lib/sources
Normal file
@@ -0,0 +1,8 @@
|
||||
!include ..\sources.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Program Information
|
||||
# -------------------------------------
|
||||
|
||||
TARGETNAME = ConAudioMidi
|
||||
TARGETTYPE = LIBRARY
|
||||
@@ -26,5 +26,6 @@ Abstract:
|
||||
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
#include <mmeapi.h>
|
||||
|
||||
// clang-format on
|
||||
|
||||
28
src/audio/midi/sources.inc
Normal file
28
src/audio/midi/sources.inc
Normal file
@@ -0,0 +1,28 @@
|
||||
!include ..\..\..\project.inc
|
||||
|
||||
# -------------------------------------
|
||||
# Windows Console
|
||||
# - Console Audio Functions
|
||||
# -------------------------------------
|
||||
|
||||
# -------------------------------------
|
||||
# Build System Settings
|
||||
# -------------------------------------
|
||||
|
||||
# Code in the OneCore depot automatically excludes default Win32 libraries.
|
||||
|
||||
# -------------------------------------
|
||||
# Sources, Headers, and Libraries
|
||||
# -------------------------------------
|
||||
|
||||
PRECOMPILED_CXX = 1
|
||||
PRECOMPILED_INCLUDE = ..\precomp.h
|
||||
|
||||
SOURCES = \
|
||||
..\MidiAudio.cpp \
|
||||
|
||||
INCLUDES = \
|
||||
$(INCLUDES); \
|
||||
..; \
|
||||
..\..\..\inc; \
|
||||
$(MINWIN_INTERNAL_PRIV_SDK_INC_PATH_L); \
|
||||
@@ -1138,7 +1138,7 @@ til::point TextBuffer::GetWordStart(const til::point target, const std::wstring_
|
||||
// that it actually points to a space in the buffer
|
||||
copy = bufferSize.BottomRightInclusive();
|
||||
}
|
||||
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
else if (target >= limit)
|
||||
{
|
||||
// if at/past the limit --> clamp to limit
|
||||
copy = limitOptional.value_or(bufferSize.BottomRightInclusive());
|
||||
@@ -1254,7 +1254,7 @@ til::point TextBuffer::GetWordEnd(const til::point target, const std::wstring_vi
|
||||
// Already at/past the limit. Can't move forward.
|
||||
const auto bufferSize{ GetSize() };
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
if (target >= limit)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
@@ -1282,7 +1282,7 @@ til::point TextBuffer::_GetWordEndForAccessibility(const til::point target, cons
|
||||
const auto bufferSize{ GetSize() };
|
||||
auto result{ target };
|
||||
|
||||
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
|
||||
if (target >= limit)
|
||||
{
|
||||
// if we're already on/past the last RegularChar,
|
||||
// clamp result to that position
|
||||
@@ -1419,7 +1419,7 @@ bool TextBuffer::MoveToNextWord(til::point& pos, const std::wstring_view wordDel
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
|
||||
|
||||
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
|
||||
if (copy >= limit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1466,7 +1466,7 @@ til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::po
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
|
||||
// Clamp pos to limit
|
||||
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
|
||||
if (resultPos > limit)
|
||||
{
|
||||
resultPos = limit;
|
||||
}
|
||||
@@ -1494,7 +1494,7 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
|
||||
// Clamp pos to limit
|
||||
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
|
||||
if (resultPos > limit)
|
||||
{
|
||||
resultPos = limit;
|
||||
}
|
||||
@@ -1524,9 +1524,19 @@ til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode,
|
||||
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
const auto bufferSize = GetSize();
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
bool pastEndInclusive;
|
||||
til::point limit;
|
||||
{
|
||||
// if the limit is past the end of the buffer,
|
||||
// 1) clamp limit to end of buffer
|
||||
// 2) set pastEndInclusive
|
||||
const auto endInclusive{ bufferSize.BottomRightInclusive() };
|
||||
const auto val = limitOptional.value_or(endInclusive);
|
||||
pastEndInclusive = val > endInclusive;
|
||||
limit = pastEndInclusive ? endInclusive : val;
|
||||
}
|
||||
|
||||
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
|
||||
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit) + (pastEndInclusive ? 1 : 0) };
|
||||
if (distanceToLimit >= 0)
|
||||
{
|
||||
// Corner Case: we're on/past the limit
|
||||
@@ -1569,7 +1579,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
|
||||
const auto bufferSize = GetSize();
|
||||
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
|
||||
|
||||
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
|
||||
if (pos > limit)
|
||||
{
|
||||
// we're past the end
|
||||
// clamp us to the limit
|
||||
@@ -1611,7 +1621,7 @@ const std::vector<til::inclusive_rect> TextBuffer::GetTextRects(til::point start
|
||||
// (0,0) is the top-left of the screen
|
||||
// the physically "higher" coordinate is closer to the top-left
|
||||
// the physically "lower" coordinate is closer to the bottom-right
|
||||
const auto [higherCoord, lowerCoord] = bufferSize.CompareInBounds(start, end) <= 0 ?
|
||||
const auto [higherCoord, lowerCoord] = start <= end ?
|
||||
std::make_tuple(start, end) :
|
||||
std::make_tuple(end, start);
|
||||
|
||||
|
||||
@@ -135,6 +135,10 @@
|
||||
<!-- 16.3.0 - remove non-resources.pri PRI files since we just forced them back in. -->
|
||||
<AppxPackagePayload Remove="@(AppxPackagePayload)" Condition="'%(Extension)' == '.pri' and '%(Filename)' != 'resources'" />
|
||||
<AppxUploadPackagePayload Remove="@(AppxUploadPackagePayload)" Condition="'%(Extension)' == '.pri' and '%(Filename)' != 'resources'" />
|
||||
|
||||
<!-- Remove all of the xaml files, because we are using embedded xbf payloads (saves about 500kb on disk!) -->
|
||||
<AppxPackagePayload Remove="@(AppxPackagePayload)" Condition="'%(Extension)' == '.xaml'" />
|
||||
<AppxUploadPackagePayload Remove="@(AppxUploadPackagePayload)" Condition="'%(Extension)' == '.xaml'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<ClCompile Include="DeserializationTests.cpp" />
|
||||
<ClCompile Include="SerializationTests.cpp" />
|
||||
<ClCompile Include="TerminalSettingsTests.cpp" />
|
||||
<ClCompile Include="ThemeTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
||||
281
src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
Normal file
281
src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalSettingsModel/Theme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../types/inc/colorTable.hpp"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
#include <defaults.h>
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
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 ThemeTests : 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(ThemeTests)
|
||||
TEST_CLASS_PROPERTY(L"RunAs", L"UAP")
|
||||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(ParseSimpleTheme);
|
||||
TEST_METHOD(ParseEmptyTheme);
|
||||
TEST_METHOD(ParseNoWindowTheme);
|
||||
TEST_METHOD(ParseNullWindowTheme);
|
||||
TEST_METHOD(ParseThemeWithNullThemeColor);
|
||||
TEST_METHOD(InvalidCurrentTheme);
|
||||
|
||||
static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
|
||||
{
|
||||
return Core::Color{ r, g, b, 255 };
|
||||
}
|
||||
static Core::Color rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
return Core::Color{ r, g, b, a };
|
||||
}
|
||||
};
|
||||
|
||||
void ThemeTests::ParseSimpleTheme()
|
||||
{
|
||||
static constexpr std::string_view orangeTheme{ R"({
|
||||
"name": "orange",
|
||||
"tabRow":
|
||||
{
|
||||
"background": "#FFFF8800",
|
||||
"unfocusedBackground": "#FF8844"
|
||||
},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(orangeTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"orange", theme->Name());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow());
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
|
||||
VERIFY_ARE_EQUAL(rgba(0xff, 0xff, 0x88, 0x00), theme->TabRow().Background().Color());
|
||||
VERIFY_ARE_EQUAL(rgba(0xff, 0x88, 0x44, 0xff), theme->TabRow().UnfocusedBackground().Color());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Light, theme->Window().RequestedTheme());
|
||||
VERIFY_ARE_EQUAL(true, theme->Window().UseMica());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseEmptyTheme()
|
||||
{
|
||||
Log::Comment(L"This theme doesn't have any elements defined.");
|
||||
static constexpr std::string_view emptyTheme{ R"({
|
||||
"name": "empty"
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(emptyTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"empty", theme->Name());
|
||||
VERIFY_IS_NULL(theme->TabRow());
|
||||
VERIFY_IS_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseNoWindowTheme()
|
||||
{
|
||||
Log::Comment(L"This theme doesn't have a window defined.");
|
||||
static constexpr std::string_view emptyTheme{ R"({
|
||||
"name": "noWindow",
|
||||
"tabRow":
|
||||
{
|
||||
"background": "#112233",
|
||||
"unfocusedBackground": "#FF884400"
|
||||
},
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(emptyTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"noWindow", theme->Name());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow());
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
|
||||
VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());
|
||||
|
||||
VERIFY_IS_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseNullWindowTheme()
|
||||
{
|
||||
Log::Comment(L"This theme doesn't have a window defined.");
|
||||
static constexpr std::string_view emptyTheme{ R"({
|
||||
"name": "nullWindow",
|
||||
"tabRow":
|
||||
{
|
||||
"background": "#112233",
|
||||
"unfocusedBackground": "#FF884400"
|
||||
},
|
||||
"window": null
|
||||
})" };
|
||||
|
||||
const auto schemeObject = VerifyParseSucceeded(emptyTheme);
|
||||
auto theme = Theme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"nullWindow", theme->Name());
|
||||
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow());
|
||||
VERIFY_IS_NOT_NULL(theme->TabRow().Background());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::ThemeColorType::Color, theme->TabRow().Background().ColorType());
|
||||
VERIFY_ARE_EQUAL(rgb(0x11, 0x22, 0x33), theme->TabRow().Background().Color());
|
||||
|
||||
VERIFY_IS_NULL(theme->Window());
|
||||
VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Default, theme->RequestedTheme());
|
||||
}
|
||||
|
||||
void ThemeTests::ParseThemeWithNullThemeColor()
|
||||
{
|
||||
Log::Comment(L"These themes are all missing a tabRow background. Make sure we don't somehow default-construct one for them");
|
||||
|
||||
static constexpr std::string_view settingsString{ R"json({
|
||||
"themes": [
|
||||
{
|
||||
"name": "backgroundEmpty",
|
||||
"tabRow":
|
||||
{
|
||||
},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "backgroundNull",
|
||||
"tabRow":
|
||||
{
|
||||
"background": null
|
||||
},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "backgroundOmittedEntirely",
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
}
|
||||
]
|
||||
})json" };
|
||||
|
||||
try
|
||||
{
|
||||
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };
|
||||
|
||||
const auto& themes{ settings->GlobalSettings().Themes() };
|
||||
{
|
||||
const auto& backgroundEmpty{ themes.Lookup(L"backgroundEmpty") };
|
||||
VERIFY_ARE_EQUAL(L"backgroundEmpty", backgroundEmpty.Name());
|
||||
VERIFY_IS_NOT_NULL(backgroundEmpty.TabRow());
|
||||
VERIFY_IS_NULL(backgroundEmpty.TabRow().Background());
|
||||
}
|
||||
{
|
||||
const auto& backgroundNull{ themes.Lookup(L"backgroundNull") };
|
||||
VERIFY_ARE_EQUAL(L"backgroundNull", backgroundNull.Name());
|
||||
VERIFY_IS_NOT_NULL(backgroundNull.TabRow());
|
||||
VERIFY_IS_NULL(backgroundNull.TabRow().Background());
|
||||
}
|
||||
{
|
||||
const auto& backgroundOmittedEntirely{ themes.Lookup(L"backgroundOmittedEntirely") };
|
||||
VERIFY_ARE_EQUAL(L"backgroundOmittedEntirely", backgroundOmittedEntirely.Name());
|
||||
VERIFY_IS_NULL(backgroundOmittedEntirely.TabRow());
|
||||
}
|
||||
}
|
||||
catch (const SettingsException& ex)
|
||||
{
|
||||
auto loadError = ex.Error();
|
||||
loadError;
|
||||
throw ex;
|
||||
}
|
||||
catch (const SettingsTypedDeserializationException& e)
|
||||
{
|
||||
auto deserializationErrorMessage = til::u8u16(e.what());
|
||||
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
void ThemeTests::InvalidCurrentTheme()
|
||||
{
|
||||
Log::Comment(L"Make sure specifying an invalid theme falls back to a sensible default.");
|
||||
|
||||
static constexpr std::string_view settingsString{ R"json({
|
||||
"theme": "foo",
|
||||
"themes": [
|
||||
{
|
||||
"name": "bar",
|
||||
"tabRow": {},
|
||||
"window":
|
||||
{
|
||||
"applicationTheme": "light",
|
||||
"useMica": true
|
||||
}
|
||||
}
|
||||
]
|
||||
})json" };
|
||||
|
||||
try
|
||||
{
|
||||
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString, DefaultJson) };
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings->Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(Settings::Model::SettingsLoadWarnings::UnknownTheme, settings->Warnings().GetAt(0));
|
||||
|
||||
const auto& themes{ settings->GlobalSettings().Themes() };
|
||||
{
|
||||
const auto& bar{ themes.Lookup(L"bar") };
|
||||
VERIFY_ARE_EQUAL(L"bar", bar.Name());
|
||||
VERIFY_IS_NOT_NULL(bar.TabRow());
|
||||
VERIFY_IS_NULL(bar.TabRow().Background());
|
||||
}
|
||||
|
||||
const auto currentTheme{ settings->GlobalSettings().CurrentTheme() };
|
||||
VERIFY_IS_NOT_NULL(currentTheme);
|
||||
VERIFY_ARE_EQUAL(L"system", currentTheme.Name());
|
||||
}
|
||||
catch (const SettingsException& ex)
|
||||
{
|
||||
auto loadError = ex.Error();
|
||||
loadError;
|
||||
throw ex;
|
||||
}
|
||||
catch (const SettingsTypedDeserializationException& e)
|
||||
{
|
||||
auto deserializationErrorMessage = til::u8u16(e.what());
|
||||
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,6 @@
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\dx\lib\dx.vcxproj">
|
||||
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj">
|
||||
<Project>{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
|
||||
@@ -1112,4 +1112,14 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleSwitchSelectionEndpoint(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
const auto handled = control.SwitchSelectionEndpoint();
|
||||
args.Handled(handled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ static const std::array settingsLoadWarningsLabels {
|
||||
USES_RESOURCE(L"InvalidSplitSize"),
|
||||
USES_RESOURCE(L"FailedToParseStartupActions"),
|
||||
USES_RESOURCE(L"FailedToParseSubCommands"),
|
||||
USES_RESOURCE(L"UnknownTheme"),
|
||||
};
|
||||
static const std::array settingsLoadErrorsLabels {
|
||||
USES_RESOURCE(L"NoProfilesText"),
|
||||
@@ -292,7 +293,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_root->Maximized(true);
|
||||
}
|
||||
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode))
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow())
|
||||
{
|
||||
_root->SetFullscreen(true);
|
||||
}
|
||||
@@ -367,11 +368,12 @@ 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().CurrentTheme() };
|
||||
auto requestedTheme{ theme.RequestedTheme() };
|
||||
auto element{ sender.try_as<winrt::Windows::UI::Xaml::FrameworkElement>() };
|
||||
while (element)
|
||||
{
|
||||
element.RequestedTheme(theme);
|
||||
element.RequestedTheme(requestedTheme);
|
||||
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
|
||||
}
|
||||
} };
|
||||
@@ -737,13 +739,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().Theme();
|
||||
return Theme().RequestedTheme();
|
||||
}
|
||||
|
||||
bool AppLogic::GetShowTabsInTitlebar()
|
||||
@@ -962,9 +958,16 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void AppLogic::_RefreshThemeRoutine()
|
||||
{
|
||||
_ApplyTheme(_settings.GlobalSettings().Theme());
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_RequestedThemeChangedHandlers(*this, Theme());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
@@ -1073,18 +1076,6 @@ namespace winrt::TerminalApp::implementation
|
||||
return _settings;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void AppLogic::_ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme)
|
||||
{
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_RequestedThemeChangedHandlers(*this, newTheme);
|
||||
}
|
||||
|
||||
UIElement AppLogic::GetRoot() noexcept
|
||||
{
|
||||
return _root.as<winrt::Windows::UI::Xaml::Controls::Control>();
|
||||
@@ -1219,6 +1210,19 @@ namespace winrt::TerminalApp::implementation
|
||||
return {};
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush AppLogic::TitlebarBrush()
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
return _root->TitlebarBrush();
|
||||
}
|
||||
return { nullptr };
|
||||
}
|
||||
void AppLogic::WindowActivated(const bool activated)
|
||||
{
|
||||
_root->WindowActivated(activated);
|
||||
}
|
||||
|
||||
bool AppLogic::HasCommandlineArguments() const noexcept
|
||||
{
|
||||
return _hasCommandLineArguments;
|
||||
@@ -1496,6 +1500,17 @@ namespace winrt::TerminalApp::implementation
|
||||
return _root ? _root->AlwaysOnTop() : false;
|
||||
}
|
||||
|
||||
bool AppLogic::AutoHideWindow()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _settings.GlobalSettings().AutoHideWindow();
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> AppLogic::GlobalHotkeys()
|
||||
{
|
||||
return _settings.GlobalSettings().ActionMap().GlobalHotkeys();
|
||||
@@ -1645,4 +1660,15 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return _settings.GlobalSettings().ShowTitleInTitlebar();
|
||||
}
|
||||
|
||||
Microsoft::Terminal::Settings::Model::Theme AppLogic::Theme()
|
||||
{
|
||||
if (!_loadedInitialSettings)
|
||||
{
|
||||
// Load settings if we haven't already
|
||||
LoadSettings();
|
||||
}
|
||||
return _settings.GlobalSettings().CurrentTheme();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool Fullscreen() const;
|
||||
void Maximized(bool newMaximized);
|
||||
bool AlwaysOnTop() const;
|
||||
bool AutoHideWindow();
|
||||
|
||||
bool ShouldUsePersistedLayout();
|
||||
bool ShouldImmediatelyHandoffToElevated();
|
||||
@@ -117,6 +118,8 @@ namespace winrt::TerminalApp::implementation
|
||||
void WindowVisibilityChanged(const bool showOrHide);
|
||||
|
||||
winrt::TerminalApp::TaskbarState TaskbarState();
|
||||
winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush();
|
||||
void WindowActivated(const bool activated);
|
||||
|
||||
bool GetMinimizeToNotificationArea();
|
||||
bool GetAlwaysShowNotificationIcon();
|
||||
@@ -127,8 +130,19 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
Windows::Foundation::Collections::IMapView<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::Command> GlobalHotkeys();
|
||||
|
||||
Microsoft::Terminal::Settings::Model::Theme Theme();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
// PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
|
||||
// Usually we'd just do
|
||||
// WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
//
|
||||
// But what we're doing here is exposing the Page's PropertyChanged _as
|
||||
// our own event_. It's a FORWARDED_CALLBACK, essentially.
|
||||
winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); }
|
||||
void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); }
|
||||
|
||||
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme);
|
||||
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
|
||||
|
||||
@@ -183,8 +197,6 @@ namespace winrt::TerminalApp::implementation
|
||||
void _ReloadSettings();
|
||||
void _OpenSettingsUI();
|
||||
|
||||
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
|
||||
|
||||
bool _hasCommandLineArguments{ false };
|
||||
bool _hasSettingsStartupActions{ false };
|
||||
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> _warnings;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace TerminalApp
|
||||
|
||||
// See IDialogPresenter and TerminalPage's DialogPresenter for more
|
||||
// information.
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
AppLogic();
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace TerminalApp
|
||||
Boolean Fullscreen { get; };
|
||||
void Maximized(Boolean newMaximized);
|
||||
Boolean AlwaysOnTop { get; };
|
||||
Boolean AutoHideWindow { get; };
|
||||
|
||||
void IdentifyWindow();
|
||||
String WindowName;
|
||||
@@ -94,6 +95,8 @@ namespace TerminalApp
|
||||
void WindowVisibilityChanged(Boolean showOrHide);
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
|
||||
void WindowActivated(Boolean activated);
|
||||
|
||||
Boolean ShouldUsePersistedLayout();
|
||||
Boolean ShouldImmediatelyHandoffToElevated();
|
||||
@@ -105,6 +108,8 @@ namespace TerminalApp
|
||||
Boolean GetAlwaysShowNotificationIcon();
|
||||
Boolean GetShowTitleInTitlebar();
|
||||
|
||||
Microsoft.Terminal.Settings.Model.Theme Theme { get; };
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
|
||||
@@ -117,7 +122,7 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Theme> RequestedThemeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
|
||||
|
||||
@@ -525,9 +525,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// This event is called when the user clicks on an ChevronLeft button right
|
||||
// This event is called when the user clicks on a ChevronLeft button right
|
||||
// next to the ParentCommandName (e.g. New Tab...) above the subcommands list.
|
||||
// It'll go up a level when the users click the button.
|
||||
// It'll go up a single level when the user clicks the button.
|
||||
// Arguments:
|
||||
// - sender: the button that got clicked
|
||||
// Return Value:
|
||||
@@ -536,12 +536,32 @@ namespace winrt::TerminalApp::implementation
|
||||
const Windows::UI::Xaml::RoutedEventArgs&)
|
||||
{
|
||||
_PreviewActionHandlers(*this, nullptr);
|
||||
_nestedActionStack.Clear();
|
||||
ParentCommandName(L"");
|
||||
_currentNestedCommands.Clear();
|
||||
_searchBox().Focus(FocusState::Programmatic);
|
||||
|
||||
const auto previousAction{ _nestedActionStack.GetAt(_nestedActionStack.Size() - 1) };
|
||||
_nestedActionStack.RemoveAtEnd();
|
||||
|
||||
// Repopulate nested commands when the root has not been reached yet
|
||||
if (_nestedActionStack.Size() > 0)
|
||||
{
|
||||
const auto newPreviousAction{ _nestedActionStack.GetAt(_nestedActionStack.Size() - 1) };
|
||||
const auto actionPaletteItem{ newPreviousAction.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() };
|
||||
|
||||
ParentCommandName(actionPaletteItem.Command().Name());
|
||||
_updateCurrentNestedCommands(actionPaletteItem.Command());
|
||||
}
|
||||
else
|
||||
{
|
||||
ParentCommandName(L"");
|
||||
_currentNestedCommands.Clear();
|
||||
}
|
||||
_updateFilteredActions();
|
||||
_filteredActionsView().SelectedIndex(0);
|
||||
|
||||
const auto lastSelectedIt = std::find_if(begin(_filteredActions), end(_filteredActions), [&](const auto& filteredCommand) {
|
||||
return filteredCommand.Item().Name() == previousAction.Item().Name();
|
||||
});
|
||||
const auto lastSelectedIndex = static_cast<int32_t>(std::distance(begin(_filteredActions), lastSelectedIt));
|
||||
_scrollToIndex(lastSelectedIt != end(_filteredActions) ? lastSelectedIndex : 0);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -639,14 +659,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// to pick from.
|
||||
_nestedActionStack.Append(filteredCommand);
|
||||
ParentCommandName(actionPaletteItem.Command().Name());
|
||||
_currentNestedCommands.Clear();
|
||||
for (const auto& nameAndCommand : actionPaletteItem.Command().NestedCommands())
|
||||
{
|
||||
const auto action = nameAndCommand.Value();
|
||||
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
|
||||
auto nestedFilteredCommand{ winrt::make<FilteredCommand>(nestedActionPaletteItem) };
|
||||
_currentNestedCommands.Append(nestedFilteredCommand);
|
||||
}
|
||||
_updateCurrentNestedCommands(actionPaletteItem.Command());
|
||||
|
||||
_updateUIForStackChange();
|
||||
}
|
||||
@@ -1108,6 +1121,25 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the list of current nested commands to match that of the
|
||||
// given parent command.
|
||||
// Arguments:
|
||||
// - parentCommand: the command with an optional list of nested commands.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void CommandPalette::_updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand)
|
||||
{
|
||||
_currentNestedCommands.Clear();
|
||||
for (const auto& nameAndCommand : parentCommand.NestedCommands())
|
||||
{
|
||||
const auto action = nameAndCommand.Value();
|
||||
auto nestedActionPaletteItem{ winrt::make<winrt::TerminalApp::implementation::ActionPaletteItem>(action) };
|
||||
auto nestedFilteredCommand{ winrt::make<FilteredCommand>(nestedActionPaletteItem) };
|
||||
_currentNestedCommands.Append(nestedFilteredCommand);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Dismiss the command palette. This will:
|
||||
// * select all the current text in the input box
|
||||
|
||||
@@ -96,6 +96,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _updateFilteredActions();
|
||||
|
||||
void _updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand);
|
||||
|
||||
std::vector<winrt::TerminalApp::FilteredCommand> _collectFilteredActions();
|
||||
|
||||
void _close();
|
||||
|
||||
@@ -111,7 +111,15 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
||||
|
||||
void DebugTapConnection::_OutputHandler(const hstring str)
|
||||
{
|
||||
_TerminalOutputHandlers(til::visualize_control_codes(str));
|
||||
auto output = til::visualize_control_codes(str);
|
||||
// To make the output easier to read, we introduce a line break whenever
|
||||
// an LF control is encountered. But at this point, the LF would have
|
||||
// been converted to U+240A (␊), so that's what we need to search for.
|
||||
for (size_t lfPos = 0; (lfPos = output.find(L'\u240A', lfPos)) != std::wstring::npos;)
|
||||
{
|
||||
output.insert(++lfPos, L"\r\n");
|
||||
}
|
||||
_TerminalOutputHandlers(output);
|
||||
}
|
||||
|
||||
// Called by the DebugInputTapConnection to print user input
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "winrt/Microsoft.UI.Xaml.Controls.h"
|
||||
|
||||
#include "HighlightedTextSegment.g.h"
|
||||
#include "HighlightedText.g.h"
|
||||
|
||||
|
||||
@@ -20,70 +20,73 @@
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<Color x:Key="CloseButtonColor">#C42B1C</Color>
|
||||
<x:Double x:Key="CaptionButtonStrokeWidth">1.0</x:Double>
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
|
||||
ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPressed"
|
||||
ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="CaptionButtonStroke"
|
||||
<StaticResource x:Key="CaptionButtonForeground"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="CaptionButtonStrokeColor"
|
||||
<StaticResource x:Key="CaptionButtonForegroundColor"
|
||||
ResourceKey="SystemBaseHighColor" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver"
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed"
|
||||
<StaticResource x:Key="CaptionButtonForegroundPressed"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackground"
|
||||
Color="Transparent" />
|
||||
<Color x:Key="CaptionButtonBackgroundColor">Transparent</Color>
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver"
|
||||
Color="{ThemeResource CloseButtonColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePointerOver"
|
||||
<SolidColorBrush x:Key="CloseButtonForegroundPointerOver"
|
||||
Color="White" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
|
||||
Opacity="0.9"
|
||||
Color="{ThemeResource CloseButtonColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePressed"
|
||||
<SolidColorBrush x:Key="CloseButtonForegroundPressed"
|
||||
Opacity="0.7"
|
||||
Color="White" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackground"
|
||||
Color="#00e81123" />
|
||||
<Color x:Key="CloseButtonBackgroundColor">#00e81123</Color>
|
||||
Color="#00C42B1C" />
|
||||
<Color x:Key="CloseButtonBackgroundColor">#00C42B1C</Color>
|
||||
|
||||
<StaticResource x:Key="RestoreButtonGlyph"
|
||||
ResourceKey="RestoreGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<Color x:Key="CloseButtonColor">#C42B1C</Color>
|
||||
<x:Double x:Key="CaptionButtonStrokeWidth">1.0</x:Double>
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
|
||||
ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPressed"
|
||||
ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="CaptionButtonStroke"
|
||||
<StaticResource x:Key="CaptionButtonForeground"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="CaptionButtonStrokeColor"
|
||||
<StaticResource x:Key="CaptionButtonForegroundColor"
|
||||
ResourceKey="SystemBaseHighColor" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver"
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed"
|
||||
<StaticResource x:Key="CaptionButtonForegroundPressed"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackground"
|
||||
Color="Transparent" />
|
||||
<Color x:Key="CaptionButtonBackgroundColor">Transparent</Color>
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver"
|
||||
Color="{ThemeResource CloseButtonColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePointerOver"
|
||||
<SolidColorBrush x:Key="CloseButtonForegroundPointerOver"
|
||||
Color="White" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
|
||||
Opacity="0.9"
|
||||
Color="{ThemeResource CloseButtonColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePressed"
|
||||
<SolidColorBrush x:Key="CloseButtonForegroundPressed"
|
||||
Opacity="0.7"
|
||||
Color="White" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackground"
|
||||
Color="#00e81123" />
|
||||
<Color x:Key="CloseButtonBackgroundColor">#00e81123</Color>
|
||||
Color="#00C42B1C" />
|
||||
<Color x:Key="CloseButtonBackgroundColor">#00C42B1C</Color>
|
||||
|
||||
<StaticResource x:Key="RestoreButtonGlyph"
|
||||
ResourceKey="RestoreGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<x:Double x:Key="CaptionButtonStrokeWidth">2.0</x:Double>
|
||||
<SolidColorBrush x:Key="CaptionButtonBackground"
|
||||
Color="{ThemeResource SystemColorButtonFaceColor}" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundColor"
|
||||
@@ -92,32 +95,43 @@
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonBackgroundPressed"
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonStroke"
|
||||
<SolidColorBrush x:Key="CaptionButtonForeground"
|
||||
Color="{ThemeResource SystemColorButtonTextColor}" />
|
||||
<StaticResource x:Key="CaptionButtonStrokeColor"
|
||||
<StaticResource x:Key="CaptionButtonForegroundColor"
|
||||
ResourceKey="SystemColorButtonTextColor" />
|
||||
<SolidColorBrush x:Key="CaptionButtonStrokePointerOver"
|
||||
<SolidColorBrush x:Key="CaptionButtonForegroundPointerOver"
|
||||
Color="{ThemeResource SystemColorHighlightTextColor}" />
|
||||
<SolidColorBrush x:Key="CaptionButtonStrokePressed"
|
||||
<SolidColorBrush x:Key="CaptionButtonForegroundPressed"
|
||||
Color="{ThemeResource SystemColorHighlightTextColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPointerOver"
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePointerOver"
|
||||
<SolidColorBrush x:Key="CloseButtonForegroundPointerOver"
|
||||
Color="{ThemeResource SystemColorHighlightTextColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonBackgroundPressed"
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="CloseButtonStrokePressed"
|
||||
<SolidColorBrush x:Key="CloseButtonForegroundPressed"
|
||||
Color="{ThemeResource SystemColorHighlightTextColor}" />
|
||||
|
||||
<StaticResource x:Key="RestoreButtonGlyph"
|
||||
ResourceKey="RestoreGlyphContrast" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!--
|
||||
These strings need to be initialized to something, so they're
|
||||
just initialized to the path for the close button. Each specific
|
||||
button should override them as needed.
|
||||
Initializes the string to the close button glyph.
|
||||
Each specific button overrides it as needed.
|
||||
-->
|
||||
<x:String x:Key="CaptionButtonPath">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
|
||||
<x:String x:Key="CaptionButtonPathWindowMaximized">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
|
||||
<x:String x:Key="CaptionButtonGlyph"></x:String>
|
||||
|
||||
<x:String x:Key="MinimizeGlyph"></x:String>
|
||||
<x:String x:Key="MaximizeGlyph"></x:String>
|
||||
<x:String x:Key="RestoreGlyph"></x:String>
|
||||
<x:String x:Key="CloseGlyph"></x:String>
|
||||
|
||||
<x:String x:Key="MinimizeGlyphContrast"></x:String>
|
||||
<x:String x:Key="MaximizeGlyphContrast"></x:String>
|
||||
<x:String x:Key="RestoreGlyphContrast"></x:String>
|
||||
<x:String x:Key="CloseGlyphContrast"></x:String>
|
||||
|
||||
<!--
|
||||
"CaptionButtonHeightWindowed" and
|
||||
@@ -151,16 +165,13 @@
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<Path x:Name="Path"
|
||||
Width="10"
|
||||
Height="10"
|
||||
Data="{ThemeResource CaptionButtonPath}"
|
||||
Stretch="Fill"
|
||||
Stroke="{ThemeResource CaptionButtonStroke}"
|
||||
StrokeEndLineCap="Square"
|
||||
StrokeStartLineCap="Square"
|
||||
StrokeThickness="{ThemeResource CaptionButtonStrokeWidth}"
|
||||
UseLayoutRounding="True" />
|
||||
<Viewbox Width="10"
|
||||
Height="10">
|
||||
<FontIcon x:Name="ButtonIcon"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Foreground="{ThemeResource CaptionButtonForeground}"
|
||||
Glyph="{ThemeResource CaptionButtonGlyph}" />
|
||||
</Viewbox>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
@@ -172,9 +183,9 @@
|
||||
Storyboard.TargetProperty="(UIElement.Background).(SolidColorBrush.Color)"
|
||||
To="{ThemeResource CaptionButtonBackgroundColor}"
|
||||
Duration="0:0:0.15" />
|
||||
<ColorAnimation Storyboard.TargetName="Path"
|
||||
Storyboard.TargetProperty="(UIElement.Stroke).(SolidColorBrush.Color)"
|
||||
To="{ThemeResource CaptionButtonStrokeColor}"
|
||||
<ColorAnimation Storyboard.TargetName="ButtonIcon"
|
||||
Storyboard.TargetProperty="(UIElement.Foreground).(SolidColorBrush.Color)"
|
||||
To="{ThemeResource CaptionButtonForegroundColor}"
|
||||
Duration="0:0:0.1" />
|
||||
</Storyboard>
|
||||
</VisualTransition>
|
||||
@@ -183,21 +194,21 @@
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackground}" />
|
||||
<Setter Target="Path.Stroke" Value="{ThemeResource CaptionButtonStroke}" />
|
||||
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForeground}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackgroundPointerOver}" />
|
||||
<Setter Target="Path.Stroke" Value="{ThemeResource CaptionButtonStrokePointerOver}" />
|
||||
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ButtonBaseElement.Background" Value="{ThemeResource CaptionButtonBackgroundPressed}" />
|
||||
<Setter Target="Path.Stroke" Value="{ThemeResource CaptionButtonStrokePressed}" />
|
||||
<Setter Target="ButtonIcon.Foreground" Value="{ThemeResource CaptionButtonForegroundPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
@@ -209,7 +220,7 @@
|
||||
|
||||
<VisualState x:Name="WindowStateMaximized">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Path.Data" Value="{ThemeResource CaptionButtonPathWindowMaximized}" />
|
||||
<Setter Target="ButtonIcon.Glyph" Value="{ThemeResource RestoreButtonGlyph}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -235,7 +246,20 @@
|
||||
Style="{StaticResource CaptionButton}">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
<x:String x:Key="CaptionButtonPath">M 0 0 H 10</x:String>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="MinimizeGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="MinimizeGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="MinimizeGlyphContrast" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<ToolTipService.ToolTip>
|
||||
@@ -253,10 +277,21 @@
|
||||
Click="_MaximizeClick"
|
||||
Style="{StaticResource CaptionButton}">
|
||||
<Button.Resources>
|
||||
<!-- These paths are complicated, but taken directly from WinUI's WindowCaptionButton paths. -->
|
||||
<ResourceDictionary>
|
||||
<x:String x:Key="CaptionButtonPath">M 1.516 -0.001 L 7.451 0.009 C 8.751 0.019 9 1 8.981 1.477 L 9.002 7.558 M 9.002 7.547 C 8.929 8.669 8 9 7.43 9.015 L 1.464 9.005 C 0.374 8.973 0 8 -0.004 7.484 L -0.004 1.477 C 0 1 0.415 0.009 1.527 -0.001</x:String>
|
||||
<x:String x:Key="CaptionButtonPathWindowMaximized">M 1.516 -0.001 L 7.451 0.009 C 8.751 0.019 9 1 8.981 1.477 L 9.002 7.558 M 11 6 L 11 2 C 11 0 10 -2 8.011 -1.946 L 7.06 -1.969 L 3 -2 M 9.002 7.547 C 8.929 8.669 8 9 7.43 9.015 L 1.464 9.005 C 0.374 8.973 0 8 -0.004 7.484 L -0.004 1.477 C 0 1 0.415 0.009 1.527 -0.001</x:String>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="MaximizeGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="MaximizeGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="MaximizeGlyphContrast" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<ToolTipService.ToolTip>
|
||||
@@ -283,42 +318,46 @@
|
||||
ResourceKey="CloseButtonBackgroundPointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPressed"
|
||||
ResourceKey="CloseButtonBackgroundPressed" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver"
|
||||
ResourceKey="CloseButtonStrokePointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed"
|
||||
ResourceKey="CloseButtonStrokePressed" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="CloseButtonForegroundPointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPressed"
|
||||
ResourceKey="CloseButtonForegroundPressed" />
|
||||
<StaticResource x:Key="CaptionButtonBackground"
|
||||
ResourceKey="CloseButtonBackground" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundColor"
|
||||
ResourceKey="CloseButtonBackgroundColor" />
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="CloseGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
|
||||
ResourceKey="CloseButtonBackgroundPointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPressed"
|
||||
ResourceKey="CloseButtonBackgroundPressed" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver"
|
||||
ResourceKey="CloseButtonStrokePointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed"
|
||||
ResourceKey="CloseButtonStrokePressed" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="CloseButtonForegroundPointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPressed"
|
||||
ResourceKey="CloseButtonForegroundPressed" />
|
||||
<StaticResource x:Key="CaptionButtonBackground"
|
||||
ResourceKey="CloseButtonBackground" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundColor"
|
||||
ResourceKey="CloseButtonBackgroundColor" />
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="CloseGlyph" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPointerOver"
|
||||
ResourceKey="CloseButtonBackgroundPointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonBackgroundPressed"
|
||||
ResourceKey="CloseButtonBackgroundPressed" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePointerOver"
|
||||
ResourceKey="CloseButtonStrokePointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonStrokePressed"
|
||||
ResourceKey="CloseButtonStrokePressed" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPointerOver"
|
||||
ResourceKey="CloseButtonForegroundPointerOver" />
|
||||
<StaticResource x:Key="CaptionButtonForegroundPressed"
|
||||
ResourceKey="CloseButtonForegroundPressed" />
|
||||
<StaticResource x:Key="CaptionButtonGlyph"
|
||||
ResourceKey="CloseGlyphContrast" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<x:String x:Key="CaptionButtonPath">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
<ToolTipService.ToolTip>
|
||||
|
||||
@@ -242,6 +242,10 @@
|
||||
<value>• Found a keybinding that was missing a required parameter value. This keybinding will be ignored.</value>
|
||||
<comment>{Locked="•"} This glyph is a bullet, used in a bulleted list.</comment>
|
||||
</data>
|
||||
<data name="UnknownTheme" xml:space="preserve">
|
||||
<value>• The specified "theme" was not found in the list of themes. Temporarily falling back to the default value.</value>
|
||||
<comment>{Locked="•"} This glyph is a bullet, used in a bulleted list.</comment>
|
||||
</data>
|
||||
<data name="LegacyGlobalsProperty" xml:space="preserve">
|
||||
<value>The "globals" property is deprecated - your settings might need updating. </value>
|
||||
<comment>{Locked="\"globals\""} </comment>
|
||||
@@ -432,6 +436,9 @@
|
||||
<data name="AboutDialog.Title" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="AboutDialog.PrimaryButtonText" xml:space="preserve">
|
||||
<value>Send Feedback</value>
|
||||
</data>
|
||||
<data name="AboutDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
</data>
|
||||
@@ -443,6 +450,10 @@
|
||||
<value>Getting Started</value>
|
||||
<comment>A hyperlink name for a guide on how to get started using Terminal</comment>
|
||||
</data>
|
||||
<data name="AboutDialog_SourceCodeLink.Content" xml:space="preserve">
|
||||
<value>Source Code</value>
|
||||
<comment>A hyperlink name for the Terminal's documentation</comment>
|
||||
</data>
|
||||
<data name="AboutDialog_DocumentationLink.Content" xml:space="preserve">
|
||||
<value>Documentation</value>
|
||||
<comment>A hyperlink name for user documentation</comment>
|
||||
|
||||
@@ -125,8 +125,18 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
newTabImpl->Initialize();
|
||||
|
||||
uint32_t insertPosition = _tabs.Size();
|
||||
if (_settings.GlobalSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
|
||||
{
|
||||
auto currentTabIndex = _GetFocusedTabIndex();
|
||||
if (currentTabIndex.has_value())
|
||||
{
|
||||
insertPosition = currentTabIndex.value() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new tab to the list of our tabs.
|
||||
_tabs.Append(*newTabImpl);
|
||||
_tabs.InsertAt(insertPosition, *newTabImpl);
|
||||
_mruTabs.Append(*newTabImpl);
|
||||
|
||||
newTabImpl->SetDispatch(*_actionDispatch);
|
||||
@@ -154,6 +164,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// Possibly update the icon of the tab.
|
||||
page->_UpdateTabIcon(*tab);
|
||||
|
||||
page->_updateThemeColors();
|
||||
|
||||
// Update the taskbar progress as well. We'll raise our own
|
||||
// SetTaskbarProgress event here, to get tell the hosting
|
||||
// application to re-query this value from us.
|
||||
@@ -220,7 +232,7 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
_tabView.TabItems().InsertAt(insertPosition, tabViewItem);
|
||||
|
||||
// Set this tab's icon to the icon from the user's profile
|
||||
if (const auto profile{ newTabImpl->GetFocusedProfile() })
|
||||
@@ -925,6 +937,8 @@ namespace winrt::TerminalApp::implementation
|
||||
_TitleChangedHandlers(*this, tab.Title());
|
||||
}
|
||||
|
||||
_updateThemeColors();
|
||||
|
||||
auto tab_impl = _GetTerminalTabImpl(tab);
|
||||
if (tab_impl)
|
||||
{
|
||||
|
||||
@@ -60,7 +60,8 @@
|
||||
FontSize="12">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Placement="Mouse">
|
||||
<TextBlock IsTextSelectionEnabled="False">
|
||||
<TextBlock IsTextSelectionEnabled="False"
|
||||
TextWrapping="Wrap">
|
||||
<Run x:Uid="NewTabRun" /> <LineBreak />
|
||||
<Run x:Uid="NewPaneRun"
|
||||
FontStyle="Italic" /> <LineBreak />
|
||||
|
||||
@@ -183,43 +183,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
const auto isElevated = IsElevated();
|
||||
|
||||
if (_settings.GlobalSettings().UseAcrylicInTabRow())
|
||||
{
|
||||
const auto res = Application::Current().Resources();
|
||||
const auto lightKey = winrt::box_value(L"Light");
|
||||
const auto darkKey = winrt::box_value(L"Dark");
|
||||
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
|
||||
|
||||
for (const auto& dictionary : res.MergedDictionaries())
|
||||
{
|
||||
// Don't change MUX resources
|
||||
if (dictionary.Source())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& kvPair : dictionary.ThemeDictionaries())
|
||||
{
|
||||
const auto themeDictionary = kvPair.Value().as<winrt::Windows::UI::Xaml::ResourceDictionary>();
|
||||
|
||||
if (themeDictionary.HasKey(tabViewBackgroundKey))
|
||||
{
|
||||
const auto backgroundSolidBrush = themeDictionary.Lookup(tabViewBackgroundKey).as<Media::SolidColorBrush>();
|
||||
|
||||
const til::color backgroundColor = backgroundSolidBrush.Color();
|
||||
|
||||
const auto acrylicBrush = Media::AcrylicBrush();
|
||||
acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
|
||||
acrylicBrush.FallbackColor(backgroundColor);
|
||||
acrylicBrush.TintColor(backgroundColor);
|
||||
acrylicBrush.TintOpacity(0.5);
|
||||
|
||||
themeDictionary.Insert(tabViewBackgroundKey, acrylicBrush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
_tabView.CanReorderTabs(!isElevated);
|
||||
_tabView.CanDragTabs(!isElevated);
|
||||
@@ -260,6 +223,7 @@ namespace winrt::TerminalApp::implementation
|
||||
transparent.Color(Windows::UI::Colors::Transparent());
|
||||
_tabRow.Background(transparent);
|
||||
}
|
||||
_updateThemeColors();
|
||||
|
||||
// Hookup our event handlers to the ShortcutActionDispatch
|
||||
_RegisterActionCallbacks();
|
||||
@@ -729,8 +693,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Method Description:
|
||||
// - Show a dialog with "About" information. Displays the app's Display
|
||||
// Name, version, getting started link, documentation link, release
|
||||
// Notes link, and privacy policy link.
|
||||
// Name, version, getting started link, source code link, documentation link, release
|
||||
// Notes link, send feedback link and privacy policy link.
|
||||
void TerminalPage::_ShowAboutDialog()
|
||||
{
|
||||
_ShowDialogHelper(L"AboutDialog");
|
||||
@@ -746,6 +710,11 @@ namespace winrt::TerminalApp::implementation
|
||||
return CascadiaSettings::ApplicationVersion();
|
||||
}
|
||||
|
||||
void TerminalPage::_SendFeedbackOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& /*eventArgs*/)
|
||||
{
|
||||
ShellExecute(nullptr, nullptr, L"https://github.com/microsoft/terminal/issues", nullptr, nullptr, SW_SHOW);
|
||||
}
|
||||
|
||||
void TerminalPage::_ThirdPartyNoticesOnClick(const IInspectable& /*sender*/, const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
std::filesystem::path currentPath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
|
||||
@@ -1471,6 +1440,16 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
term.ConnectionStateChanged({ get_weak(), &TerminalPage::_ConnectionStateChangedHandler });
|
||||
|
||||
term.PropertyChanged([weakThis = get_weak()](auto& /*sender*/, auto& e) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
if (e.PropertyName() == L"BackgroundBrush")
|
||||
{
|
||||
page->_updateThemeColors();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
|
||||
}
|
||||
|
||||
@@ -1510,37 +1489,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
// react on color changed events
|
||||
hostingTab.ColorSelected([weakTab, weakThis](auto&& color) {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab && (tab->FocusState() != FocusState::Unfocused))
|
||||
{
|
||||
page->_SetNonClientAreaColors(color);
|
||||
}
|
||||
});
|
||||
|
||||
hostingTab.ColorCleared([weakTab, weakThis]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab && (tab->FocusState() != FocusState::Unfocused))
|
||||
{
|
||||
page->_ClearNonClientAreaColors();
|
||||
}
|
||||
});
|
||||
|
||||
// Add an event handler for when the terminal or tab wants to set a
|
||||
// progress indicator on the taskbar
|
||||
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
|
||||
|
||||
// TODO GH#3327: Once we support colorizing the NewTab button based on
|
||||
// the color of the tab, we'll want to make sure to call
|
||||
// _ClearNewTabButtonColor here, to reset it to the default (for the
|
||||
// newly created tab).
|
||||
// remove any colors left by other colored tabs
|
||||
// _ClearNewTabButtonColor();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -2750,6 +2701,13 @@ namespace winrt::TerminalApp::implementation
|
||||
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings.GlobalSettings().DisableAnimations());
|
||||
|
||||
_tabRow.ShowElevationShield(IsElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
|
||||
Media::SolidColorBrush transparent{ Windows::UI::Colors::Transparent() };
|
||||
_tabView.Background(transparent);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Begin Theme handling
|
||||
_updateThemeColors();
|
||||
}
|
||||
|
||||
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
|
||||
@@ -3134,32 +3092,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_newTabButton.Foreground(foregroundBrush);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the tab split button color when a new tab color is selected
|
||||
// - This method could also set the color of the title bar and tab row
|
||||
// in the future
|
||||
// Arguments:
|
||||
// - selectedTabColor: The color of the newly selected tab
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_SetNonClientAreaColors(const Windows::UI::Color& /*selectedTabColor*/)
|
||||
{
|
||||
// TODO GH#3327: Look at what to do with the NC area when we have XAML theming
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Clears the tab split button color when the tab's color is cleared
|
||||
// - This method could also clear the color of the title bar and tab row
|
||||
// in the future
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_ClearNonClientAreaColors()
|
||||
{
|
||||
// TODO GH#3327: Look at what to do with the NC area when we have XAML theming
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - This is a helper method to get the commandline out of a
|
||||
// ExecuteCommandline action, break it into subcommands, and attempt to
|
||||
@@ -3274,6 +3206,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
|
||||
const IInspectable unused{ nullptr };
|
||||
_SetAsDefaultDismissHandler(unused, unused);
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
@@ -3566,10 +3501,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalPage::_UpdateTeachingTipTheme(winrt::Windows::UI::Xaml::FrameworkElement element)
|
||||
{
|
||||
auto theme{ _settings.GlobalSettings().Theme() };
|
||||
auto theme{ _settings.GlobalSettings().CurrentTheme() };
|
||||
auto requestedTheme{ theme.RequestedTheme() };
|
||||
while (element)
|
||||
{
|
||||
element.RequestedTheme(theme);
|
||||
element.RequestedTheme(requestedTheme);
|
||||
element = element.Parent().try_as<winrt::Windows::UI::Xaml::FrameworkElement>();
|
||||
}
|
||||
}
|
||||
@@ -4073,4 +4009,144 @@ namespace winrt::TerminalApp::implementation
|
||||
applicationState.DismissedMessages(std::move(messages));
|
||||
}
|
||||
|
||||
void TerminalPage::_updateThemeColors()
|
||||
{
|
||||
if (_settings == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto theme = _settings.GlobalSettings().CurrentTheme();
|
||||
auto requestedTheme{ theme.RequestedTheme() };
|
||||
|
||||
// First: Update the colors of our individual TabViewItems. This applies tab.background to the tabs via TerminalTab::ThemeColor
|
||||
{
|
||||
auto tabBackground = theme.Tab() ? theme.Tab().Background() : nullptr;
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
if (const auto& terminalTabImpl{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
terminalTabImpl->ThemeColor(tabBackground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto res = Application::Current().Resources();
|
||||
|
||||
// XAML Hacks:
|
||||
//
|
||||
// the App is always in the OS theme, so the
|
||||
// App::Current().Resources() lookup will always get the value for the
|
||||
// OS theme, not the requested theme.
|
||||
//
|
||||
// This helper allows us to instead lookup the value of a resource
|
||||
// specified by `key` for the given `requestedTheme`, from the
|
||||
// dictionaries in App.xaml. Make sure the value is actually there!
|
||||
// Otherwise this'll throw like any other Lookup for a resource that
|
||||
// isn't there.
|
||||
static const auto lookup = [](auto& res, auto& requestedTheme, auto& key) {
|
||||
// You want the Default version of the resource? Great, the App is
|
||||
// always in the OS theme. Just look it up and be done.
|
||||
if (requestedTheme == ElementTheme::Default)
|
||||
{
|
||||
return res.Lookup(key);
|
||||
}
|
||||
static const auto lightKey = winrt::box_value(L"Light");
|
||||
static const auto darkKey = winrt::box_value(L"Dark");
|
||||
// There isn't an ElementTheme::HighContrast.
|
||||
|
||||
auto requestedThemeKey = requestedTheme == ElementTheme::Dark ? darkKey : lightKey;
|
||||
for (const auto& dictionary : res.MergedDictionaries())
|
||||
{
|
||||
// Don't look in the MUX resources. They come first. A person
|
||||
// with more patience than me may find a way to look through our
|
||||
// dictionaries first, then the MUX ones, but that's not needed
|
||||
// currently
|
||||
if (dictionary.Source())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Look through the theme dictionaries we defined:
|
||||
for (const auto& [dictionaryKey, dict] : dictionary.ThemeDictionaries())
|
||||
{
|
||||
// Does the key for this dict match the theme we're looking for?
|
||||
if (winrt::unbox_value<winrt::hstring>(dictionaryKey) !=
|
||||
winrt::unbox_value<winrt::hstring>(requestedThemeKey))
|
||||
{
|
||||
// No? skip it.
|
||||
continue;
|
||||
}
|
||||
// Look for the requested resource in this dict.
|
||||
const auto themeDictionary = dict.as<winrt::Windows::UI::Xaml::ResourceDictionary>();
|
||||
if (themeDictionary.HasKey(key))
|
||||
{
|
||||
return themeDictionary.Lookup(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find it in the requested dict, fall back to the default dictionary.
|
||||
return res.Lookup(key);
|
||||
};
|
||||
|
||||
// Use our helper to lookup the theme-aware version of the resource.
|
||||
const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground");
|
||||
const auto backgroundSolidBrush = lookup(res, requestedTheme, tabViewBackgroundKey).as<Media::SolidColorBrush>();
|
||||
|
||||
til::color bgColor = backgroundSolidBrush.Color();
|
||||
|
||||
if (_settings.GlobalSettings().UseAcrylicInTabRow())
|
||||
{
|
||||
const auto acrylicBrush = Media::AcrylicBrush();
|
||||
acrylicBrush.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
|
||||
acrylicBrush.FallbackColor(bgColor);
|
||||
acrylicBrush.TintColor(bgColor);
|
||||
acrylicBrush.TintOpacity(0.5);
|
||||
|
||||
TitlebarBrush(acrylicBrush);
|
||||
}
|
||||
else if (auto tabRowBg{ theme.TabRow() ? (_activated ? theme.TabRow().Background() :
|
||||
theme.TabRow().UnfocusedBackground()) :
|
||||
ThemeColor{ nullptr } })
|
||||
{
|
||||
const auto terminalBrush = [this]() -> Media::Brush {
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
return control.BackgroundBrush();
|
||||
}
|
||||
else if (auto settingsTab = _GetFocusedTab().try_as<TerminalApp::SettingsTab>())
|
||||
{
|
||||
return settingsTab.Content().try_as<Settings::Editor::MainPage>().BackgroundBrush();
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
const auto themeBrush{ tabRowBg.Evaluate(res, terminalBrush, true) };
|
||||
bgColor = ThemeColor::ColorFromBrush(themeBrush);
|
||||
TitlebarBrush(themeBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing was set in the theme - fall back to our original `TabViewBackground` color.
|
||||
TitlebarBrush(backgroundSolidBrush);
|
||||
}
|
||||
|
||||
if (!_settings.GlobalSettings().ShowTabsInTitlebar())
|
||||
{
|
||||
_tabRow.Background(TitlebarBrush());
|
||||
}
|
||||
|
||||
// Update the new tab button to have better contrast with the new color.
|
||||
// In theory, it would be convenient to also change these for the
|
||||
// inactive tabs as well, but we're leaving that as a follow up.
|
||||
_SetNewTabButtonColor(bgColor, bgColor);
|
||||
}
|
||||
|
||||
void TerminalPage::WindowActivated(const bool activated)
|
||||
{
|
||||
// Stash if we're activated. Use that when we reload
|
||||
// the settings, change active panes, etc.
|
||||
_activated = activated;
|
||||
_updateThemeColors();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool IsElevated() const noexcept;
|
||||
|
||||
void OpenSettingsUI();
|
||||
void WindowActivated(const bool activated);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
@@ -157,6 +158,8 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
|
||||
TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs)
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
|
||||
std::optional<HWND> _hostingHwnd;
|
||||
@@ -196,6 +199,8 @@ namespace winrt::TerminalApp::implementation
|
||||
std::optional<int> _rearrangeFrom{};
|
||||
std::optional<int> _rearrangeTo{};
|
||||
bool _removing{ false };
|
||||
|
||||
bool _activated{ false };
|
||||
bool _visible{ true };
|
||||
|
||||
std::vector<std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>> _previouslyClosedPanesAndTabs{};
|
||||
@@ -246,6 +251,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _CommandPaletteButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _SendFeedbackOnClick(const IInspectable& sender, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& eventArgs);
|
||||
|
||||
void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
|
||||
@@ -383,8 +389,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _RefreshUIForSettingsReload();
|
||||
|
||||
void _SetNonClientAreaColors(const Windows::UI::Color& selectedTabColor);
|
||||
void _ClearNonClientAreaColors();
|
||||
void _SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor);
|
||||
void _ClearNewTabButtonColor();
|
||||
|
||||
@@ -443,6 +447,8 @@ namespace winrt::TerminalApp::implementation
|
||||
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
|
||||
static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
|
||||
|
||||
void _updateThemeColors();
|
||||
|
||||
winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace TerminalApp
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
|
||||
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
|
||||
void WindowActivated(Boolean activated);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
|
||||
@@ -100,13 +100,16 @@
|
||||
x:Uid="AboutDialog"
|
||||
Grid.Row="2"
|
||||
x:Load="False"
|
||||
DefaultButton="Close">
|
||||
DefaultButton="Close"
|
||||
PrimaryButtonClick="_SendFeedbackOnClick">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock IsTextSelectionEnabled="True">
|
||||
<Run Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
|
||||
<Run x:Uid="AboutDialog_VersionLabel" />
|
||||
<Run Text="{x:Bind ApplicationVersion}" />
|
||||
</TextBlock>
|
||||
<HyperlinkButton x:Uid="AboutDialog_SourceCodeLink"
|
||||
NavigateUri="https://github.com/microsoft/terminal" />
|
||||
<HyperlinkButton x:Uid="AboutDialog_DocumentationLink"
|
||||
NavigateUri="https://go.microsoft.com/fwlink/?linkid=2125416" />
|
||||
<HyperlinkButton x:Uid="AboutDialog_ReleaseNotesLink"
|
||||
|
||||
@@ -316,12 +316,16 @@ namespace winrt::TerminalApp::implementation
|
||||
if (hide)
|
||||
{
|
||||
Icon({});
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX({}));
|
||||
// TabViewItem().IconSource(IconPathConverter::IconSourceMUX({}));
|
||||
TabViewItem().Icon(IconPathConverter::IconFromPath({}));
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
// TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
auto ico = IconPathConverter::IconFromPath(_lastIconPath);
|
||||
|
||||
TabViewItem().Icon(IconPathConverter::IconFromPath(_lastIconPath));
|
||||
}
|
||||
tab->_iconHidden = hide;
|
||||
}
|
||||
@@ -864,7 +868,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
@@ -1069,7 +1073,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Add a Closed event handler to the Pane. If the pane closes out from
|
||||
// underneath us, and it's zoomed, we want to be able to make sure to
|
||||
// update our state accordingly to un-zoom that pane. See GH#7252.
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget {
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_zoomedPane)
|
||||
@@ -1343,7 +1347,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// -------------------- | -- | --
|
||||
// Runtime Color | _optional_ | Color Picker / `setTabColor` action
|
||||
// Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT
|
||||
// Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme
|
||||
// Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor)
|
||||
// Tab Default Color | **default** | TabView in XAML
|
||||
//
|
||||
// coalesce will get us the first of these values that's
|
||||
@@ -1352,7 +1356,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
return til::coalesce(_runtimeTabColor,
|
||||
controlTabColor,
|
||||
_themeTabColor,
|
||||
std::optional<Windows::UI::Color>(std::nullopt));
|
||||
}
|
||||
|
||||
@@ -1370,6 +1373,12 @@ namespace winrt::TerminalApp::implementation
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
void TerminalTab::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color)
|
||||
{
|
||||
_themeColor = color;
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This function dispatches a function to the UI thread to recalculate
|
||||
// what this tab's current background color should be. If a color is set,
|
||||
@@ -1390,11 +1399,37 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
auto tab{ ptrTab };
|
||||
|
||||
// GetTabColor will return the color set by the color picker, or the
|
||||
// color specified in the profile. If neither of those were set,
|
||||
// then look to _themeColor to see if there's a value there.
|
||||
// Otherwise, clear our color, falling back to the TabView defaults.
|
||||
auto currentColor = tab->GetTabColor();
|
||||
if (currentColor.has_value())
|
||||
{
|
||||
tab->_ApplyTabColor(currentColor.value());
|
||||
}
|
||||
else if (tab->_themeColor != nullptr)
|
||||
{
|
||||
// One-liner to safely get the active control's brush.
|
||||
Media::Brush terminalBrush{ nullptr };
|
||||
if (const auto& c{ tab->GetActiveTerminalControl() })
|
||||
{
|
||||
terminalBrush = c.BackgroundBrush();
|
||||
}
|
||||
|
||||
if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
|
||||
{
|
||||
// ThemeColor.Evaluate will get us a Brush (because the
|
||||
// TermControl could have an acrylic BG, for example). Take
|
||||
// that brush, and get the color out of it. We don't really
|
||||
// want to have the tab items themselves be acrylic.
|
||||
tab->_ApplyTabColor(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_ClearTabBackgroundColor();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_ClearTabBackgroundColor();
|
||||
@@ -1452,18 +1487,16 @@ namespace winrt::TerminalApp::implementation
|
||||
subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
|
||||
}
|
||||
|
||||
hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color));
|
||||
selectedTabBrush.Color(color);
|
||||
|
||||
// currently if a tab has a custom color, a deselected state is
|
||||
// signified by using the same color with a bit of transparency
|
||||
auto deselectedTabColor = color;
|
||||
deselectedTabColor.A = 64;
|
||||
deselectedTabBrush.Color(deselectedTabColor);
|
||||
deselectedTabBrush.Color(color);
|
||||
deselectedTabBrush.Opacity(0.3);
|
||||
|
||||
hoverTabBrush.Color(color);
|
||||
hoverTabBrush.Opacity(0.6);
|
||||
|
||||
// currently if a tab has a custom color, a deselected state is
|
||||
// signified by using the same color with a bit of transparency
|
||||
//
|
||||
// Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
|
||||
// use TabViewItem().Background() for that. HOWEVER,
|
||||
// TabViewItem().Background() only sets the color of the tab background
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
std::optional<winrt::Windows::UI::Color> GetTabColor();
|
||||
|
||||
void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color);
|
||||
void SetRuntimeTabColor(const winrt::Windows::UI::Color& color);
|
||||
void ResetRuntimeTabColor();
|
||||
void ActivateColorPicker();
|
||||
@@ -113,10 +114,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::hstring _lastIconPath{};
|
||||
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
|
||||
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
|
||||
std::optional<winrt::Windows::UI::Color> _runtimeTabColor{};
|
||||
winrt::TerminalApp::TabHeaderControl _headerControl{};
|
||||
winrt::TerminalApp::TerminalTabStatus _tabStatus{};
|
||||
winrt::Microsoft::Terminal::Settings::Model::ThemeColor _themeColor{ nullptr };
|
||||
|
||||
struct ControlEventTokens
|
||||
{
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "TitlebarControl.h"
|
||||
|
||||
#include "ColorHelper.h"
|
||||
|
||||
#include "TitlebarControl.g.cpp"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
@@ -21,6 +23,25 @@ namespace winrt::TerminalApp::implementation
|
||||
MinMaxCloseControl().MinimizeClick({ this, &TitlebarControl::Minimize_Click });
|
||||
MinMaxCloseControl().MaximizeClick({ this, &TitlebarControl::Maximize_Click });
|
||||
MinMaxCloseControl().CloseClick({ this, &TitlebarControl::Close_Click });
|
||||
|
||||
// Listen for changes to the Background. If the Background changes,
|
||||
// we'll want to manually adjust the RequestedTheme of our caption
|
||||
// buttons, so the foreground stands out against whatever BG color was
|
||||
// selected for us.
|
||||
//
|
||||
// This is how you register a PropertyChanged event for the Background
|
||||
// property of a Grid. The Background property is defined in the base
|
||||
// class Panel.
|
||||
const auto bgProperty{ winrt::Windows::UI::Xaml::Controls::Panel::BackgroundProperty() };
|
||||
RegisterPropertyChangedCallback(bgProperty, [weakThis = get_weak(), bgProperty](auto& /*sender*/, auto& e) {
|
||||
if (auto self{ weakThis.get() })
|
||||
{
|
||||
if (e == bgProperty)
|
||||
{
|
||||
self->_backgroundChanged(self->Background());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
double TitlebarControl::CaptionButtonWidth()
|
||||
@@ -144,4 +165,26 @@ namespace winrt::TerminalApp::implementation
|
||||
MinMaxCloseControl().ReleaseButtons();
|
||||
}
|
||||
|
||||
void TitlebarControl::_backgroundChanged(winrt::Windows::UI::Xaml::Media::Brush brush)
|
||||
{
|
||||
// Loosely cribbed from TerminalPage::_SetNewTabButtonColor
|
||||
til::color c;
|
||||
if (auto acrylic = brush.try_as<winrt::Windows::UI::Xaml::Media::AcrylicBrush>())
|
||||
{
|
||||
c = acrylic.TintColor();
|
||||
}
|
||||
else if (auto solidColor = brush.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>())
|
||||
{
|
||||
c = solidColor.Color();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto isBrightColor = ColorHelper::IsBrightColor(c);
|
||||
MinMaxCloseControl().RequestedTheme(isBrightColor ? winrt::Windows::UI::Xaml::ElementTheme::Light :
|
||||
winrt::Windows::UI::Xaml::ElementTheme::Dark);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace winrt::TerminalApp::implementation
|
||||
private:
|
||||
void _OnMaximizeOrRestore(byte flag);
|
||||
HWND _window{ nullptr }; // non-owning handle; should not be freed in the dtor.
|
||||
|
||||
void _backgroundChanged(winrt::Windows::UI::Xaml::Media::Brush brush);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
VerticalAlignment="Top"
|
||||
d:DesignHeight="36"
|
||||
d:DesignWidth="400"
|
||||
Background="{ThemeResource TabViewBackground}"
|
||||
SizeChanged="Root_SizeChanged"
|
||||
mc:Ignorable="d">
|
||||
|
||||
|
||||
@@ -53,7 +53,12 @@ CATCH_RETURN()
|
||||
HRESULT CTerminalHandoff::s_StopListening()
|
||||
{
|
||||
std::unique_lock lock{ _mtx };
|
||||
return s_StopListeningLocked();
|
||||
}
|
||||
|
||||
// See s_StopListening()
|
||||
HRESULT CTerminalHandoff::s_StopListeningLocked()
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
||||
|
||||
_pfnHandoff = nullptr;
|
||||
@@ -101,14 +106,16 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE sign
|
||||
{
|
||||
try
|
||||
{
|
||||
// Stash a local copy of _pfnHandoff before we stop listening.
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
// s_StopListeningLocked sets _pfnHandoff to nullptr.
|
||||
// localPfnHandoff is tested for nullness below.
|
||||
#pragma warning(suppress : 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
auto localPfnHandoff = _pfnHandoff;
|
||||
|
||||
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
|
||||
// COM does not automatically clean that up for us. We must do it.
|
||||
s_StopListening();
|
||||
|
||||
std::unique_lock lock{ _mtx };
|
||||
LOG_IF_FAILED(s_StopListeningLocked());
|
||||
|
||||
// Report an error if no one registered a handoff function before calling this.
|
||||
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
|
||||
|
||||
@@ -43,6 +43,9 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
|
||||
|
||||
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
|
||||
static HRESULT s_StopListening();
|
||||
|
||||
private:
|
||||
static HRESULT s_StopListeningLocked();
|
||||
};
|
||||
|
||||
// Disable warnings from the CoCreatableClass macro as the value it provides for
|
||||
|
||||
@@ -417,12 +417,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
vkey != VK_SNAPSHOT &&
|
||||
keyDown)
|
||||
{
|
||||
if (_terminal->IsInMarkMode() && modifiers.IsCtrlPressed() && vkey == 'A')
|
||||
const auto isInMarkMode = _terminal->SelectionMode() == ::Terminal::SelectionInteractionMode::Mark;
|
||||
if (isInMarkMode)
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectAll();
|
||||
_renderer->TriggerSelection();
|
||||
return true;
|
||||
if (modifiers.IsCtrlPressed() && vkey == 'A')
|
||||
{
|
||||
// Ctrl + A --> Select all
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectAll();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
else if (vkey == VK_TAB && _settings->DetectURLs())
|
||||
{
|
||||
// [Shift +] Tab --> next/previous hyperlink
|
||||
auto lock = _terminal->LockForWriting();
|
||||
const auto direction = modifiers.IsShiftPressed() ? ::Terminal::SearchDirection::Backward : ::Terminal::SearchDirection::Forward;
|
||||
_terminal->SelectHyperlink(direction);
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
else if (vkey == VK_RETURN && modifiers.IsCtrlPressed() && _terminal->IsTargetingUrl())
|
||||
{
|
||||
// Ctrl + Enter --> Open URL
|
||||
auto lock = _terminal->LockForReading();
|
||||
const auto uri = _terminal->GetHyperlinkAtBufferPosition(_terminal->GetSelectionAnchor());
|
||||
_OpenHyperlinkHandlers(*this, winrt::make<OpenHyperlinkEventArgs>(winrt::hstring{ uri }));
|
||||
return true;
|
||||
}
|
||||
else if (vkey == VK_RETURN)
|
||||
{
|
||||
// [Shift +] Enter --> copy text
|
||||
// Don't lock here! CopySelectionToClipboard already locks for you!
|
||||
CopySelectionToClipboard(modifiers.IsShiftPressed(), nullptr);
|
||||
_terminal->ClearSelection();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// try to update the selection
|
||||
@@ -430,7 +461,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -438,7 +469,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (!modifiers.IsWinPressed())
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
// When there is a selection active, escape should clear it and NOT flow through
|
||||
@@ -590,12 +621,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_lastHoveredCell = terminalPosition;
|
||||
uint16_t newId{ 0u };
|
||||
// we can't use auto here because we're pre-declaring newInterval.
|
||||
decltype(_terminal->GetHyperlinkIntervalFromPosition({})) newInterval{ std::nullopt };
|
||||
decltype(_terminal->GetHyperlinkIntervalFromViewportPosition({})) newInterval{ std::nullopt };
|
||||
if (terminalPosition.has_value())
|
||||
{
|
||||
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
|
||||
newId = _terminal->GetHyperlinkIdAtPosition(*terminalPosition);
|
||||
newInterval = _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition);
|
||||
newId = _terminal->GetHyperlinkIdAtViewportPosition(*terminalPosition);
|
||||
newInterval = _terminal->GetHyperlinkIntervalFromViewportPosition(*terminalPosition);
|
||||
}
|
||||
|
||||
// If the hyperlink ID changed or the interval changed, trigger a redraw all
|
||||
@@ -625,7 +656,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Lock for the duration of our reads.
|
||||
auto lock = _terminal->LockForReading();
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(til::point{ pos }) };
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(til::point{ pos }) };
|
||||
}
|
||||
|
||||
winrt::hstring ControlCore::HoveredUriText() const
|
||||
@@ -633,7 +664,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
|
||||
if (_lastHoveredCell.has_value())
|
||||
{
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) };
|
||||
return winrt::hstring{ _terminal->GetHyperlinkAtViewportPosition(*_lastHoveredCell) };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -934,6 +965,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_terminal->SetSelectionAnchor(position);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves selection metadata from Terminal necessary to draw the
|
||||
// selection markers.
|
||||
// - Since all of this needs to be done under lock, it is more performant
|
||||
// to throw it all in a struct and pass it along.
|
||||
Control::SelectionData ControlCore::SelectionInfo() const
|
||||
{
|
||||
auto lock = _terminal->LockForReading();
|
||||
Control::SelectionData info;
|
||||
|
||||
const auto start{ _terminal->SelectionStartForRendering() };
|
||||
info.StartPos = { start.X, start.Y };
|
||||
|
||||
const auto end{ _terminal->SelectionEndForRendering() };
|
||||
info.EndPos = { end.X, end.Y };
|
||||
|
||||
info.Endpoint = static_cast<SelectionEndpointTarget>(_terminal->SelectionEndpointTarget());
|
||||
|
||||
const auto bufferSize{ _terminal->GetTextBuffer().GetSize() };
|
||||
info.StartAtLeftBoundary = _terminal->GetSelectionAnchor().x == bufferSize.Left();
|
||||
info.EndAtRightBoundary = _terminal->GetSelectionEnd().x == bufferSize.RightInclusive();
|
||||
return info;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
|
||||
// Arguments:
|
||||
@@ -956,7 +1011,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// save location (for rendering) + render
|
||||
_terminal->SetSelectionEnd(terminalPosition);
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
// Called when the Terminal wants to set something to the clipboard, i.e.
|
||||
@@ -969,7 +1024,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
||||
@@ -1015,12 +1069,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bgColor) :
|
||||
"";
|
||||
|
||||
if (!_settings->CopyOnSelect())
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
|
||||
// send data up for clipboard
|
||||
_CopyToClipboardHandlers(*this,
|
||||
winrt::make<CopyToClipboardEventArgs>(winrt::hstring{ textData },
|
||||
@@ -1034,7 +1082,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->SelectAll();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
void ControlCore::ClearSelection()
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->ClearSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
bool ControlCore::ToggleBlockSelection()
|
||||
@@ -1044,6 +1099,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_terminal->SetBlockSelection(!_terminal->IsBlockSelection());
|
||||
_renderer->TriggerSelection();
|
||||
// do not update the selection markers!
|
||||
// if we were showing them, keep it that way.
|
||||
// otherwise, continue to not show them
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1053,12 +1111,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
auto lock = _terminal->LockForWriting();
|
||||
_terminal->ToggleMarkMode();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
bool ControlCore::IsInMarkMode() const
|
||||
Control::SelectionInteractionMode ControlCore::SelectionMode() const
|
||||
{
|
||||
return _terminal->IsInMarkMode();
|
||||
return static_cast<Control::SelectionInteractionMode>(_terminal->SelectionMode());
|
||||
}
|
||||
|
||||
bool ControlCore::SwitchSelectionEndpoint()
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
_terminal->SwitchSelectionEndpoint();
|
||||
_updateSelectionUI();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1068,7 +1137,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_terminal->WritePastedText(hstr);
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
_updateSelectionUI();
|
||||
_terminal->TrySnapOnInput();
|
||||
}
|
||||
|
||||
@@ -1303,7 +1372,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
if (!_midiAudio)
|
||||
{
|
||||
_midiAudio = std::make_unique<MidiAudio>();
|
||||
const auto windowHandle = reinterpret_cast<HWND>(_owningHwnd);
|
||||
_midiAudio = std::make_unique<MidiAudio>(windowHandle);
|
||||
_midiAudio->Initialize();
|
||||
}
|
||||
return *_midiAudio;
|
||||
@@ -1388,7 +1458,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_terminal->SetBlockSelection(false);
|
||||
search.Select();
|
||||
|
||||
// this is used for search,
|
||||
// DO NOT call _updateSelectionUI() here.
|
||||
// We don't want to show the markers so manually tell it to clear it.
|
||||
_renderer->TriggerSelection();
|
||||
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
|
||||
}
|
||||
|
||||
// Raise a FoundMatch event, which the control will use to notify
|
||||
@@ -1589,8 +1664,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_terminal->MultiClickSelection(terminalPosition, mode);
|
||||
selectionNeedsToBeCopied = true;
|
||||
}
|
||||
_updateSelectionUI();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates the renderer's representation of the selection as well as the selection marker overlay in TermControl
|
||||
void ControlCore::_updateSelectionUI()
|
||||
{
|
||||
_renderer->TriggerSelection();
|
||||
// only show the markers if we're doing a keyboard selection or in mark mode
|
||||
const bool showMarkers{ _terminal->SelectionMode() >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard };
|
||||
_UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(!showMarkers));
|
||||
}
|
||||
|
||||
void ControlCore::AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine)
|
||||
@@ -1859,13 +1943,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - This is related to work done for GH#2988.
|
||||
void ControlCore::GotFocus()
|
||||
{
|
||||
_terminal->FocusChanged(true);
|
||||
_focusChanged(true);
|
||||
}
|
||||
|
||||
// See GotFocus.
|
||||
void ControlCore::LostFocus()
|
||||
{
|
||||
_terminal->FocusChanged(false);
|
||||
_focusChanged(false);
|
||||
}
|
||||
|
||||
void ControlCore::_focusChanged(bool focused)
|
||||
{
|
||||
// GH#13461 - temporarily turn off read-only mode, send the focus event,
|
||||
// then turn it back on. Even in focus mode, focus events are fine to
|
||||
// send. We don't want to pop a warning every time the control is
|
||||
// focused.
|
||||
const auto previous = std::exchange(_isReadOnly, false);
|
||||
const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; });
|
||||
_terminal->FocusChanged(focused);
|
||||
}
|
||||
|
||||
bool ControlCore::_isBackgroundTransparent()
|
||||
@@ -2017,19 +2112,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
const auto viewHeight = ViewHeight();
|
||||
const auto bufferSize = BufferHeight();
|
||||
|
||||
// UserScrollViewport, to update the Terminal about where the viewport should be
|
||||
// then raise a _terminalScrollPositionChanged to inform the control to update the scrollbar.
|
||||
if (tgt.has_value())
|
||||
{
|
||||
UserScrollViewport(tgt->start.y);
|
||||
_terminalScrollPositionChanged(tgt->start.y, viewHeight, bufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (direction == ScrollToMarkDirection::Last || direction == ScrollToMarkDirection::Next)
|
||||
{
|
||||
UserScrollViewport(BufferHeight());
|
||||
_terminalScrollPositionChanged(BufferHeight(), viewHeight, bufferSize);
|
||||
}
|
||||
else if (direction == ScrollToMarkDirection::First || direction == ScrollToMarkDirection::Previous)
|
||||
{
|
||||
UserScrollViewport(0);
|
||||
_terminalScrollPositionChanged(0, viewHeight, bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +82,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void PasteText(const winrt::hstring& hstr);
|
||||
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
|
||||
void SelectAll();
|
||||
void ClearSelection();
|
||||
bool ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
bool IsInMarkMode() const;
|
||||
Control::SelectionInteractionMode SelectionMode() const;
|
||||
bool SwitchSelectionEndpoint();
|
||||
|
||||
void GotFocus();
|
||||
void LostFocus();
|
||||
@@ -159,6 +161,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bool HasSelection() const;
|
||||
bool CopyOnSelect() const;
|
||||
Windows::Foundation::Collections::IVector<winrt::hstring> SelectedText(bool trimTrailingWhitespace) const;
|
||||
Control::SelectionData SelectionInfo() const;
|
||||
void SetSelectionAnchor(const til::point position);
|
||||
void SetEndSelectionPoint(const til::point position);
|
||||
|
||||
@@ -214,6 +217,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable);
|
||||
TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs);
|
||||
TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
|
||||
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
@@ -269,6 +274,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bool _setFontSizeUnderLock(int fontSize);
|
||||
void _updateFont(const bool initialUpdate = false);
|
||||
void _refreshSizeUnderLock();
|
||||
void _updateSelectionUI();
|
||||
|
||||
void _sendInputToConnection(std::wstring_view wstr);
|
||||
|
||||
@@ -306,6 +312,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _setOpacity(const double opacity);
|
||||
|
||||
bool _isBackgroundTransparent();
|
||||
void _focusChanged(bool focused);
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
|
||||
@@ -29,6 +29,30 @@ namespace Microsoft.Terminal.Control
|
||||
All
|
||||
};
|
||||
|
||||
enum SelectionInteractionMode
|
||||
{
|
||||
None,
|
||||
Mouse,
|
||||
Keyboard,
|
||||
Mark
|
||||
};
|
||||
|
||||
[flags]
|
||||
enum SelectionEndpointTarget
|
||||
{
|
||||
Start = 0x1,
|
||||
End = 0x2
|
||||
};
|
||||
|
||||
struct SelectionData
|
||||
{
|
||||
Microsoft.Terminal.Core.Point StartPos;
|
||||
Microsoft.Terminal.Core.Point EndPos;
|
||||
SelectionEndpointTarget Endpoint;
|
||||
Boolean StartAtLeftBoundary;
|
||||
Boolean EndAtRightBoundary;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ControlCore : ICoreState
|
||||
{
|
||||
ControlCore(IControlSettings settings,
|
||||
@@ -65,10 +89,11 @@ namespace Microsoft.Terminal.Control
|
||||
void SendInput(String text);
|
||||
void PasteText(String text);
|
||||
void SelectAll();
|
||||
void ClearSelection();
|
||||
Boolean ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
Boolean SwitchSelectionEndpoint();
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
Boolean IsInMarkMode();
|
||||
|
||||
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
|
||||
void ClearHoveredCell();
|
||||
@@ -90,6 +115,8 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
Boolean HasSelection { get; };
|
||||
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
|
||||
SelectionData SelectionInfo { get; };
|
||||
SelectionInteractionMode SelectionMode();
|
||||
|
||||
String HoveredUriText { get; };
|
||||
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
|
||||
@@ -125,6 +152,7 @@ namespace Microsoft.Terminal.Control
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReceivedOutput;
|
||||
event Windows.Foundation.TypedEventHandler<Object, FoundResultsArgs> FoundMatch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
|
||||
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,7 +142,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
||||
@@ -257,15 +256,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
|
||||
{
|
||||
// CopyOnSelect right click always pastes
|
||||
if (_core->CopyOnSelect() || !_core->HasSelection())
|
||||
// Try to copy the text and clear the selection
|
||||
const auto successfulCopy = CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
_core->ClearSelection();
|
||||
if (_core->CopyOnSelect() || !successfulCopy)
|
||||
{
|
||||
// CopyOnSelect: right click always pastes!
|
||||
// Otherwise: no selection --> paste
|
||||
RequestPasteTextFromClipboard();
|
||||
}
|
||||
else
|
||||
{
|
||||
CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,6 +382,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
isLeftMouseRelease &&
|
||||
_selectionNeedsToBeCopied)
|
||||
{
|
||||
// IMPORTANT!
|
||||
// DO NOT clear the selection here!
|
||||
// Otherwise, the selection will be cleared immediately after you make it.
|
||||
CopySelectionToClipboard(false, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,3 +13,4 @@
|
||||
#include "TransparencyChangedEventArgs.g.cpp"
|
||||
#include "FoundResultsArgs.g.cpp"
|
||||
#include "ShowWindowArgs.g.cpp"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.cpp"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "TransparencyChangedEventArgs.g.h"
|
||||
#include "FoundResultsArgs.g.h"
|
||||
#include "ShowWindowArgs.g.h"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
@@ -157,4 +158,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
WINRT_PROPERTY(bool, ShowOrHide);
|
||||
};
|
||||
|
||||
struct UpdateSelectionMarkersEventArgs : public UpdateSelectionMarkersEventArgsT<UpdateSelectionMarkersEventArgs>
|
||||
{
|
||||
public:
|
||||
UpdateSelectionMarkersEventArgs(const bool clearMarkers) :
|
||||
_ClearMarkers(clearMarkers)
|
||||
{
|
||||
}
|
||||
|
||||
WINRT_PROPERTY(bool, ClearMarkers, false);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ namespace Microsoft.Terminal.Control
|
||||
Double Opacity { get; };
|
||||
}
|
||||
|
||||
|
||||
runtimeclass FoundResultsArgs
|
||||
{
|
||||
Boolean FoundMatch { get; };
|
||||
@@ -79,4 +78,9 @@ namespace Microsoft.Terminal.Control
|
||||
{
|
||||
Boolean ShowOrHide { get; };
|
||||
}
|
||||
|
||||
runtimeclass UpdateSelectionMarkersEventArgs
|
||||
{
|
||||
Boolean ClearMarkers { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
|
||||
_core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
|
||||
_core.FoundMatch({ this, &TermControl::_coreFoundMatch });
|
||||
_core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers });
|
||||
_core.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
|
||||
_interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
|
||||
_interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
|
||||
|
||||
@@ -358,6 +360,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// switch from a solid color brush to an acrylic one.
|
||||
_changeBackgroundColor(bg);
|
||||
|
||||
// Update selection markers
|
||||
Windows::UI::Xaml::Media::SolidColorBrush cursorColorBrush{ til::color{ newAppearance.CursorColor() } };
|
||||
SelectionStartMarker().Fill(cursorColorBrush);
|
||||
SelectionEndMarker().Fill(cursorColorBrush);
|
||||
|
||||
// Set TSF Foreground
|
||||
Media::SolidColorBrush foregroundBrush{};
|
||||
if (_core.Settings().UseBackgroundImageForWindow())
|
||||
@@ -426,12 +433,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// achieve the intended effect.
|
||||
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
|
||||
ScrollBar().Visibility(Visibility::Collapsed);
|
||||
ScrollMarksGrid().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
else // (default or Visible)
|
||||
{
|
||||
// Default behavior
|
||||
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
|
||||
ScrollBar().Visibility(Visibility::Visible);
|
||||
ScrollMarksGrid().Visibility(Visibility::Visible);
|
||||
}
|
||||
|
||||
_interactivity.UpdateSettings();
|
||||
@@ -562,6 +571,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
RootGrid().Background(solidColor);
|
||||
}
|
||||
|
||||
BackgroundBrush(RootGrid().Background());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -607,6 +618,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
solidColor.Color(bg);
|
||||
}
|
||||
|
||||
BackgroundBrush(RootGrid().Background());
|
||||
|
||||
// Don't use the normal BackgroundBrush() Observable Property setter
|
||||
// here. (e.g. `BackgroundBrush()`). The one from the macro will
|
||||
// automatically ignore changes where the value doesn't _actually_
|
||||
// change. In our case, most of the time when changing the colors of the
|
||||
// background, the _Brush_ itself doesn't change, we simply change the
|
||||
// Color() of the brush. This results in the event not getting bubbled
|
||||
// up.
|
||||
//
|
||||
// Firing it manually makes sure it does.
|
||||
_BackgroundBrush = RootGrid().Background();
|
||||
_PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1182,7 +1207,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
// Manually show the cursor when a key is pressed. Restarting
|
||||
// the timer prevents flickering.
|
||||
_core.CursorOn(!_core.IsInMarkMode());
|
||||
_core.CursorOn(_core.SelectionMode() != SelectionInteractionMode::Mark);
|
||||
_cursorTimer->Start();
|
||||
}
|
||||
|
||||
@@ -1653,7 +1678,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
if (_cursorTimer)
|
||||
{
|
||||
// When the terminal focuses, show the cursor immediately
|
||||
_core.CursorOn(!_core.IsInMarkMode());
|
||||
_core.CursorOn(_core.SelectionMode() != SelectionInteractionMode::Mark);
|
||||
_cursorTimer->Start();
|
||||
}
|
||||
|
||||
@@ -1831,6 +1856,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
update.newValue = args.ViewTop();
|
||||
|
||||
_updateScrollBar->Run(update);
|
||||
|
||||
// if a selection marker is already visible,
|
||||
// update the position of those markers
|
||||
if (SelectionStartMarker().Visibility() == Visibility::Visible || SelectionEndMarker().Visibility() == Visibility::Visible)
|
||||
{
|
||||
_updateSelectionMarkers(nullptr, winrt::make<UpdateSelectionMarkersEventArgs>(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1889,7 +1921,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
return _interactivity.CopySelectionToClipboard(singleLine, formats);
|
||||
const auto successfulCopy = _interactivity.CopySelectionToClipboard(singleLine, formats);
|
||||
_core.ClearSelection();
|
||||
return successfulCopy;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1914,6 +1948,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.ToggleMarkMode();
|
||||
}
|
||||
|
||||
bool TermControl::SwitchSelectionEndpoint()
|
||||
{
|
||||
return _core.SwitchSelectionEndpoint();
|
||||
}
|
||||
|
||||
void TermControl::Close()
|
||||
{
|
||||
if (!_IsClosing())
|
||||
@@ -2735,8 +2774,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_core.ClearHoveredCell();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable sender,
|
||||
IInspectable args)
|
||||
winrt::fire_and_forget TermControl::_hoveredHyperlinkChanged(IInspectable /*sender*/,
|
||||
IInspectable /*args*/)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
@@ -2763,12 +2802,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
HyperlinkTooltipBorder().BorderThickness(newThickness);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
||||
const til::point startPos{ lastHoveredCell.Value() };
|
||||
const til::size fontSize{ til::math::rounding, _core.FontSize() };
|
||||
const auto posInPixels{ startPos * fontSize };
|
||||
const til::point posInDIPs{ til::math::flooring, posInPixels.x / scale, posInPixels.y / scale };
|
||||
const auto locationInDIPs{ posInDIPs + marginsInDips };
|
||||
const til::point locationInDIPs{ _toPosInDips(lastHoveredCell.Value()) };
|
||||
|
||||
// Move the border to the top left corner of the cell
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
|
||||
@@ -2778,10 +2812,118 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_updateSelectionMarkers(IInspectable /*sender*/, Control::UpdateSelectionMarkersEventArgs args)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await resume_foreground(Dispatcher());
|
||||
if (weakThis.get() && args)
|
||||
{
|
||||
if (_core.HasSelection() && !args.ClearMarkers())
|
||||
{
|
||||
// retrieve all of the necessary selection marker data
|
||||
// from the TerminalCore layer under one lock to improve performance
|
||||
const auto markerData{ _core.SelectionInfo() };
|
||||
|
||||
// lambda helper function that can be used to display a selection marker
|
||||
// - targetEnd: if true, target the "end" selection marker. Otherwise, target "start".
|
||||
auto displayMarker = [&](bool targetEnd) {
|
||||
const auto flipMarker{ targetEnd ? markerData.EndAtRightBoundary : markerData.StartAtLeftBoundary };
|
||||
const auto& marker{ targetEnd ? SelectionEndMarker() : SelectionStartMarker() };
|
||||
|
||||
// Ensure the marker is oriented properly
|
||||
// (i.e. if start is at the beginning of the buffer, it should be flipped)
|
||||
auto transform{ marker.RenderTransform().as<Windows::UI::Xaml::Media::ScaleTransform>() };
|
||||
transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0));
|
||||
marker.RenderTransform(transform);
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
auto terminalPos{ targetEnd ? markerData.EndPos : markerData.StartPos };
|
||||
if (flipMarker)
|
||||
{
|
||||
// When we flip the marker, a negative scaling makes us be one cell-width to the left.
|
||||
// Add one to the viewport pos' x-coord to fix that.
|
||||
terminalPos.X += 1;
|
||||
}
|
||||
const til::point locationInDIPs{ _toPosInDips(terminalPos) };
|
||||
|
||||
// Move the marker to the top left corner of the cell
|
||||
SelectionCanvas().SetLeft(marker,
|
||||
(locationInDIPs.x - SwapChainPanel().ActualOffset().x));
|
||||
SelectionCanvas().SetTop(marker,
|
||||
(locationInDIPs.y - SwapChainPanel().ActualOffset().y));
|
||||
marker.Visibility(Visibility::Visible);
|
||||
};
|
||||
|
||||
// show/update selection markers
|
||||
// figure out which endpoint to move, get it and the relevant icon (hide the other icon)
|
||||
const auto movingEnd{ WI_IsFlagSet(markerData.Endpoint, SelectionEndpointTarget::End) };
|
||||
const auto selectionAnchor{ movingEnd ? markerData.EndPos : markerData.StartPos };
|
||||
const auto& marker{ movingEnd ? SelectionEndMarker() : SelectionStartMarker() };
|
||||
const auto& otherMarker{ movingEnd ? SelectionStartMarker() : SelectionEndMarker() };
|
||||
if (selectionAnchor.Y < 0 || selectionAnchor.Y >= _core.ViewHeight())
|
||||
{
|
||||
// if the endpoint is outside of the viewport,
|
||||
// just hide the markers
|
||||
marker.Visibility(Visibility::Collapsed);
|
||||
otherMarker.Visibility(Visibility::Collapsed);
|
||||
co_return;
|
||||
}
|
||||
else if (WI_AreAllFlagsSet(markerData.Endpoint, SelectionEndpointTarget::Start | SelectionEndpointTarget::End))
|
||||
{
|
||||
// display both markers
|
||||
displayMarker(true);
|
||||
displayMarker(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// display one marker,
|
||||
// but hide the other
|
||||
displayMarker(movingEnd);
|
||||
otherMarker.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// hide selection markers
|
||||
SelectionStartMarker().Visibility(Visibility::Collapsed);
|
||||
SelectionEndMarker().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
til::point TermControl::_toPosInDips(const Core::Point terminalCellPos)
|
||||
{
|
||||
const til::point terminalPos{ terminalCellPos };
|
||||
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
|
||||
const til::size fontSize{ til::math::rounding, _core.FontSize() };
|
||||
const til::point posInPixels{ terminalPos * fontSize };
|
||||
const auto scale{ SwapChainPanel().CompositionScaleX() };
|
||||
const til::point posInDIPs{ til::math::flooring, posInPixels.x / scale, posInPixels.y / scale };
|
||||
return posInDIPs + marginsInDips;
|
||||
}
|
||||
|
||||
void TermControl::_coreFontSizeChanged(const int fontWidth,
|
||||
const int fontHeight,
|
||||
const bool isInitialChange)
|
||||
{
|
||||
// scale the selection markers to be the size of a cell
|
||||
auto scaleMarker = [fontWidth, fontHeight, dpiScale{ SwapChainPanel().CompositionScaleX() }](const Windows::UI::Xaml::Shapes::Path& shape) {
|
||||
// The selection markers were designed to be 5x14 in size,
|
||||
// so use those dimensions below for the scaling
|
||||
const auto scaleX = fontWidth / 5.0 / dpiScale;
|
||||
const auto scaleY = fontHeight / 14.0 / dpiScale;
|
||||
|
||||
Windows::UI::Xaml::Media::ScaleTransform transform;
|
||||
transform.ScaleX(scaleX);
|
||||
transform.ScaleY(scaleY);
|
||||
shape.RenderTransform(transform);
|
||||
|
||||
// now hide the shape
|
||||
shape.Visibility(Visibility::Collapsed);
|
||||
};
|
||||
scaleMarker(SelectionStartMarker());
|
||||
scaleMarker(SelectionEndMarker());
|
||||
|
||||
// Don't try to inspect the core here. The Core is raising this while
|
||||
// it's holding its write lock. If the handlers calls back to some
|
||||
// method on the TermControl on the same thread, and that _method_ calls
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void SelectAll();
|
||||
bool ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
bool SwitchSelectionEndpoint();
|
||||
void Close();
|
||||
Windows::Foundation::Size CharacterDimensions() const;
|
||||
Windows::Foundation::Size MinimumSize();
|
||||
@@ -129,6 +130,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void AdjustOpacity(const double opacity, const bool relative);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
@@ -152,6 +155,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TYPED_EVENT(WarningBell, IInspectable, IInspectable);
|
||||
// clang-format on
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
|
||||
|
||||
@@ -287,6 +292,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs);
|
||||
|
||||
winrt::fire_and_forget _hoveredHyperlinkChanged(IInspectable sender, IInspectable args);
|
||||
winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
|
||||
|
||||
void _coreFontSizeChanged(const int fontWidth,
|
||||
const int fontHeight,
|
||||
@@ -295,6 +301,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
|
||||
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
|
||||
void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args);
|
||||
|
||||
til::point _toPosInDips(const Core::Point terminalCellPos);
|
||||
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Microsoft.Terminal.Control
|
||||
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl,
|
||||
IDirectKeyListener,
|
||||
IMouseWheelListener,
|
||||
ICoreState
|
||||
ICoreState,
|
||||
Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
TermControl(IControlSettings settings,
|
||||
IControlAppearance unfocusedAppearance,
|
||||
@@ -53,6 +54,7 @@ namespace Microsoft.Terminal.Control
|
||||
void SelectAll();
|
||||
Boolean ToggleBlockSelection();
|
||||
void ToggleMarkMode();
|
||||
Boolean SwitchSelectionEndpoint();
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
void Close();
|
||||
Windows.Foundation.Size CharacterDimensions { get; };
|
||||
@@ -89,5 +91,6 @@ namespace Microsoft.Terminal.Control
|
||||
// opacity set by the settings should call this instead.
|
||||
Double BackgroundOpacity { get; };
|
||||
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1213,6 +1213,16 @@
|
||||
</ToolTipService.ToolTip>
|
||||
</Border>
|
||||
</Canvas>
|
||||
|
||||
<Canvas x:Name="SelectionCanvas"
|
||||
Visibility="Visible">
|
||||
<Path Name="SelectionStartMarker"
|
||||
Data="M 0 0 L 4 5.5996094 L 4 14 L 5 14 L 5 7 L 5 4.1992188 L 5 0 L 0 0 z "
|
||||
Visibility="Collapsed" />
|
||||
<Path Name="SelectionEndMarker"
|
||||
Data="M 0 0 L 0 4.1992188 L 0 7 L 0 14 L 1 14 L 1 5.5996094 L 5 0 L 0 0 z "
|
||||
Visibility="Collapsed" />
|
||||
</Canvas>
|
||||
</SwapChainPanel>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -147,10 +147,6 @@
|
||||
<PRIResource Include="Resources\en-US\Resources.resw" />
|
||||
<OCResourceDirectory Include="Resources" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(WindowsTerminalBranding)'=='' or '$(WindowsTerminalBranding)'=='Dev' or '$(WindowsTerminalBranding)'=='Preview'">
|
||||
<!-- GH#13252 Only vend this dependency for Dev and Preview builds. -->
|
||||
<SDKReference Include="Microsoft.Midi.GmDls, Version=10.0.22000.0" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\types\lib\types.vcxproj" />
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <winrt/Windows.ui.xaml.shapes.h>
|
||||
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.UI.Xaml.Shapes.h>
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.Terminal.Core.h>
|
||||
|
||||
@@ -45,8 +45,11 @@ Terminal::Terminal() :
|
||||
_snapOnInput{ true },
|
||||
_altGrAliasing{ true },
|
||||
_blockSelection{ false },
|
||||
_markMode{ false },
|
||||
_selectionMode{ SelectionInteractionMode::None },
|
||||
_isTargetingUrl{ false },
|
||||
_selection{ std::nullopt },
|
||||
_selectionEndpoint{ static_cast<SelectionEndpoint>(0) },
|
||||
_anchorInactiveSelectionEndpoint{ false },
|
||||
_taskbarState{ 0 },
|
||||
_taskbarProgress{ 0 },
|
||||
_trimBlockSelection{ false },
|
||||
@@ -561,17 +564,24 @@ bool Terminal::ShouldSendAlternateScroll(const unsigned int uiButton,
|
||||
// Method Description:
|
||||
// - Given a coord, get the URI at that location
|
||||
// Arguments:
|
||||
// - The position
|
||||
std::wstring Terminal::GetHyperlinkAtPosition(const til::point position)
|
||||
// - The position relative to the viewport
|
||||
std::wstring Terminal::GetHyperlinkAtViewportPosition(const til::point viewportPos)
|
||||
{
|
||||
auto attr = _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr();
|
||||
return GetHyperlinkAtBufferPosition(_ConvertToBufferCell(viewportPos));
|
||||
}
|
||||
|
||||
std::wstring Terminal::GetHyperlinkAtBufferPosition(const til::point bufferPos)
|
||||
{
|
||||
auto attr = _activeBuffer().GetCellDataAt(bufferPos)->TextAttr();
|
||||
if (attr.IsHyperlink())
|
||||
{
|
||||
auto uri = _activeBuffer().GetHyperlinkUriFromId(attr.GetHyperlinkId());
|
||||
return uri;
|
||||
}
|
||||
// also look through our known pattern locations in our pattern interval tree
|
||||
const auto result = GetHyperlinkIntervalFromPosition(position);
|
||||
auto viewportPos = bufferPos;
|
||||
_GetVisibleViewport().ConvertToOrigin(&viewportPos);
|
||||
const auto result = GetHyperlinkIntervalFromViewportPosition(viewportPos);
|
||||
if (result.has_value() && result->value == _hyperlinkPatternId)
|
||||
{
|
||||
const auto start = result->start;
|
||||
@@ -592,23 +602,23 @@ std::wstring Terminal::GetHyperlinkAtPosition(const til::point position)
|
||||
// Method Description:
|
||||
// - Gets the hyperlink ID of the text at the given terminal position
|
||||
// Arguments:
|
||||
// - The position of the text
|
||||
// - The position of the text relative to the viewport
|
||||
// Return value:
|
||||
// - The hyperlink ID
|
||||
uint16_t Terminal::GetHyperlinkIdAtPosition(const til::point position)
|
||||
uint16_t Terminal::GetHyperlinkIdAtViewportPosition(const til::point viewportPos)
|
||||
{
|
||||
return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(position))->TextAttr().GetHyperlinkId();
|
||||
return _activeBuffer().GetCellDataAt(_ConvertToBufferCell(viewportPos))->TextAttr().GetHyperlinkId();
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - Given a position in a URI pattern, gets the start and end coordinates of the URI
|
||||
// Arguments:
|
||||
// - The position
|
||||
// - The position relative to the viewport
|
||||
// Return value:
|
||||
// - The interval representing the start and end coordinates
|
||||
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromPosition(const til::point position)
|
||||
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos)
|
||||
{
|
||||
const auto results = _patternIntervalTree.findOverlapping({ position.X + 1, position.Y }, position);
|
||||
const auto results = _patternIntervalTree.findOverlapping({ viewportPos.X + 1, viewportPos.Y }, viewportPos);
|
||||
if (results.size() > 0)
|
||||
{
|
||||
for (const auto& result : results)
|
||||
@@ -1369,7 +1379,7 @@ void Terminal::SetCursorOn(const bool isOn)
|
||||
bool Terminal::IsCursorBlinkingAllowed() const noexcept
|
||||
{
|
||||
const auto& cursor = _activeBuffer().GetCursor();
|
||||
return !_markMode && cursor.IsBlinkingAllowed();
|
||||
return _selectionMode != SelectionInteractionMode::Mark && cursor.IsBlinkingAllowed();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -162,9 +162,10 @@ public:
|
||||
|
||||
void FocusChanged(const bool focused) noexcept override;
|
||||
|
||||
std::wstring GetHyperlinkAtPosition(const til::point position);
|
||||
uint16_t GetHyperlinkIdAtPosition(const til::point position);
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromPosition(const til::point position);
|
||||
std::wstring GetHyperlinkAtViewportPosition(const til::point viewportPos);
|
||||
std::wstring GetHyperlinkAtBufferPosition(const til::point bufferPos);
|
||||
uint16_t GetHyperlinkIdAtViewportPosition(const til::point viewportPos);
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromViewportPosition(const til::point viewportPos);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IBaseData(base to IRenderData and IUiaData)
|
||||
@@ -233,6 +234,14 @@ public:
|
||||
|
||||
#pragma region TextSelection
|
||||
// These methods are defined in TerminalSelection.cpp
|
||||
enum class SelectionInteractionMode
|
||||
{
|
||||
None,
|
||||
Mouse,
|
||||
Keyboard,
|
||||
Mark
|
||||
};
|
||||
|
||||
enum class SelectionDirection
|
||||
{
|
||||
Left,
|
||||
@@ -241,6 +250,12 @@ public:
|
||||
Down
|
||||
};
|
||||
|
||||
enum class SearchDirection
|
||||
{
|
||||
Forward,
|
||||
Backward
|
||||
};
|
||||
|
||||
enum class SelectionExpansion
|
||||
{
|
||||
Char,
|
||||
@@ -249,17 +264,30 @@ public:
|
||||
Viewport,
|
||||
Buffer
|
||||
};
|
||||
|
||||
enum class SelectionEndpoint
|
||||
{
|
||||
Start = 0x1,
|
||||
End = 0x2
|
||||
};
|
||||
|
||||
void MultiClickSelection(const til::point viewportPos, SelectionExpansion expansionMode);
|
||||
void SetSelectionAnchor(const til::point position);
|
||||
void SetSelectionEnd(const til::point position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
|
||||
void SetBlockSelection(const bool isEnabled) noexcept;
|
||||
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
|
||||
void SelectAll();
|
||||
bool IsInMarkMode() const;
|
||||
SelectionInteractionMode SelectionMode() const noexcept;
|
||||
void SwitchSelectionEndpoint();
|
||||
void ToggleMarkMode();
|
||||
void SelectHyperlink(const SearchDirection dir);
|
||||
bool IsTargetingUrl() const noexcept;
|
||||
|
||||
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
|
||||
UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const;
|
||||
til::point SelectionStartForRendering() const;
|
||||
til::point SelectionEndForRendering() const;
|
||||
const SelectionEndpoint SelectionEndpointTarget() const noexcept;
|
||||
|
||||
const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace);
|
||||
#pragma endregion
|
||||
@@ -329,7 +357,10 @@ private:
|
||||
bool _blockSelection;
|
||||
std::wstring _wordDelimiters;
|
||||
SelectionExpansion _multiClickSelectionMode;
|
||||
bool _markMode;
|
||||
SelectionInteractionMode _selectionMode;
|
||||
bool _isTargetingUrl;
|
||||
SelectionEndpoint _selectionEndpoint;
|
||||
bool _anchorInactiveSelectionEndpoint;
|
||||
#pragma endregion
|
||||
|
||||
std::unique_ptr<TextBuffer> _mainBuffer;
|
||||
@@ -403,6 +434,7 @@ private:
|
||||
std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const;
|
||||
std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;
|
||||
til::point _ConvertToBufferCell(const til::point viewportPos) const;
|
||||
void _ScrollToPoint(const til::point pos);
|
||||
void _MoveByChar(SelectionDirection direction, til::point& pos);
|
||||
void _MoveByWord(SelectionDirection direction, til::point& pos);
|
||||
void _MoveByViewport(SelectionDirection direction, til::point& pos);
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "unicode.hpp"
|
||||
|
||||
using namespace Microsoft::Terminal::Core;
|
||||
using namespace Microsoft::Console::Types;
|
||||
|
||||
DEFINE_ENUM_FLAG_OPERATORS(Terminal::SelectionEndpoint);
|
||||
|
||||
/* Selection Pivot Description:
|
||||
* The pivot helps properly update the selection when a user moves a selection over itself
|
||||
@@ -82,6 +85,49 @@ const til::point Terminal::GetSelectionEnd() const noexcept
|
||||
return _selection->end;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the viewport-relative position of where to draw the marker
|
||||
// for the selection's start endpoint
|
||||
til::point Terminal::SelectionStartForRendering() const
|
||||
{
|
||||
auto pos{ _selection->start };
|
||||
const auto bufferSize{ GetTextBuffer().GetSize() };
|
||||
if (pos.x != bufferSize.Left())
|
||||
{
|
||||
// In general, we need to draw the marker one before the
|
||||
// beginning of the selection.
|
||||
// When we're at the left boundary, we want to
|
||||
// flip the marker, so we skip this step.
|
||||
bufferSize.DecrementInBounds(pos);
|
||||
}
|
||||
pos.Y = base::ClampSub(pos.Y, _VisibleStartIndex());
|
||||
return til::point{ pos };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the viewport-relative position of where to draw the marker
|
||||
// for the selection's end endpoint
|
||||
til::point Terminal::SelectionEndForRendering() const
|
||||
{
|
||||
auto pos{ _selection->end };
|
||||
const auto bufferSize{ GetTextBuffer().GetSize() };
|
||||
if (pos.x != bufferSize.RightInclusive())
|
||||
{
|
||||
// In general, we need to draw the marker one after the
|
||||
// end of the selection.
|
||||
// When we're at the right boundary, we want to
|
||||
// flip the marker, so we skip this step.
|
||||
bufferSize.IncrementInBounds(pos);
|
||||
}
|
||||
pos.Y = base::ClampSub(pos.Y, _VisibleStartIndex());
|
||||
return til::point{ pos };
|
||||
}
|
||||
|
||||
const Terminal::SelectionEndpoint Terminal::SelectionEndpointTarget() const noexcept
|
||||
{
|
||||
return _selectionEndpoint;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if selection is active
|
||||
// Return Value:
|
||||
@@ -170,6 +216,7 @@ void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<Selec
|
||||
// expand both anchors
|
||||
std::tie(_selection->start, _selection->end) = expandedAnchors;
|
||||
}
|
||||
_selectionMode = SelectionInteractionMode::Mouse;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -182,7 +229,7 @@ void Terminal::SetSelectionEnd(const til::point viewportPos, std::optional<Selec
|
||||
// - the new start/end for a selection
|
||||
std::pair<til::point, til::point> Terminal::_PivotSelection(const til::point targetPos, bool& targetStart) const
|
||||
{
|
||||
if (targetStart = _activeBuffer().GetSize().CompareInBounds(targetPos, _selection->pivot) <= 0)
|
||||
if (targetStart = targetPos <= _selection->pivot)
|
||||
{
|
||||
// target is before pivot
|
||||
// treat target as start
|
||||
@@ -235,14 +282,14 @@ void Terminal::SetBlockSelection(const bool isEnabled) noexcept
|
||||
_blockSelection = isEnabled;
|
||||
}
|
||||
|
||||
bool Terminal::IsInMarkMode() const
|
||||
Terminal::SelectionInteractionMode Terminal::SelectionMode() const noexcept
|
||||
{
|
||||
return _markMode;
|
||||
return _selectionMode;
|
||||
}
|
||||
|
||||
void Terminal::ToggleMarkMode()
|
||||
{
|
||||
if (_markMode)
|
||||
if (_selectionMode == SelectionInteractionMode::Mark)
|
||||
{
|
||||
// Exit Mark Mode
|
||||
ClearSelection();
|
||||
@@ -257,14 +304,195 @@ void Terminal::ToggleMarkMode()
|
||||
_selection->start = cursorPos;
|
||||
_selection->end = cursorPos;
|
||||
_selection->pivot = cursorPos;
|
||||
_markMode = true;
|
||||
_ScrollToPoint(cursorPos);
|
||||
_selectionMode = SelectionInteractionMode::Mark;
|
||||
_blockSelection = false;
|
||||
_isTargetingUrl = false;
|
||||
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - switch the targeted selection endpoint with the other one (i.e. start <--> end)
|
||||
void Terminal::SwitchSelectionEndpoint()
|
||||
{
|
||||
if (IsSelectionActive())
|
||||
{
|
||||
if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) && WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
|
||||
{
|
||||
// moving cursor --> anchor start, move end
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
_anchorInactiveSelectionEndpoint = true;
|
||||
}
|
||||
else if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::End))
|
||||
{
|
||||
// moving end --> now we're moving start
|
||||
_selectionEndpoint = SelectionEndpoint::Start;
|
||||
_selection->pivot = _selection->end;
|
||||
}
|
||||
else if (WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start))
|
||||
{
|
||||
// moving start --> now we're moving end
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
_selection->pivot = _selection->start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - selects the next/previous hyperlink, if one is available
|
||||
// Arguments:
|
||||
// - dir: the direction we're scanning the buffer in to find the hyperlink of interest
|
||||
// Return Value:
|
||||
// - true if we found a hyperlink to select (and selected it). False otherwise.
|
||||
void Terminal::SelectHyperlink(const SearchDirection dir)
|
||||
{
|
||||
if (_selectionMode != SelectionInteractionMode::Mark)
|
||||
{
|
||||
// This feature only works in mark mode
|
||||
_isTargetingUrl = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 0. Useful tools/vars
|
||||
const auto bufferSize = _activeBuffer().GetSize();
|
||||
const auto viewportHeight = _GetMutableViewport().Height();
|
||||
|
||||
// The patterns are stored relative to the "search area". Initially, this search area will be the viewport,
|
||||
// but as we progressively search through more of the buffer, this will change.
|
||||
// Keep track of the search area here, and use the lambda below to convert points to the search area coordinate space.
|
||||
auto searchArea = _GetVisibleViewport();
|
||||
auto convertToSearchArea = [&searchArea](const til::point pt) {
|
||||
auto copy = pt;
|
||||
searchArea.ConvertToOrigin(©);
|
||||
return copy;
|
||||
};
|
||||
|
||||
// extracts the next/previous hyperlink from the list of hyperlink ranges provided
|
||||
auto extractResultFromList = [&](std::vector<interval_tree::Interval<til::point, size_t>>& list) {
|
||||
const auto selectionStartInSearchArea = convertToSearchArea(_selection->start);
|
||||
|
||||
std::optional<std::pair<til::point, til::point>> resultFromList;
|
||||
if (!list.empty())
|
||||
{
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
// pattern tree includes the currently selected range when going forward,
|
||||
// so we need to check if we're pointing to that one before returning it.
|
||||
auto range = list.front();
|
||||
if (_isTargetingUrl && range.start == selectionStartInSearchArea)
|
||||
{
|
||||
if (list.size() > 1)
|
||||
{
|
||||
// if we're pointing to the currently selected URL,
|
||||
// pick the next one.
|
||||
range = til::at(list, 1);
|
||||
resultFromList = { range.start, range.stop };
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOAD-BEARING: the only range here is the one that's currently selected.
|
||||
// Make sure this is set to nullopt so that we keep searching through the buffer.
|
||||
resultFromList = std::nullopt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not on currently selected range, return the first one
|
||||
resultFromList = { range.start, range.stop };
|
||||
}
|
||||
}
|
||||
else if (dir == SearchDirection::Backward)
|
||||
{
|
||||
// moving backwards excludes the currently selected range,
|
||||
// simply return the last one in the list as it's ordered
|
||||
const auto range = list.back();
|
||||
resultFromList = { range.start, range.stop };
|
||||
}
|
||||
}
|
||||
|
||||
// pattern tree stores everything as viewport coords,
|
||||
// so we need to convert them on the way out
|
||||
if (resultFromList)
|
||||
{
|
||||
searchArea.ConvertFromOrigin(&resultFromList->first);
|
||||
searchArea.ConvertFromOrigin(&resultFromList->second);
|
||||
}
|
||||
return resultFromList;
|
||||
};
|
||||
|
||||
// 1. Look for the hyperlink
|
||||
til::point searchStart = dir == SearchDirection::Forward ? _selection->start : til::point{ bufferSize.Left(), _VisibleStartIndex() };
|
||||
til::point searchEnd = dir == SearchDirection::Forward ? til::point{ bufferSize.RightInclusive(), _VisibleEndIndex() } : _selection->start;
|
||||
|
||||
// 1.A) Try searching the current viewport (no scrolling required)
|
||||
auto resultList = _patternIntervalTree.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd));
|
||||
std::optional<std::pair<til::point, til::point>> result = extractResultFromList(resultList);
|
||||
if (!result)
|
||||
{
|
||||
// 1.B) Incrementally search through more of the space
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
searchStart = { bufferSize.Left(), searchEnd.y + 1 };
|
||||
searchEnd = { bufferSize.RightInclusive(), std::min(searchStart.y + viewportHeight, ViewEndIndex()) };
|
||||
}
|
||||
else
|
||||
{
|
||||
searchEnd = { bufferSize.RightInclusive(), searchStart.y - 1 };
|
||||
searchStart = { bufferSize.Left(), std::max(searchStart.y - viewportHeight, bufferSize.Top()) };
|
||||
}
|
||||
searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1);
|
||||
|
||||
const til::point bufferStart{ bufferSize.Origin() };
|
||||
const til::point bufferEnd{ bufferSize.RightInclusive(), ViewEndIndex() };
|
||||
while (!result && bufferSize.IsInBounds(searchStart) && bufferSize.IsInBounds(searchEnd) && searchStart <= searchEnd && bufferStart <= searchStart && searchEnd <= bufferEnd)
|
||||
{
|
||||
auto patterns = _activeBuffer().GetPatterns(searchStart.y, searchEnd.y);
|
||||
resultList = patterns.findContained(convertToSearchArea(searchStart), convertToSearchArea(searchEnd));
|
||||
result = extractResultFromList(resultList);
|
||||
if (!result)
|
||||
{
|
||||
if (dir == SearchDirection::Forward)
|
||||
{
|
||||
searchStart.y += 1;
|
||||
searchEnd.y = std::min(searchStart.y + viewportHeight, ViewEndIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
searchEnd.y -= 1;
|
||||
searchStart.y = std::max(searchEnd.y - viewportHeight, bufferSize.Top());
|
||||
}
|
||||
searchArea = Viewport::FromDimensions(searchStart, searchEnd.x + 1, searchEnd.y + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 1.C) Nothing was found. Bail!
|
||||
if (!result.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Select the hyperlink
|
||||
_selection->start = result->first;
|
||||
_selection->pivot = result->first;
|
||||
_selection->end = result->second;
|
||||
bufferSize.DecrementInBounds(_selection->end);
|
||||
_isTargetingUrl = true;
|
||||
_selectionEndpoint = SelectionEndpoint::End;
|
||||
|
||||
// 3. Scroll to the selected area (if necessary)
|
||||
_ScrollToPoint(_selection->end);
|
||||
}
|
||||
|
||||
bool Terminal::IsTargetingUrl() const noexcept
|
||||
{
|
||||
return _isTargetingUrl;
|
||||
}
|
||||
|
||||
Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const
|
||||
{
|
||||
if ((_markMode || mods.IsShiftPressed()) && !mods.IsAltPressed())
|
||||
if ((_selectionMode == SelectionInteractionMode::Mark || mods.IsShiftPressed()) && !mods.IsAltPressed())
|
||||
{
|
||||
if (mods.IsCtrlPressed())
|
||||
{
|
||||
@@ -318,14 +546,34 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
|
||||
// - mods: the key modifiers pressed when performing this update
|
||||
void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods)
|
||||
{
|
||||
// 1. Figure out which endpoint to update
|
||||
// If we're in mark mode, shift dictates whether you are moving the end or not.
|
||||
// Otherwise, we're updating an existing selection, so one of the endpoints is the pivot,
|
||||
// signifying that the other endpoint is the one we want to move.
|
||||
const auto movingEnd{ _markMode ? mods.IsShiftPressed() : _selection->start == _selection->pivot };
|
||||
auto targetPos{ movingEnd ? _selection->end : _selection->start };
|
||||
// This is a special variable used to track if we should move the cursor when in mark mode.
|
||||
// We have special functionality where if you use the "switchSelectionEndpoint" action
|
||||
// when in mark mode (moving the cursor), we anchor an endpoint and you can use the
|
||||
// plain arrow keys to move the endpoint. This way, you don't have to hold shift anymore!
|
||||
const bool shouldMoveBothEndpoints = _selectionMode == SelectionInteractionMode::Mark && !_anchorInactiveSelectionEndpoint && !mods.IsShiftPressed();
|
||||
|
||||
// 2. Perform the movement
|
||||
// 1. Figure out which endpoint to update
|
||||
// [Mark Mode]
|
||||
// - shift pressed --> only move "end" (or "start" if "pivot" == "end")
|
||||
// - otherwise --> move both "start" and "end" (moving cursor)
|
||||
// [Quick Edit]
|
||||
// - just move "end" (or "start" if "pivot" == "end")
|
||||
_selectionEndpoint = static_cast<SelectionEndpoint>(0);
|
||||
if (shouldMoveBothEndpoints)
|
||||
{
|
||||
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
|
||||
}
|
||||
else if (_selection->start == _selection->pivot)
|
||||
{
|
||||
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
|
||||
}
|
||||
else if (_selection->end == _selection->pivot)
|
||||
{
|
||||
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::Start);
|
||||
}
|
||||
auto targetPos{ WI_IsFlagSet(_selectionEndpoint, SelectionEndpoint::Start) ? _selection->start : _selection->end };
|
||||
|
||||
// 2 Perform the movement
|
||||
switch (mode)
|
||||
{
|
||||
case SelectionExpansion::Char:
|
||||
@@ -342,43 +590,31 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. Actually modify the selection
|
||||
// NOTE: targetStart doesn't matter here
|
||||
if (_markMode)
|
||||
// 3. Actually modify the selection state
|
||||
_isTargetingUrl = false;
|
||||
_selectionMode = std::max(_selectionMode, SelectionInteractionMode::Keyboard);
|
||||
if (shouldMoveBothEndpoints)
|
||||
{
|
||||
// [Mark Mode]
|
||||
// - moveSelectionEnd --> just move end (i.e. shift + arrow keys)
|
||||
// - !moveSelectionEnd --> move all three (i.e. just use arrow keys)
|
||||
// [Mark Mode] + shift unpressed --> move all three (i.e. just use arrow keys)
|
||||
_selection->start = targetPos;
|
||||
_selection->end = targetPos;
|
||||
if (!movingEnd)
|
||||
{
|
||||
_selection->start = targetPos;
|
||||
_selection->pivot = targetPos;
|
||||
}
|
||||
_selection->pivot = targetPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto targetStart = false;
|
||||
// [Mark Mode] + shift --> updating a standard selection
|
||||
// This is also standard quick-edit modification
|
||||
bool targetStart = false;
|
||||
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
|
||||
|
||||
// IMPORTANT! Pivoting the selection here might have changed which endpoint we're targeting.
|
||||
// So let's set the targeted endpoint again.
|
||||
WI_UpdateFlag(_selectionEndpoint, SelectionEndpoint::Start, targetStart);
|
||||
WI_UpdateFlag(_selectionEndpoint, SelectionEndpoint::End, !targetStart);
|
||||
}
|
||||
|
||||
// 4. Scroll (if necessary)
|
||||
if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
|
||||
{
|
||||
if (const auto amtAboveView = viewport.Top() - targetPos.Y; amtAboveView > 0)
|
||||
{
|
||||
// anchor is above visible viewport, scroll by that amount
|
||||
_scrollOffset += amtAboveView;
|
||||
}
|
||||
else
|
||||
{
|
||||
// anchor is below visible viewport, scroll by that amount
|
||||
const auto amtBelowView = targetPos.Y - viewport.BottomInclusive();
|
||||
_scrollOffset -= amtBelowView;
|
||||
}
|
||||
_NotifyScrollEvent();
|
||||
_activeBuffer().TriggerScroll();
|
||||
}
|
||||
_ScrollToPoint(targetPos);
|
||||
}
|
||||
|
||||
void Terminal::SelectAll()
|
||||
@@ -388,6 +624,7 @@ void Terminal::SelectAll()
|
||||
_selection->start = bufferSize.Origin();
|
||||
_selection->end = { bufferSize.RightInclusive(), _GetMutableViewport().BottomInclusive() };
|
||||
_selection->pivot = _selection->end;
|
||||
_selectionMode = SelectionInteractionMode::Keyboard;
|
||||
}
|
||||
|
||||
void Terminal::_MoveByChar(SelectionDirection direction, til::point& pos)
|
||||
@@ -405,13 +642,16 @@ void Terminal::_MoveByChar(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Up:
|
||||
{
|
||||
const auto bufferSize{ _activeBuffer().GetSize() };
|
||||
pos = { pos.X, std::clamp(pos.Y - 1, bufferSize.Top(), bufferSize.BottomInclusive()) };
|
||||
const auto newY{ pos.Y - 1 };
|
||||
pos = newY < bufferSize.Top() ? bufferSize.Origin() : til::point{ pos.X, newY };
|
||||
break;
|
||||
}
|
||||
case SelectionDirection::Down:
|
||||
{
|
||||
const auto bufferSize{ _activeBuffer().GetSize() };
|
||||
pos = { pos.X, std::clamp(pos.Y + 1, bufferSize.Top(), bufferSize.BottomInclusive()) };
|
||||
const auto mutableBottom{ _GetMutableViewport().BottomInclusive() };
|
||||
const auto newY{ pos.Y + 1 };
|
||||
pos = newY > mutableBottom ? til::point{ bufferSize.RightInclusive(), mutableBottom } : til::point{ pos.X, newY };
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -424,7 +664,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Left:
|
||||
{
|
||||
const auto wordStartPos{ _activeBuffer().GetWordStart(pos, _wordDelimiters) };
|
||||
if (_activeBuffer().GetSize().CompareInBounds(_selection->pivot, pos) < 0)
|
||||
if (_selection->pivot < pos)
|
||||
{
|
||||
// If we're moving towards the pivot, move one more cell
|
||||
pos = wordStartPos;
|
||||
@@ -447,7 +687,7 @@ void Terminal::_MoveByWord(SelectionDirection direction, til::point& pos)
|
||||
case SelectionDirection::Right:
|
||||
{
|
||||
const auto wordEndPos{ _activeBuffer().GetWordEnd(pos, _wordDelimiters) };
|
||||
if (_activeBuffer().GetSize().CompareInBounds(pos, _selection->pivot) < 0)
|
||||
if (pos < _selection->pivot)
|
||||
{
|
||||
// If we're moving towards the pivot, move one more cell
|
||||
pos = _activeBuffer().GetWordEnd(pos, _wordDelimiters);
|
||||
@@ -529,7 +769,10 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos)
|
||||
void Terminal::ClearSelection()
|
||||
{
|
||||
_selection = std::nullopt;
|
||||
_markMode = false;
|
||||
_selectionMode = SelectionInteractionMode::None;
|
||||
_isTargetingUrl = false;
|
||||
_selectionEndpoint = static_cast<SelectionEndpoint>(0);
|
||||
_anchorInactiveSelectionEndpoint = false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -572,6 +815,30 @@ til::point Terminal::_ConvertToBufferCell(const til::point viewportPos) const
|
||||
return bufferPos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - if necessary, scroll the viewport such that the given point is visible
|
||||
// Arguments:
|
||||
// - pos: a coordinate relative to the buffer (not viewport)
|
||||
void Terminal::_ScrollToPoint(const til::point pos)
|
||||
{
|
||||
if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(pos))
|
||||
{
|
||||
if (const auto amtAboveView = visibleViewport.Top() - pos.Y; amtAboveView > 0)
|
||||
{
|
||||
// anchor is above visible viewport, scroll by that amount
|
||||
_scrollOffset += amtAboveView;
|
||||
}
|
||||
else
|
||||
{
|
||||
// anchor is below visible viewport, scroll by that amount
|
||||
const auto amtBelowView = pos.Y - visibleViewport.BottomInclusive();
|
||||
_scrollOffset -= amtBelowView;
|
||||
}
|
||||
_NotifyScrollEvent();
|
||||
_activeBuffer().TriggerScroll();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method won't be used. We just throw and do nothing. For now we
|
||||
// need this method to implement UiaData interface
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "EnumEntry.h"
|
||||
#include "GlobalAppearance.h"
|
||||
#include "GlobalAppearance.g.cpp"
|
||||
#include "GlobalAppearancePageNavigationState.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include <WtExeUtils.h>
|
||||
@@ -19,180 +17,13 @@ using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// For ComboBox an empty SelectedItem string denotes no selection.
|
||||
// What we want instead is for "Use system language" to be selected by default.
|
||||
// --> "und" is synonymous for "Use system language".
|
||||
constexpr std::wstring_view systemLanguageTag{ L"und" };
|
||||
|
||||
static constexpr std::array appLanguageTags{
|
||||
L"en-US",
|
||||
L"de-DE",
|
||||
L"es-ES",
|
||||
L"fr-FR",
|
||||
L"it-IT",
|
||||
L"ja",
|
||||
L"ko",
|
||||
L"pt-BR",
|
||||
L"qps-PLOC",
|
||||
L"qps-PLOCA",
|
||||
L"qps-PLOCM",
|
||||
L"ru",
|
||||
L"zh-Hans",
|
||||
L"zh-Hant",
|
||||
};
|
||||
|
||||
GlobalAppearance::GlobalAppearance()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(Theme, ElementTheme, winrt::Windows::UI::Xaml::ElementTheme, L"Globals_Theme", L"Content");
|
||||
INITIALIZE_BINDABLE_ENUM_SETTING(TabWidthMode, TabViewWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, L"Globals_TabWidthMode", L"Content");
|
||||
}
|
||||
|
||||
void GlobalAppearance::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_State = e.Parameter().as<Editor::GlobalAppearancePageNavigationState>();
|
||||
_ViewModel = e.Parameter().as<Editor::GlobalAppearanceViewModel>();
|
||||
}
|
||||
|
||||
winrt::hstring GlobalAppearance::LanguageDisplayConverter(const winrt::hstring& tag)
|
||||
{
|
||||
if (tag == systemLanguageTag)
|
||||
{
|
||||
return RS_(L"Globals_LanguageDefault");
|
||||
}
|
||||
|
||||
winrt::Windows::Globalization::Language language{ tag };
|
||||
return language.NativeName();
|
||||
}
|
||||
|
||||
// Returns whether the language selector is available/shown.
|
||||
//
|
||||
// winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride()
|
||||
// doesn't work for unpackaged applications. The corresponding code in TerminalApp is disabled.
|
||||
// It would be confusing for our users if we presented a dysfunctional language selector.
|
||||
bool GlobalAppearance::LanguageSelectorAvailable()
|
||||
{
|
||||
return IsPackaged();
|
||||
}
|
||||
|
||||
// Returns the list of languages the user may override the application language with.
|
||||
// The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}.
|
||||
// "und" is short for "undefined" and is synonymous for "Use system language" in this code.
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> GlobalAppearance::LanguageList()
|
||||
{
|
||||
if (_languageList)
|
||||
{
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
{
|
||||
_languageList = {};
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
// In order to return the language list this code does the following:
|
||||
// [1] Get all possible languages we want to allow the user to choose.
|
||||
// We have to acquire languages from multiple sources, creating duplicates. See below at [1].
|
||||
// [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order.
|
||||
// I wanted to sort the localized language names initially, but it turned out to be complex.
|
||||
// [3] Remove potential duplicates in our language list from [1].
|
||||
// We don't want to have en-US twice in the list, do we?
|
||||
// [4] Optionally remove unwanted language tags (like pseudo-localizations).
|
||||
|
||||
std::vector<winrt::hstring> tags;
|
||||
|
||||
// [1]:
|
||||
{
|
||||
// ManifestLanguages contains languages the app ships with.
|
||||
// Unfortunately, we cannot use this source. Our manifest must contain the
|
||||
// ~100 languages that are localized for the shell extension and start menu
|
||||
// presentation so we align with Windows display languages for those surfaces.
|
||||
// However, the actual content of our application is limited to a much smaller
|
||||
// subset of approximately 14 languages. As such, we will code the limited
|
||||
// subset of languages that we support for selection within the Settings
|
||||
// dropdown to steer users towards the ones that we can display in the app.
|
||||
|
||||
// As per the function definition, the first item
|
||||
// is always "Use system language" ("und").
|
||||
tags.emplace_back(systemLanguageTag);
|
||||
|
||||
// Add our hardcoded languages after the system definition.
|
||||
for (const auto& v : appLanguageTags)
|
||||
{
|
||||
tags.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: The size of tags is always >0, due to tags[0] being hardcoded to "und".
|
||||
const auto tagsBegin = ++tags.begin();
|
||||
const auto tagsEnd = tags.end();
|
||||
|
||||
// [2]:
|
||||
std::sort(tagsBegin, tagsEnd);
|
||||
|
||||
// I'd love for both, std::unique and std::remove_if, to occur in a single loop,
|
||||
// but the code turned out to be complex and even less maintainable, so I gave up.
|
||||
{
|
||||
// [3] part 1:
|
||||
auto it = std::unique(tagsBegin, tagsEnd);
|
||||
|
||||
// The qps- languages are useful for testing ("pseudo-localization").
|
||||
// --> Leave them in if debug features are enabled.
|
||||
if (!_State.Globals().DebugFeaturesEnabled())
|
||||
{
|
||||
// [4] part 1:
|
||||
it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool {
|
||||
return til::starts_with(tag, L"qps-");
|
||||
});
|
||||
}
|
||||
|
||||
// [3], [4] part 2 (completing the so called "erase-remove idiom"):
|
||||
tags.erase(it, tagsEnd);
|
||||
}
|
||||
|
||||
_languageList = winrt::single_threaded_observable_vector(std::move(tags));
|
||||
return _languageList;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable GlobalAppearance::CurrentLanguage()
|
||||
{
|
||||
if (_currentLanguage)
|
||||
{
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
if (!LanguageSelectorAvailable())
|
||||
{
|
||||
_currentLanguage = {};
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
// NOTE: PrimaryLanguageOverride throws if this instance is unpackaged.
|
||||
auto currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride();
|
||||
if (currentLanguage.empty())
|
||||
{
|
||||
currentLanguage = systemLanguageTag;
|
||||
}
|
||||
|
||||
_currentLanguage = winrt::box_value(currentLanguage);
|
||||
return _currentLanguage;
|
||||
}
|
||||
|
||||
void GlobalAppearance::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag)
|
||||
{
|
||||
_currentLanguage = tag;
|
||||
|
||||
const auto currentLanguage = winrt::unbox_value<winrt::hstring>(_currentLanguage);
|
||||
const auto globals = _State.Globals();
|
||||
if (currentLanguage == systemLanguageTag)
|
||||
{
|
||||
globals.ClearLanguage();
|
||||
}
|
||||
else
|
||||
{
|
||||
globals.Language(currentLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,20 +4,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "GlobalAppearance.g.h"
|
||||
#include "GlobalAppearancePageNavigationState.g.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct GlobalAppearancePageNavigationState : GlobalAppearancePageNavigationStateT<GlobalAppearancePageNavigationState>
|
||||
{
|
||||
public:
|
||||
GlobalAppearancePageNavigationState(const Model::GlobalAppSettings& settings) :
|
||||
_Globals{ settings } {}
|
||||
|
||||
WINRT_PROPERTY(Model::GlobalAppSettings, Globals, nullptr)
|
||||
};
|
||||
|
||||
struct GlobalAppearance : public HasScrollViewer<GlobalAppearance>, GlobalAppearanceT<GlobalAppearance>
|
||||
{
|
||||
public:
|
||||
@@ -25,24 +15,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
|
||||
GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals().Theme);
|
||||
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, State().Globals().TabWidthMode);
|
||||
|
||||
public:
|
||||
// LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
|
||||
// For instance "en-US" produces "English (United States)", while "de-DE" produces
|
||||
// "Deutsch (Deutschland)". This works independently of the user's locale.
|
||||
static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag);
|
||||
|
||||
bool LanguageSelectorAvailable();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> LanguageList();
|
||||
winrt::Windows::Foundation::IInspectable CurrentLanguage();
|
||||
void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
|
||||
|
||||
private:
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
|
||||
winrt::Windows::Foundation::IInspectable _currentLanguage;
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::GlobalAppearanceViewModel, ViewModel, _PropertyChangedHandlers, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user