Compare commits

...

62 Commits

Author SHA1 Message Date
Dustin L. Howett
d752d25145 Migrate spelling-0.0.21 changes from main 2022-05-09 06:49:36 -05:00
Mike Griese
4be58f9617 why is this not simple 2022-05-09 06:49:36 -05:00
Mike Griese
cb788b00fe Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/theming-2022-prototype 2022-05-06 15:28:47 -05:00
Mike Griese
f38dc5cf5a typos 2022-05-06 15:26:18 -05:00
Mike Griese
331b98f008 sensible defaults 2022-05-06 15:24:19 -05:00
Mike Griese
2712e462a1 Fix having a null window, cause that was actually very possible 2022-05-06 12:04:16 -05:00
Mike Griese
28119c7614 This is what I was looking for 2022-05-06 12:04:15 -05:00
Mike Griese
45fb4b15f7 Fill in more tests 2022-05-06 12:04:15 -05:00
Mike Griese
cae5218a7b Start by just adding a test file 2022-05-06 12:04:15 -05:00
Mike Griese
b2abca6027 Add comments 2022-05-06 06:44:45 -05:00
Mike Griese
3af471a93a derp 2022-05-05 18:06:00 -05:00
Mike Griese
e52b7a9c2b well, this was part of it... 2022-05-05 14:14:02 -05:00
Mike Griese
d73e450f14 this fixes the terminalBG + runtime change 2022-05-05 13:30:42 -05:00
Mike Griese
44319a1690 Fixes the Settings UI terminalBackground settings 2022-05-05 13:19:21 -05:00
Mike Griese
cd2fd43498 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/theming-2022-prototype 2022-05-05 11:33:50 -05:00
Mike Griese
342eb2b64d Removes the ability to add Mica in the titlebar, but good enough for v0 2022-05-04 14:59:08 -05:00
Mike Griese
6c28ba6bf3 make the code a lot cleaner 2022-05-03 15:24:03 -05:00
Mike Griese
99bc963222 this is simpler 2022-05-03 12:09:44 -05:00
Mike Griese
68d70a053b this is REALLY janky but seems to work 2022-05-03 12:04:14 -05:00
Mike Griese
3281a3a334 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/theming-2022-prototype 2022-05-03 09:33:23 -05:00
Mike Griese
24f945a7f5 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/theming-2022-prototype 2022-04-29 09:05:09 -05:00
Mike Griese
03da3b7cc8 runformat 2022-04-28 05:47:27 -05:00
Mike Griese
9b9a6440b5 spell 2022-04-28 05:29:27 -05:00
Mike Griese
a1ea0fdc4f forgot you 2022-04-28 05:27:00 -05:00
Mike Griese
fd2dd3a1b5 cleanup for the review 2022-04-27 16:38:49 -05:00
Mike Griese
446e17b833 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/theming-2022-prototype 2022-04-27 16:07:42 -05:00
Mike Griese
bc8bb8eb15 more comments 2022-04-27 16:05:06 -05:00
Mike Griese
e2318b4dad This is horrifying 2022-04-27 14:40:03 -05:00
Mike Griese
b6b9aeaaaa Use better display names 2022-04-27 13:01:26 -05:00
Mike Griese
a57187b93c include the themes 2022-04-27 12:33:41 -05:00
Mike Griese
cfb99d9720 Change theme with the SUI and actually have it persist 2022-04-27 12:17:50 -05:00
Mike Griese
9bebb74aef Some settings saving roundtripping bugs 2022-04-27 12:08:41 -05:00
Mike Griese
39b6fddea4 Some minor todos 2022-04-26 15:58:14 -05:00
Mike Griese
01d69072ad I think that's everything 2022-04-26 15:46:03 -05:00
Mike Griese
de22df01e5 switches when a tab does, not the pane 2022-04-26 15:01:58 -05:00
Mike Griese
606db76010 this is like 99% of the work here 2022-04-26 14:40:08 -05:00
Mike Griese
724360313f hey this is easier 2022-04-26 13:57:41 -05:00
Mike Griese
df0efffd8e stragglers 2022-04-26 12:35:47 -05:00
Mike Griese
7fcfda36d1 wow 2022-04-26 12:30:44 -05:00
Mike Griese
fe26114610 better, better, 2022-04-26 12:11:40 -05:00
Mike Griese
5081118bbb This is horrifying and yet I'm totally shipping it. Move the objects to be sub-properties of the Theme 2022-04-26 12:03:30 -05:00
Mike Griese
4f42661488 Lookup themes from a list of themes, use that one to style the window 2022-04-26 11:06:04 -05:00
Mike Griese
a7ce8603ea cleanup, works on start and when unset 2022-04-26 09:37:06 -05:00
Mike Griese
99dc75d872 this actually works now. Remember kids, break statements in switch/cases are NOT optional 2022-04-26 09:18:45 -05:00
Mike Griese
e22046fb42 accept accent color as a ThemeColor 2022-04-26 06:34:51 -05:00
Mike Griese
1d0a5d747d Merge remote-tracking branch 'origin/dev/migrie/titebar-colors' into dev/migrie/fhl/theming-2022-prototype 2022-04-26 05:59:48 -05:00
Mike Griese
03211c82cb Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/theming-2022-prototype 2022-04-26 05:38:56 -05:00
Mike Griese
ac49459dde change color based on if the window is activated or now 2022-04-26 04:52:04 -05:00
Mike Griese
cd0012a6b1 guess what, it works 2022-04-25 10:04:34 -05:00
Mike Griese
f92724dfb0 We can't be clever here. Just make an observable property and slam the brush in there 2022-04-25 09:25:33 -05:00
Mike Griese
44112fcfa6 really, no good progress here. It's rough 2022-03-28 11:04:45 -05:00
Mike Griese
9eff0a2e28 i feel like I'm getting closer 2022-03-25 11:03:59 -05:00
Mike Griese
ee9333af13 A pile of dead ends.
https://stackoverflow.com/questions/64694722/changing-themeresources-dynamically-in-uwp

That post looked SUPER promising. Problem is though, I CANNOT for the life of me
get that to work. Like, I can't get anything to `{Binding Brush, Mode=TwoWay,
Source={StaticResource TerminalBackground}}` to the `TerminalBackground` thing I
made there. I thought that was so clever.

I wanted an easy way to just change the value of a resource and have it update
the Titlebar, but since the Titlebar isn't a child of the TerminalPage, and this
binding thing didn't work, I think I'm at a dead end.
2022-03-25 10:45:18 -05:00
Mike Griese
01f07be64f okay so you can't change a solid brush to an acylic one at runtime 2022-03-25 05:45:52 -05:00
Mike Griese
c555e6efca this did seem to work, but will it work for acrylic? 2022-03-25 05:29:11 -05:00
Mike Griese
9670d139a6 mica works, but the titlebar looks DUMB 2022-03-24 11:25:49 -05:00
Mike Griese
84805f59aa dumbly accept alpha channel in colors 2022-03-24 10:54:59 -05:00
Mike Griese
9627483f56 okay so i bet that resource changed. Whatever, we'll work with it. 2022-03-24 10:41:00 -05:00
Mike Griese
b4cd7b3d71 this is better. Apphost is gonna need to be involved anyways so fuck it 2022-03-24 10:23:10 -05:00
Mike Griese
00c135b4d4 on reload, this changes the BG of the titlebar, but not the tab view 2022-03-24 10:15:12 -05:00
Mike Griese
7e0ae358fb this fixes the App bits as well 2022-03-24 06:45:52 -05:00
Mike Griese
15e4fd9be9 Settings project builds at least 2022-03-24 06:31:38 -05:00
59 changed files with 3034 additions and 978 deletions

15
.github/actions/spelling/README.md vendored Normal file
View 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.

View File

@@ -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>

View File

@@ -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
@@ -26,15 +27,19 @@ EDDC
Enum'd
Fitt
formattings
FTCS
ftp
fvar
gantt
gcc
geeksforgeeks
ghe
github
gje
godbolt
hostname
hostnames
https
hyperlink
hyperlinking
hyperlinks
@@ -44,19 +49,21 @@ inlined
It'd
kje
libfuzzer
libuv
liga
lje
Llast
llvm
Lmid
locl
lol
lorem
Lorigin
maxed
minimalistic
mkmk
mnt
mru
noreply
nje
noreply
ogonek
@@ -77,13 +84,16 @@ runtimes
shcha
slnt
Sos
ssh
timeline
timelines
timestamped
TLDR
tokenizes
tonos
toolset
tshe
ubuntu
uiatextrange
UIs
und
@@ -92,6 +102,7 @@ versioned
vsdevcmd
We'd
wildcards
XBox
YBox
yeru
zhe
allcolors

View File

@@ -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,10 +30,14 @@ dataobject
dcomp
DERR
dlldata
DNE
DONTADDTORECENT
DWORDLONG
DWMSBT
DWMWA
DWMWA
DWORDLONG
endfor
ENDSESSION
enumset
environstrings
EXPCMDFLAGS
@@ -71,6 +77,7 @@ IDirect
IExplorer
IFACEMETHOD
IFile
IGraphics
IInheritable
IMap
IMonarch
@@ -85,6 +92,7 @@ istream
IStringable
ITab
ITaskbar
itow
IUri
IVirtual
KEYSELECT
@@ -96,12 +104,15 @@ lround
Lsa
lsass
LSHIFT
LTGRAY
MAINWINDOW
memchr
memicmp
MENUCOMMAND
MENUDATA
MENUITEMINFOW
MENUINFO
MENUITEMINFOW
mmeapi
MOUSELEAVE
mov
mptt
@@ -137,16 +148,18 @@ OUTLINETEXTMETRICW
overridable
PACL
PAGESCROLL
PATINVERT
PEXPLICIT
PICKFOLDERS
pmr
ptstr
QUERYENDSESSION
rcx
REGCLS
RETURNCMD
rfind
roundf
ROOTOWNER
roundf
RSHIFT
SACL
schandle
@@ -155,7 +168,6 @@ serializer
SETVERSION
SHELLEXECUTEINFOW
shobjidl
SHOWDEFAULT
SHOWHIDE
SHOWMINIMIZED
SHOWTIP
@@ -177,6 +189,8 @@ Stubless
Subheader
Subpage
syscall
SYSTEMBACKDROP
TABROW
TASKBARCREATED
TBPF
THEMECHANGED
@@ -196,6 +210,8 @@ UOI
UPDATEINIFILE
userenv
USEROBJECTFLAGS
Viewbox
virtualalloc
wcsstr
wcstoui
winmain

View File

@@ -69,6 +69,8 @@ Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham

View 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
(['"]|&quot;)[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-)/

View File

@@ -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$

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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.*

View File

@@ -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 }}

View File

@@ -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>

View File

@@ -0,0 +1,280 @@
// 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 };
}
};
void ThemeTests::ParseSimpleTheme()
{
static constexpr std::string_view orangeTheme{ R"({
"name": "orange",
"tabRow":
{
"background": "#FFFF8800",
"unfocusedBackground": "#FF884400"
},
"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(rgb(0xff, 0x88, 0x00), theme->TabRow().Background().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": "#FF112233",
"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": "#FF112233",
"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;
DebugBreak();
throw ex;
}
catch (const SettingsTypedDeserializationException& e)
{
auto deserializationErrorMessage = til::u8u16(e.what());
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
DebugBreak();
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;
DebugBreak();
throw ex;
}
catch (const SettingsTypedDeserializationException& e)
{
auto deserializationErrorMessage = til::u8u16(e.what());
Log::Comment(NoThrowString().Format(deserializationErrorMessage.c_str()));
DebugBreak();
throw e;
}
}
}

View File

@@ -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"),
@@ -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>();
}
} };
@@ -743,7 +745,7 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
return _settings.GlobalSettings().Theme();
return _settings.GlobalSettings().CurrentTheme().RequestedTheme();
}
bool AppLogic::GetShowTabsInTitlebar()
@@ -964,7 +966,7 @@ namespace winrt::TerminalApp::implementation
void AppLogic::_RefreshThemeRoutine()
{
_ApplyTheme(_settings.GlobalSettings().Theme());
_ApplyTheme(_settings.GlobalSettings().CurrentTheme().RequestedTheme());
}
// Function Description:
@@ -1219,6 +1221,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;
@@ -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();
}
}

View File

@@ -117,6 +117,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,7 +129,13 @@ 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 ---------------------------------
// PropertyChanged is surprisingly not a typed event, so we'll define that one manually.
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::Windows::UI::Xaml::ElementTheme);
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);

View File

@@ -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();
@@ -94,6 +94,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 +107,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();

View File

@@ -3,8 +3,6 @@
#pragma once
#include "winrt/Microsoft.UI.Xaml.Controls.h"
#include "HighlightedTextSegment.g.h"
#include "HighlightedText.g.h"

View File

@@ -242,6 +242,10 @@
<value>&#x2022; Found a keybinding that was missing a required parameter value. This keybinding will be ignored.</value>
<comment>{Locked="&#x2022;"} This glyph is a bullet, used in a bulleted list.</comment>
</data>
<data name="UnknownTheme" xml:space="preserve">
<value>&#x2022; The specified "theme" was not found in the list of themes. Temporarily falling back to the default value.</value>
<comment>{Locked="&#x2022;"} 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>

View File

@@ -154,6 +154,8 @@ namespace winrt::TerminalApp::implementation
// Possibly update the icon of the tab.
page->_UpdateTabIcon(*tab);
page->_updateTabRowColors();
// 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.
@@ -915,6 +917,8 @@ namespace winrt::TerminalApp::implementation
_TitleChangedHandlers(*this, tab.Title());
}
_updateTabRowColors();
auto tab_impl = _GetTerminalTabImpl(tab);
if (tab_impl)
{

View File

@@ -160,43 +160,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);
@@ -219,6 +182,7 @@ namespace winrt::TerminalApp::implementation
// Inform the host that our titlebar content has changed.
_SetTitleBarContentHandlers(*this, _tabRow);
}
_updateTabRowColors();
// Hookup our event handlers to the ShortcutActionDispatch
_RegisterActionCallbacks();
@@ -1122,7 +1086,22 @@ namespace winrt::TerminalApp::implementation
connection.Initialize(valueSet);
}
else if (connectionType == TerminalConnection::PipeConnection::ConnectionType())
{
connection = TerminalConnection::PipeConnection();
StringMap envMap{};
auto newWorkingDirectory{ settings.StartingDirectory() };
auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
newWorkingDirectory,
settings.StartingTitle(),
envMap.GetView(),
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid());
connection.Initialize(valueSet);
}
else
{
// profile is guaranteed to exist here
@@ -1475,6 +1454,17 @@ namespace winrt::TerminalApp::implementation
term.ConnectionStateChanged({ get_weak(), &TerminalPage::_ConnectionStateChangedHandler });
auto weakThis{ get_weak() };
term.PropertyChanged([weakThis](auto& /*sender*/, auto& e) {
if (auto page{ weakThis.get() })
{
if (e.PropertyName() == L"BackgroundBrush")
{
page->_updateTabRowColors();
}
}
});
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
}
@@ -1514,37 +1504,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:
@@ -2742,6 +2704,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
_updateTabRowColors();
}
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
@@ -3126,32 +3095,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
@@ -3558,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>();
}
}
@@ -4065,4 +4009,133 @@ namespace winrt::TerminalApp::implementation
applicationState.DismissedMessages(std::move(messages));
}
void TerminalPage::_updateTabRowColors()
{
if (_settings == nullptr)
{
return;
}
const auto theme = _settings.GlobalSettings().CurrentTheme();
auto requestedTheme{ theme.RequestedTheme() };
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 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);
TitlebarBrush(acrylicBrush);
bgColor = backgroundColor;
}
else if (theme.TabRow() && theme.TabRow().Background())
{
const auto tabRowBg = theme.TabRow().Background();
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<Controls::Page>().Background();
}
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;
_updateTabRowColors();
}
}

View File

@@ -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{};
@@ -383,8 +388,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 +446,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 _updateTabRowColors();
winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
#pragma region ActionHandlers

View File

@@ -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;

View File

@@ -13,7 +13,6 @@
VerticalAlignment="Top"
d:DesignHeight="36"
d:DesignWidth="400"
Background="{ThemeResource TabViewBackground}"
SizeChanged="Root_SizeChanged"
mc:Ignorable="d">

View File

@@ -0,0 +1,501 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "PipeConnection.h"
#include <winternl.h>
#include "PipeConnection.g.cpp"
#include "CTerminalHandoff.h"
#include "../../types/inc/utils.hpp"
#include "../../types/inc/Environment.hpp"
#include "LibraryResources.h"
using namespace ::Microsoft::Console;
using namespace std::string_view_literals;
// Format is: "DecimalResult (HexadecimalForm)"
static constexpr auto _errorFormat = L"{0} ({0:#010x})"sv;
static constexpr winrt::guid PipeConnectionType = { 0xfffffdfa, 0xa479, 0x412c, { 0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62 } };
// Notes:
// There is a number of ways that the Conpty connection can be terminated (voluntarily or not):
// 1. The connection is Close()d
// 2. The pseudoconsole or process cannot be spawned during Start()
// 3. The client process exits with a code.
// (Successful (0) or any other code)
// 4. The read handle is terminated.
// (This usually happens when the pseudoconsole host crashes.)
// In each of these termination scenarios, we need to be mindful of tripping the others.
// Closing the pseudoconsole in response to the client exiting (3) can trigger (4).
// Close() (1) will cause the automatic triggering of (3) and (4).
// In a lot of cases, we use the connection state to stop "flapping."
//
// To figure out where we handle these, search for comments containing "EXIT POINT"
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
winrt::guid PipeConnection::ConnectionType() noexcept
{
return PipeConnectionType;
}
// Function Description:
// - launches the client application attached to the new pseudoconsole
HRESULT PipeConnection::_LaunchAttachedClient() noexcept
try
{
STARTUPINFOEX siEx{ 0 };
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
siEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
wil::unique_hfile outPipeOurSide, outPipePseudoConsoleSide;
wil::unique_hfile inPipeOurSide, inPipePseudoConsoleSide;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(&inPipePseudoConsoleSide, &inPipeOurSide, &saAttr, 0));
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(&outPipeOurSide, &outPipePseudoConsoleSide, &saAttr, 0));
SetHandleInformation(inPipeOurSide.get(), HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(outPipeOurSide.get(), HANDLE_FLAG_INHERIT, 0);
// RETURN_IF_FAILED(ConptyCreatePseudoConsole(size, inPipePseudoConsoleSide.get(), outPipePseudoConsoleSide.get(), dwFlags, phPC));
siEx.StartupInfo.hStdInput = inPipePseudoConsoleSide.get();
siEx.StartupInfo.hStdOutput = siEx.StartupInfo.hStdError = outPipePseudoConsoleSide.get();
auto inPipe = inPipeOurSide.release();
_inPipe.reset(inPipe);
_outPipe.reset(outPipeOurSide.release());
auto cmdline{ wil::ExpandEnvironmentStringsW<std::wstring>(_commandline.c_str()) }; // mutable copy -- required for CreateProcessW
Utils::EnvironmentVariableMapW environment;
auto zeroEnvMap = wil::scope_exit([&]() noexcept {
// Can't zero the keys, but at least we can zero the values.
for (auto& [name, value] : environment)
{
::SecureZeroMemory(value.data(), value.size() * sizeof(decltype(value.begin())::value_type));
}
environment.clear();
});
// Populate the environment map with the current environment.
RETURN_IF_FAILED(Utils::UpdateEnvironmentMapW(environment));
{
// Convert connection Guid to string and ignore the enclosing '{}'.
auto wsGuid{ Utils::GuidToString(_guid) };
wsGuid.pop_back();
const auto guidSubStr = std::wstring_view{ wsGuid }.substr(1);
// Ensure every connection has the unique identifier in the environment.
environment.insert_or_assign(L"WT_SESSION", guidSubStr.data());
if (_environment)
{
// add additional WT env vars like WT_SETTINGS, WT_DEFAULTS and WT_PROFILE_ID
for (auto item : _environment)
{
try
{
auto key = item.Key();
// This will throw if the value isn't a string. If that
// happens, then just skip this entry.
auto value = winrt::unbox_value<hstring>(item.Value());
// avoid clobbering WSLENV
if (std::wstring_view{ key } == L"WSLENV")
{
auto current = environment[L"WSLENV"];
value = current + L":" + value;
}
environment.insert_or_assign(key.c_str(), value.c_str());
}
CATCH_LOG();
}
}
// WSLENV is a colon-delimited list of environment variables (+flags) that should appear inside WSL
// https://devblogs.microsoft.com/commandline/share-environment-vars-between-wsl-and-windows/
auto wslEnv = environment[L"WSLENV"];
wslEnv = L"WT_SESSION:" + wslEnv; // prepend WT_SESSION to make sure it's visible inside WSL.
environment.insert_or_assign(L"WSLENV", wslEnv);
}
std::vector<wchar_t> newEnvVars;
auto zeroNewEnv = wil::scope_exit([&]() noexcept {
::SecureZeroMemory(newEnvVars.data(),
newEnvVars.size() * sizeof(decltype(newEnvVars.begin())::value_type));
});
RETURN_IF_FAILED(Utils::EnvironmentMapToEnvironmentStringsW(environment, newEnvVars));
auto lpEnvironment = newEnvVars.empty() ? nullptr : newEnvVars.data();
// If we have a startingTitle, create a mutable character buffer to add
// it to the STARTUPINFO.
std::wstring mutableTitle{};
if (!_startingTitle.empty())
{
mutableTitle = _startingTitle;
siEx.StartupInfo.lpTitle = mutableTitle.data();
}
auto [newCommandLine, newStartingDirectory] = Utils::MangleStartingDirectoryForWSL(cmdline, _startingDirectory);
const auto startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr;
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr,
newCommandLine.data(),
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
true, // bInheritHandles
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
lpEnvironment, // lpEnvironment
startingDirectory,
&siEx.StartupInfo, // lpStartupInfo
&_piClient // lpProcessInformation
));
DeleteProcThreadAttributeList(siEx.lpAttributeList);
const std::filesystem::path processName = wil::GetModuleFileNameExW<std::wstring>(_piClient.hProcess, nullptr);
_clientName = processName.filename().wstring();
#pragma warning(suppress : 26477 26485 26494 26482 26446) // We don't control TraceLoggingWrite
TraceLoggingWrite(
g_hTerminalConnectionProvider,
"ConPtyConnected",
TraceLoggingDescription("Event emitted when ConPTY connection is started"),
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingWideString(_clientName.c_str(), "Client", "The attached client process"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
return S_OK;
}
CATCH_RETURN();
void PipeConnection::Initialize(const Windows::Foundation::Collections::ValueSet& settings)
{
if (settings)
{
// For the record, the following won't crash:
// auto bad = unbox_value_or<hstring>(settings.TryLookup(L"foo").try_as<IPropertyValue>(), nullptr);
// It'll just return null
_commandline = winrt::unbox_value_or<winrt::hstring>(settings.TryLookup(L"commandline").try_as<Windows::Foundation::IPropertyValue>(), _commandline);
_startingDirectory = winrt::unbox_value_or<winrt::hstring>(settings.TryLookup(L"startingDirectory").try_as<Windows::Foundation::IPropertyValue>(), _startingDirectory);
_startingTitle = winrt::unbox_value_or<winrt::hstring>(settings.TryLookup(L"startingTitle").try_as<Windows::Foundation::IPropertyValue>(), _startingTitle);
_initialRows = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialRows").try_as<Windows::Foundation::IPropertyValue>(), _initialRows);
_initialCols = winrt::unbox_value_or<uint32_t>(settings.TryLookup(L"initialCols").try_as<Windows::Foundation::IPropertyValue>(), _initialCols);
_guid = winrt::unbox_value_or<winrt::guid>(settings.TryLookup(L"guid").try_as<Windows::Foundation::IPropertyValue>(), _guid);
_environment = settings.TryLookup(L"environment").try_as<Windows::Foundation::Collections::ValueSet>();
if constexpr (Feature_VtPassthroughMode::IsEnabled())
{
_passthroughMode = winrt::unbox_value_or<bool>(settings.TryLookup(L"passthroughMode").try_as<Windows::Foundation::IPropertyValue>(), _passthroughMode);
}
}
if (_guid == guid{})
{
_guid = Utils::CreateGuid();
}
}
winrt::guid PipeConnection::Guid() const noexcept
{
return _guid;
}
winrt::hstring PipeConnection::Commandline() const
{
return _commandline;
}
void PipeConnection::Start()
try
{
_transitionToState(ConnectionState::Connecting);
THROW_IF_FAILED(_LaunchAttachedClient());
_startTime = std::chrono::high_resolution_clock::now();
// Create our own output handling thread
// This must be done after the pipes are populated.
// Each connection needs to make sure to drain the output from its backing host.
_hOutputThread.reset(CreateThread(
nullptr,
0,
[](LPVOID lpParameter) noexcept {
const auto pInstance = static_cast<PipeConnection*>(lpParameter);
if (pInstance)
{
return pInstance->_OutputThread();
}
return gsl::narrow_cast<DWORD>(E_INVALIDARG);
},
this,
0,
nullptr));
THROW_LAST_ERROR_IF_NULL(_hOutputThread);
LOG_IF_FAILED(SetThreadDescription(_hOutputThread.get(), L"PipeConnection Output Thread"));
_clientExitWait.reset(CreateThreadpoolWait(
[](PTP_CALLBACK_INSTANCE /*callbackInstance*/, PVOID context, PTP_WAIT /*wait*/, TP_WAIT_RESULT /*waitResult*/) noexcept {
const auto pInstance = static_cast<PipeConnection*>(context);
if (pInstance)
{
pInstance->_ClientTerminated();
}
},
this,
nullptr));
SetThreadpoolWait(_clientExitWait.get(), _piClient.hProcess, nullptr);
_transitionToState(ConnectionState::Connected);
}
catch (...)
{
// EXIT POINT
const auto hr = wil::ResultFromCaughtException();
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
winrt::hstring failureText{ fmt::format(std::wstring_view{ RS_(L"ProcessFailedToLaunch") },
fmt::format(_errorFormat, static_cast<unsigned int>(hr)),
_commandline) };
_TerminalOutputHandlers(failureText);
// If the path was invalid, let's present an informative message to the user
if (hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY))
{
winrt::hstring badPathText{ fmt::format(std::wstring_view{ RS_(L"BadPathText") },
_startingDirectory) };
_TerminalOutputHandlers(L"\r\n");
_TerminalOutputHandlers(badPathText);
}
_transitionToState(ConnectionState::Failed);
// Tear down any state we may have accumulated.
// _hPC.reset();
}
// Method Description:
// - prints out the "process exited" message formatted with the exit code
// Arguments:
// - status: the exit code.
void PipeConnection::_indicateExitWithStatus(unsigned int status) noexcept
{
try
{
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status)) };
_TerminalOutputHandlers(L"\r\n");
_TerminalOutputHandlers(exitText);
}
CATCH_LOG();
}
// Method Description:
// - called when the client application (not necessarily its pty) exits for any reason
void PipeConnection::_ClientTerminated() noexcept
try
{
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
// This termination was expected.
return;
}
// EXIT POINT
DWORD exitCode{ 0 };
GetExitCodeProcess(_piClient.hProcess, &exitCode);
// Signal the closing or failure of the process.
// Load bearing. Terminating the pseudoconsole will make the output thread exit unexpectedly,
// so we need to signal entry into the correct closing state before we do that.
_transitionToState(exitCode == 0 ? ConnectionState::Closed : ConnectionState::Failed);
// Close the pseudoconsole and wait for all output to drain.
// _hPC.reset();
if (auto localOutputThreadHandle = std::move(_hOutputThread))
{
LOG_LAST_ERROR_IF(WAIT_FAILED == WaitForSingleObject(localOutputThreadHandle.get(), INFINITE));
}
_indicateExitWithStatus(exitCode);
_piClient.reset();
}
CATCH_LOG()
void PipeConnection::WriteInput(const hstring& data)
{
if (!_isConnected())
{
return;
}
// convert from UTF-16LE to UTF-8 as ConPty expects UTF-8
// TODO GH#3378 reconcile and unify UTF-8 converters
auto str = winrt::to_string(data);
LOG_IF_WIN32_BOOL_FALSE(WriteFile(_inPipe.get(), str.c_str(), (DWORD)str.length(), nullptr, nullptr));
}
void PipeConnection::Resize(uint32_t /*rows*/, uint32_t /*columns*/)
{
}
void PipeConnection::ClearBuffer()
{
}
void PipeConnection::ShowHide(const bool /*show*/)
{
}
void PipeConnection::ReparentWindow(const uint64_t /*newParent*/)
{
}
void PipeConnection::Close() noexcept
try
{
if (_transitionToState(ConnectionState::Closing))
{
// EXIT POINT
_clientExitWait.reset(); // immediately stop waiting for the client to exit.
// _hPC.reset(); // tear down the pseudoconsole (this is like clicking X on a console window)
_inPipe.reset(); // break the pipes
_outPipe.reset();
if (_hOutputThread)
{
// Tear down our output thread -- now that the output pipe was closed on the
// far side, we can run down our local reader.
LOG_LAST_ERROR_IF(WAIT_FAILED == WaitForSingleObject(_hOutputThread.get(), INFINITE));
_hOutputThread.reset();
}
if (_piClient.hProcess)
{
// Wait for the client to terminate (which it should do successfully)
LOG_LAST_ERROR_IF(WAIT_FAILED == WaitForSingleObject(_piClient.hProcess, INFINITE));
_piClient.reset();
}
_transitionToState(ConnectionState::Closed);
}
}
CATCH_LOG()
DWORD PipeConnection::_OutputThread()
{
// Keep us alive until the output thread terminates; the destructor
// won't wait for us, and the known exit points _do_.
auto strongThis{ get_strong() };
// process the data of the output pipe in a loop
while (true)
{
DWORD read{};
const auto readFail{ !ReadFile(_outPipe.get(), _buffer.data(), gsl::narrow_cast<DWORD>(_buffer.size()), &read, nullptr) };
if (readFail) // reading failed (we must check this first, because read will also be 0.)
{
const auto lastError = GetLastError();
if (lastError != ERROR_BROKEN_PIPE && !_isStateAtOrBeyond(ConnectionState::Closing))
{
// EXIT POINT
_indicateExitWithStatus(HRESULT_FROM_WIN32(lastError)); // print a message
_transitionToState(ConnectionState::Failed);
return gsl::narrow_cast<DWORD>(HRESULT_FROM_WIN32(lastError));
}
// else we call convertUTF8ChunkToUTF16 with an empty string_view to convert possible remaining partials to U+FFFD
}
const auto result{ til::u8u16(std::string_view{ _buffer.data(), read }, _u16Str, _u8State) };
if (FAILED(result))
{
if (_isStateAtOrBeyond(ConnectionState::Closing))
{
// This termination was expected.
return 0;
}
// EXIT POINT
_indicateExitWithStatus(result); // print a message
_transitionToState(ConnectionState::Failed);
return gsl::narrow_cast<DWORD>(result);
}
if (_u16Str.empty())
{
return 0;
}
if (!_receivedFirstByte)
{
const auto now = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> delta = now - _startTime;
#pragma warning(suppress : 26477 26485 26494 26482 26446) // We don't control TraceLoggingWrite
TraceLoggingWrite(g_hTerminalConnectionProvider,
"ReceivedFirstByte",
TraceLoggingDescription("An event emitted when the connection receives the first byte"),
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingFloat64(delta.count(), "Duration"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
_receivedFirstByte = true;
}
// Pass the output to our registered event handlers
_TerminalOutputHandlers(_u16Str);
}
return 0;
}
// Function Description:
// - This function will be called (by C++/WinRT) after the final outstanding reference to
// any given connection instance is released.
// When a client application exits, its termination will wait for the output thread to
// run down. However, because our teardown is somewhat complex, our last reference may
// be owned by the very output thread that the client wait threadpool is blocked on.
// During destruction, we'll try to release any outstanding handles--including the one
// we have to the threadpool wait. As you might imagine, this takes us right to deadlock
// city.
// Deferring the final destruction of the connection to a background thread that can't
// be awaiting our destruction breaks the deadlock.
// Arguments:
// - connection: the final living reference to an outgoing connection
winrt::fire_and_forget PipeConnection::final_release(std::unique_ptr<PipeConnection> connection)
{
co_await winrt::resume_background(); // move to background
connection.reset(); // explicitly destruct
}
}

View File

@@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "PipeConnection.g.h"
#include "ConnectionStateHolder.h"
#include <conpty-static.h>
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
struct PipeConnection : PipeConnectionT<PipeConnection>, ConnectionStateHolder<PipeConnection>
{
static winrt::guid ConnectionType() noexcept;
PipeConnection() noexcept = default;
void Initialize(const Windows::Foundation::Collections::ValueSet& settings);
static winrt::fire_and_forget final_release(std::unique_ptr<PipeConnection> connection);
void Start();
void WriteInput(const hstring& data);
void Resize(uint32_t rows, uint32_t columns);
void Close() noexcept;
void ClearBuffer();
void ShowHide(const bool show);
void ReparentWindow(const uint64_t newParent);
winrt::guid Guid() const noexcept;
winrt::hstring Commandline() const;
WINRT_CALLBACK(TerminalOutput, TerminalOutputHandler);
private:
HRESULT _LaunchAttachedClient() noexcept;
void _indicateExitWithStatus(unsigned int status) noexcept;
void _ClientTerminated() noexcept;
uint32_t _initialRows{};
uint32_t _initialCols{};
uint64_t _initialParentHwnd{ 0 };
hstring _commandline{};
hstring _startingDirectory{};
hstring _startingTitle{};
bool _initialVisibility{ false };
Windows::Foundation::Collections::ValueSet _environment{ nullptr };
guid _guid{}; // A unique session identifier for connected client
hstring _clientName{}; // The name of the process hosted by this ConPTY connection (as of launch).
bool _receivedFirstByte{ false };
std::chrono::high_resolution_clock::time_point _startTime{};
wil::unique_hfile _inPipe; // The pipe for writing input to
wil::unique_hfile _outPipe; // The pipe for reading output from
wil::unique_handle _hOutputThread;
wil::unique_process_information _piClient;
// wil::unique_static_pseudoconsole_handle _hPC;
wil::unique_threadpool_wait _clientExitWait;
til::u8state _u8State{};
std::wstring _u16Str{};
std::array<char, 4096> _buffer{};
bool _passthroughMode{};
DWORD _OutputThread();
};
}
namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation
{
BASIC_FACTORY(PipeConnection);
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ITerminalConnection.idl";
namespace Microsoft.Terminal.TerminalConnection
{
[default_interface] runtimeclass PipeConnection : ITerminalConnection
{
static Guid ConnectionType { get; };
PipeConnection();
Guid Guid { get; };
String Commandline { get; };
void ClearBuffer();
void ShowHide(Boolean show);
void ReparentWindow(UInt64 newParent);
};
}

View File

@@ -32,6 +32,9 @@
<ClInclude Include="EchoConnection.h">
<DependentUpon>EchoConnection.idl</DependentUpon>
</ClInclude>
<ClInclude Include="PipeConnection.h">
<DependentUpon>PipeConnection.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CTerminalHandoff.cpp" />
@@ -51,6 +54,9 @@
<ClCompile Include="ConptyConnection.cpp">
<DependentUpon>ConptyConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="PipeConnection.cpp">
<DependentUpon>PipeConnection.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
@@ -59,6 +65,7 @@
<Midl Include="ConptyConnection.idl" />
<Midl Include="EchoConnection.idl" />
<Midl Include="AzureConnection.idl" />
<Midl Include="PipeConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources\en-US\Resources.resw">

View File

@@ -503,6 +503,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
RootGrid().Background(solidColor);
}
BackgroundBrush(RootGrid().Background());
}
// Method Description:
@@ -548,6 +550,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
solidColor.Color(bg);
}
BackgroundBrush(RootGrid().Background());
// Don't use the normal BackgroundBrush() Observable Property setter
// here. 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:

View File

@@ -118,6 +118,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);
@@ -141,6 +143,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

View File

@@ -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,
@@ -86,5 +87,6 @@ namespace Microsoft.Terminal.Control
// opacity set by the settings should call this instead.
Double BackgroundOpacity { get; };
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
}
}

View File

@@ -41,17 +41,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
L"zh-Hant-TW",
};
GlobalAppearance::GlobalAppearance()
GlobalAppearance::GlobalAppearance() :
_ThemeList{ single_threaded_observable_vector<Model::Theme>() }
{
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>();
_UpdateThemeList();
}
winrt::hstring GlobalAppearance::LanguageDisplayConverter(const winrt::hstring& tag)
@@ -195,4 +196,62 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
// Function Description:
// - Updates the list of all themes available to choose from.
void GlobalAppearance::_UpdateThemeList()
{
// Surprisingly, though this is called every time we navigate to the page,
// the list does not keep growing on each navigation.
const auto& ThemeMap{ _State.Globals().Themes() };
for (const auto& pair : ThemeMap)
{
const auto& theme{ pair.Value() };
_ThemeList.Append(theme);
}
}
winrt::Windows::Foundation::IInspectable GlobalAppearance::CurrentTheme()
{
return _State.Globals().CurrentTheme();
}
// Get the name out of the newly selected item, stash that as the Theme name
// set for the globals. That controls which theme is actually the current
// theme.
void GlobalAppearance::CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag)
{
if (const auto& theme{ tag.try_as<Model::Theme>() })
{
_State.Globals().Theme(theme.Name());
}
}
// Method Description:
// - Convert the names of the inbox themes to some more descriptive,
// well-known values. If the passed in theme isn't an inbox one, then just
// return its set Name.
// - "light" becomes "Light"
// - "dark" becomes "Dark"
// - "system" becomes "Use Windows theme"
// - These values are all localized based on the app language.
// Arguments:
// - theme: the theme to get the display name for.
// Return Value:
// - the potentially localized name to use for this Theme.
winrt::hstring GlobalAppearance::WellKnownThemeNameConverter(const Model::Theme& theme)
{
if (theme.Name() == L"dark")
{
return RS_(L"Globals_ThemeDark/Content");
}
if (theme.Name() == L"light")
{
return RS_(L"Globals_ThemeLight/Content");
}
if (theme.Name() == L"system")
{
return RS_(L"Globals_ThemeSystem/Content");
}
return theme.Name();
}
}

View File

@@ -26,9 +26,10 @@ 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);
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Model::Theme>, ThemeList, nullptr);
public:
// LanguageDisplayConverter maps the given BCP 47 tag to a localized string.
// For instance "en-US" produces "English (United States)", while "de-DE" produces
@@ -40,9 +41,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::Windows::Foundation::IInspectable CurrentLanguage();
void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag);
winrt::Windows::Foundation::IInspectable CurrentTheme();
void CurrentTheme(const winrt::Windows::Foundation::IInspectable& tag);
static winrt::hstring WellKnownThemeNameConverter(const Model::Theme& theme);
private:
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _languageList;
winrt::Windows::Foundation::IInspectable _currentLanguage;
winrt::Windows::Foundation::IInspectable _currentTheme;
void _UpdateThemeList();
};
}

View File

@@ -21,7 +21,8 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentLanguage;
IInspectable CurrentTheme;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> ThemeList { get; };
static String WellKnownThemeNameConverter(Microsoft.Terminal.Settings.Model.Theme theme);
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.Theme> ThemeList { get; };
IInspectable CurrentTabWidthMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };

View File

@@ -8,6 +8,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Microsoft.Terminal.Settings.Model"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">
@@ -42,10 +43,15 @@
<!-- Theme -->
<local:SettingContainer x:Uid="Globals_Theme">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ThemeList, Mode=OneWay}"
SelectedItem="{x:Bind CurrentTheme, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}" />
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="model:Theme">
<TextBlock Text="{x:Bind local:GlobalAppearance.WellKnownThemeNameConverter((model:Theme)), Mode=OneWay}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</local:SettingContainer>
<!-- Always show tabs -->

View File

@@ -408,6 +408,7 @@ void CascadiaSettings::_validateSettings()
_validateMediaResources();
_validateKeybindings();
_validateColorSchemesInCommands();
_validateThemeExists();
}
// Method Description:
@@ -1136,3 +1137,14 @@ void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)
}
CATCH_LOG();
}
void CascadiaSettings::_validateThemeExists()
{
if (!_globals->Themes().HasKey(_globals->Theme()))
{
_warnings.Append(SettingsLoadWarnings::UnknownTheme);
// safely fall back to system as the theme.
_globals->Theme(L"system");
}
}

View File

@@ -73,6 +73,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const Json::Value& colorSchemes;
const Json::Value& profileDefaults;
const Json::Value& profilesList;
const Json::Value& themes;
};
static std::pair<size_t, size_t> _lineAndColumnFromPosition(const std::string_view& string, const size_t position);
@@ -157,6 +158,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void _validateKeybindings() const;
void _validateColorSchemesInCommands() const;
bool _hasInvalidColorScheme(const Model::Command& command) const;
void _validateThemeExists();
// user settings
winrt::com_ptr<implementation::GlobalAppSettings> _globals = winrt::make_self<implementation::GlobalAppSettings>();

View File

@@ -39,6 +39,7 @@ static constexpr std::string_view ProfilesKey{ "profiles" };
static constexpr std::string_view DefaultSettingsKey{ "defaults" };
static constexpr std::string_view ProfilesListKey{ "list" };
static constexpr std::string_view SchemesKey{ "schemes" };
static constexpr std::string_view ThemesKey{ "themes" };
static constexpr std::wstring_view jsonExtension{ L".json" };
static constexpr std::wstring_view FragmentsSubDirectory{ L"\\Fragments" };
@@ -529,6 +530,23 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source
}
}
{
for (const auto& themeJson : json.themes)
{
if (const auto theme = Theme::FromJson(themeJson))
{
if (origin != OriginTag::InBox &&
(theme->Name() == L"system" || theme->Name() == L"light" || theme->Name() == L"dark"))
{
// If the theme didn't come from the in box themes, and it's
// name was one of the reserved names, then just ignore it.
continue;
}
settings.globals->AddTheme(*theme);
}
}
}
{
settings.baseLayerProfile = Profile::FromJson(json.profileDefaults);
// Remove the `guid` member from the default settings.
@@ -627,10 +645,11 @@ SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view&
{
auto root = content.empty() ? Json::Value{ Json::ValueType::objectValue } : _parseJSON(content);
const auto& colorSchemes = _getJSONValue(root, SchemesKey);
const auto& themes = _getJSONValue(root, ThemesKey);
const auto& profilesObject = _getJSONValue(root, ProfilesKey);
const auto& profileDefaults = _getJSONValue(profilesObject, DefaultSettingsKey);
const auto& profilesList = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList };
return JsonSettings{ std::move(root), colorSchemes, profileDefaults, profilesList, themes };
}
// Just a common helper function between _parse and _parseFragment.
@@ -1055,6 +1074,20 @@ Json::Value CascadiaSettings::ToJson() const
}
json[JsonKey(SchemesKey)] = schemes;
Json::Value themes{ Json::ValueType::arrayValue };
for (const auto& entry : _globals->Themes())
{
// Ignore the built in themes, when serializing the themes back out. We
// don't want to re-include them in the user settings file.
const auto theme{ winrt::get_self<Theme>(entry.Value()) };
if (theme->Name() == L"system" || theme->Name() == L"light" || theme->Name() == L"dark")
{
continue;
}
themes.append(theme->ToJson());
}
json[JsonKey(ThemesKey)] = themes;
return json;
}

View File

@@ -17,6 +17,7 @@ using namespace winrt::Microsoft::UI::Xaml::Controls;
static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" };
static constexpr std::string_view ActionsKey{ "actions" };
static constexpr std::string_view ThemeKey{ "theme" };
static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" };
@@ -39,6 +40,14 @@ void GlobalAppSettings::_FinalizeInheritance()
_colorSchemes.Insert(k, v);
}
}
for (const auto& [k, v] : parent->_themes)
{
if (!_themes.HasKey(k))
{
_themes.Insert(k, v);
}
}
}
}
@@ -65,6 +74,14 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_colorSchemes.Insert(kv.Key(), *schemeImpl->Copy());
}
}
if (_themes)
{
for (auto kv : _themes)
{
const auto themeImpl{ winrt::get_self<implementation::Theme>(kv.Value()) };
globals->_themes.Insert(kv.Key(), *themeImpl->Copy());
}
}
for (const auto& parent : _parents)
{
@@ -192,3 +209,17 @@ Json::Value GlobalAppSettings::ToJson() const
json[JsonKey(ActionsKey)] = _actionMap->ToJson();
return json;
}
winrt::Microsoft::Terminal::Settings::Model::Theme GlobalAppSettings::CurrentTheme() noexcept
{
return _themes.HasKey(Theme()) ? _themes.Lookup(Theme()) : nullptr;
}
void GlobalAppSettings::AddTheme(const Model::Theme& theme)
{
_themes.Insert(theme.Name(), theme);
}
winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::Theme> GlobalAppSettings::Themes() noexcept
{
return _themes.GetView();
}

View File

@@ -22,6 +22,7 @@ Author(s):
#include "ActionMap.h"
#include "Command.h"
#include "ColorScheme.h"
#include "Theme.h"
// fwdecl unittest classes
namespace SettingsModelLocalTests
@@ -62,6 +63,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
DisableAnimations(!invertedDisableAnimationsValue);
}
Windows::Foundation::Collections::IMapView<hstring, Model::Theme> Themes() noexcept;
void AddTheme(const Model::Theme& theme);
Model::Theme CurrentTheme() noexcept;
INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L"");
#define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
@@ -78,7 +83,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
winrt::guid _defaultProfile;
winrt::com_ptr<implementation::ActionMap> _actionMap{ winrt::make_self<implementation::ActionMap>() };
std::vector<SettingsLoadWarnings> _keybindingsWarnings;
Windows::Foundation::Collections::IMap<winrt::hstring, Model::ColorScheme> _colorSchemes{ winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>() };
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Theme> _themes{ winrt::single_threaded_map<winrt::hstring, Model::Theme>() };
};
}

View File

@@ -3,6 +3,7 @@
#include "IInheritable.idl.h"
import "Theme.idl";
import "ColorScheme.idl";
import "ActionMap.idl";
@@ -54,7 +55,6 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar);
INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs);
INHERITABLE_SETTING(String, Language);
INHERITABLE_SETTING(Windows.UI.Xaml.ElementTheme, Theme);
INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode);
INHERITABLE_SETTING(Boolean, UseAcrylicInTabRow);
INHERITABLE_SETTING(Boolean, ShowTabsInTitlebar);
@@ -94,5 +94,10 @@ namespace Microsoft.Terminal.Settings.Model
void RemoveColorScheme(String schemeName);
ActionMap ActionMap { get; };
Windows.Foundation.Collections.IMapView<String, Theme> Themes();
void AddTheme(Theme theme);
INHERITABLE_SETTING(String, Theme);
Theme CurrentTheme { get; };
}
}

View File

@@ -33,8 +33,8 @@ Author(s):
X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \
X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \
X(bool, ConfirmCloseAllTabs, "confirmCloseAllTabs", true) \
X(hstring, Theme, "theme") \
X(hstring, Language, "language") \
X(winrt::Windows::UI::Xaml::ElementTheme, Theme, "theme", winrt::Windows::UI::Xaml::ElementTheme::Default) \
X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \
X(bool, UseAcrylicInTabRow, "useAcrylicInTabRow", false) \
X(bool, ShowTabsInTitlebar, "showTabsInTitlebar", true) \
@@ -112,3 +112,14 @@ Author(s):
// Intentionally omitted Appearance settings:
// * ForegroundKey, BackgroundKey, SelectionBackgroundKey, CursorColorKey: all optional colors
// * Opacity: needs special parsing
#define MTSM_THEME_SETTINGS(X) \
X(winrt::Microsoft::Terminal::Settings::Model::WindowTheme, Window, "window", nullptr) \
X(winrt::Microsoft::Terminal::Settings::Model::TabRowTheme, TabRow, "tabRow", nullptr)
#define MTSM_THEME_WINDOW_SETTINGS(X) \
X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "applicationTheme", winrt::Windows::UI::Xaml::ElementTheme::Default) \
X(bool, UseMica, "useMica", false)
#define MTSM_THEME_TABROW_SETTINGS(X) \
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr)

View File

@@ -48,6 +48,9 @@
<ClInclude Include="ColorScheme.h">
<DependentUpon>ColorScheme.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Theme.h">
<DependentUpon>Theme.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Command.h">
<DependentUpon>Command.idl</DependentUpon>
</ClInclude>
@@ -128,6 +131,9 @@
<ClCompile Include="ColorScheme.cpp">
<DependentUpon>ColorScheme.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Theme.cpp">
<DependentUpon>Theme.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Command.cpp">
<DependentUpon>Command.idl</DependentUpon>
</ClCompile>
@@ -173,6 +179,7 @@
<Midl Include="ApplicationState.idl" />
<Midl Include="CascadiaSettings.idl" />
<Midl Include="ColorScheme.idl" />
<Midl Include="Theme.idl" />
<Midl Include="Command.idl" />
<Midl Include="DefaultTerminal.idl" />
<Midl Include="GlobalAppSettings.idl" />

View File

@@ -546,3 +546,76 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage)
pair_type{ "setAsDefault", ValueType::SetAsDefault },
};
};
template<>
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<winrt::Microsoft::Terminal::Settings::Model::ThemeColor>
{
winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromJson(const Json::Value& json)
{
if (json == Json::Value::null)
{
return nullptr;
}
const auto string{ Detail::GetStringView(json) };
if (string == "accent")
{
return winrt::Microsoft::Terminal::Settings::Model::ThemeColor::FromAccent();
}
else if (string == "terminalBackground")
{
return winrt::Microsoft::Terminal::Settings::Model::ThemeColor::FromTerminalBackground();
}
else
{
return winrt::Microsoft::Terminal::Settings::Model::ThemeColor::FromColor(::Microsoft::Console::Utils::ColorFromHexString(string));
}
}
bool CanConvert(const Json::Value& json)
{
if (json == Json::Value::null)
{
return true;
}
if (!json.isString())
{
return false;
}
const auto string{ Detail::GetStringView(json) };
const auto isColorSpec = (string.length() == 9 || string.length() == 7 || string.length() == 4) && string.front() == '#';
const auto isAccent = string == "accent";
const auto isTerminalBackground = string == "terminalBackground";
return isColorSpec || isAccent || isTerminalBackground;
}
Json::Value ToJson(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& val)
{
if (val == nullptr)
{
return Json::Value::null;
}
switch (val.ColorType())
{
case winrt::Microsoft::Terminal::Settings::Model::ThemeColorType::Accent:
{
return "accent";
}
case winrt::Microsoft::Terminal::Settings::Model::ThemeColorType::Color:
{
return til::u16u8(til::color{ val.Color() }.ToHexString(false));
}
case winrt::Microsoft::Terminal::Settings::Model::ThemeColorType::TerminalBackground:
{
return "terminalBackground";
}
}
return til::u16u8(til::color{ val.Color() }.ToHexString(false));
}
std::string TypeDescription() const
{
return "ThemeColor (#rrggbb, #rgb, #aarrggbb, accent, terminalBackground)";
}
};

View File

@@ -21,6 +21,7 @@ namespace Microsoft.Terminal.Settings.Model
InvalidSplitSize,
FailedToParseStartupActions,
FailedToParseSubCommands,
UnknownTheme,
WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder.
};

View File

@@ -0,0 +1,339 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "Theme.h"
#include "../../types/inc/Utils.hpp"
#include "../../types/inc/colorTable.hpp"
#include "Utils.h"
#include "JsonUtils.h"
#include "TerminalSettingsSerializationHelpers.h"
#include "ThemeColor.g.cpp"
#include "WindowTheme.g.cpp"
#include "TabRowTheme.g.cpp"
#include "Theme.g.cpp"
using namespace ::Microsoft::Console;
using namespace Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
using namespace winrt::Windows::UI;
static constexpr std::string_view NameKey{ "name" };
static constexpr wchar_t RegKeyDwm[] = L"Software\\Microsoft\\Windows\\DWM";
static constexpr wchar_t RegKeyAccentColor[] = L"AccentColor";
winrt::Microsoft::Terminal::Settings::Model::ThemeColor ThemeColor::FromColor(const winrt::Microsoft::Terminal::Core::Color& coreColor) noexcept
{
auto result = winrt::make_self<implementation::ThemeColor>();
result->_Color = coreColor;
result->_ColorType = ThemeColorType::Color;
return *result;
}
winrt::Microsoft::Terminal::Settings::Model::ThemeColor ThemeColor::FromAccent() noexcept
{
auto result = winrt::make_self<implementation::ThemeColor>();
result->_ColorType = ThemeColorType::Accent;
return *result;
}
winrt::Microsoft::Terminal::Settings::Model::ThemeColor ThemeColor::FromTerminalBackground() noexcept
{
auto result = winrt::make_self<implementation::ThemeColor>();
result->_ColorType = ThemeColorType::TerminalBackground;
return *result;
}
static wil::unique_hkey openDwmRegKey()
{
HKEY hKey{ nullptr };
if (RegOpenKeyEx(HKEY_CURRENT_USER, RegKeyDwm, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
return wil::unique_hkey{ hKey };
}
return nullptr;
}
static DWORD readDwmSubValue(const wil::unique_hkey& dwmRootKey, const wchar_t* key)
{
DWORD val{ 0 };
DWORD size{ sizeof(val) };
LOG_IF_FAILED(RegQueryValueExW(dwmRootKey.get(), key, nullptr, nullptr, reinterpret_cast<BYTE*>(&val), &size));
return val;
}
static til::color _getAccentColorForTitlebar()
{
// The color used for the "Use Accent color in the title bar" in DWM is
// stored in HKCU\Software\Microsoft\Windows\DWM\AccentColor.
return til::color{ static_cast<COLORREF>(readDwmSubValue(openDwmRegKey(), RegKeyAccentColor)) };
}
til::color ThemeColor::ColorFromBrush(const winrt::Windows::UI::Xaml::Media::Brush& brush)
{
if (auto acrylic = brush.try_as<winrt::Windows::UI::Xaml::Media::AcrylicBrush>())
{
return acrylic.TintColor();
}
else if (auto solidColor = brush.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>())
{
return solidColor.Color();
}
return {};
}
winrt::Windows::UI::Xaml::Media::Brush ThemeColor::Evaluate(const winrt::Windows::UI::Xaml::ResourceDictionary& res,
const winrt::Windows::UI::Xaml::Media::Brush& terminalBackground,
const bool forTitlebar)
{
static const auto accentColorKey{ winrt::box_value(L"SystemAccentColor") };
// NOTE: Currently, the DWM titlebar is always drawn, underneath our XAML
// content. If the opacity is <1.0, the you'll be able to see it, including
// the original caption buttons, which we don't want.
switch (ColorType())
{
case ThemeColorType::Accent:
{
til::color accentColor;
if (forTitlebar)
{
accentColor = _getAccentColorForTitlebar();
}
else
{
accentColor = winrt::unbox_value<winrt::Windows::UI::Color>(res.Lookup(accentColorKey));
}
const auto accentBrush = winrt::Windows::UI::Xaml::Media::SolidColorBrush();
accentBrush.Color(accentColor);
if (forTitlebar)
{
accentBrush.Opacity(1.0);
}
return accentBrush;
}
case ThemeColorType::Color:
{
const auto solidBrush = winrt::Windows::UI::Xaml::Media::SolidColorBrush();
solidBrush.Color(forTitlebar ? Color().with_alpha(255) : Color());
return solidBrush;
}
case ThemeColorType::TerminalBackground:
{
// If we're evaluating this color for the tab row, there are some rules
// we have to follow, unfortunately. We can't allow a transparent
// background, so we have to make sure to fill that in with Opacity(1.0)
// manually.
//
// So for that case, just make a new brush with the relevant properties
// set.
if (forTitlebar)
{
if (auto acrylic = terminalBackground.try_as<winrt::Windows::UI::Xaml::Media::AcrylicBrush>())
{
winrt::Windows::UI::Xaml::Media::AcrylicBrush newBrush{};
newBrush.TintColor(acrylic.TintColor());
newBrush.FallbackColor(acrylic.FallbackColor());
newBrush.TintLuminosityOpacity(acrylic.TintLuminosityOpacity());
// Allow acrylic opacity, but it's gotta be HostBackdrop acrylic.
//
// For now, just always use 50% opacity for this. If we do ever
// figure out how to get rid of our titlebar under the XAML tab
// row (GH#10509), we can always get rid of the HostBackdrop
// thing, and all this copying, and just return the
// terminalBackground brush directly.
//
// Because we're wholesale copying the brush, we won't be able
// to adjust it's opacity with the mouse wheel. This seems like
// an acceptable tradeoff for now.
newBrush.TintOpacity(.5);
newBrush.BackgroundSource(winrt::Windows::UI::Xaml::Media::AcrylicBackgroundSource::HostBackdrop);
return newBrush;
}
else if (auto solidColor = terminalBackground.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>())
{
winrt::Windows::UI::Xaml::Media::SolidColorBrush newBrush{};
newBrush.Color(til::color{ solidColor.Color() }.with_alpha(255));
return newBrush;
}
}
return terminalBackground;
}
}
return nullptr;
}
#define THEME_SETTINGS_FROM_JSON(type, name, jsonKey, ...) \
{ \
std::optional<type> _val; \
_val = JsonUtils::GetValueForKey<std::optional<type>>(json, jsonKey); \
if (_val) \
result->name(*_val); \
}
#define THEME_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
JsonUtils::SetValueForKey(json, jsonKey, val.name());
#define THEME_OBJECT_CONVERTER(nameSpace, name, macro) \
template<> \
struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<nameSpace::name> \
{ \
nameSpace::name FromJson(const Json::Value& json) \
{ \
if (json == Json::Value::null) \
return nullptr; \
auto result = winrt::make_self<nameSpace::implementation::name>(); \
macro(THEME_SETTINGS_FROM_JSON); \
return *result; \
} \
\
bool CanConvert(const Json::Value& json) \
{ \
return json.isObject(); \
} \
\
Json::Value ToJson(const nameSpace::name& val) \
{ \
if (val == nullptr) \
return Json::Value::null; \
Json::Value json{ Json::ValueType::objectValue }; \
macro(THEME_SETTINGS_TO_JSON); \
return json; \
} \
\
std::string TypeDescription() const \
{ \
return "name (You should never see this)"; \
} \
};
THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
#undef THEME_SETTINGS_FROM_JSON
#undef THEME_SETTINGS_TO_JSON
#undef THEME_OBJECT_CONVERTER
Theme::Theme() noexcept :
Theme{ winrt::Windows::UI::Xaml::ElementTheme::Default }
{
}
Theme::Theme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) noexcept
{
auto window{ winrt::make_self<implementation::WindowTheme>() };
window->RequestedTheme(requestedTheme);
_Window = *window;
}
winrt::com_ptr<Theme> Theme::Copy() const
{
auto theme{ winrt::make_self<Theme>() };
theme->_Name = _Name;
if (_Window)
{
theme->_Window = *winrt::get_self<implementation::WindowTheme>(_Window)->Copy();
}
if (_TabRow)
{
theme->_TabRow = *winrt::get_self<implementation::TabRowTheme>(_TabRow)->Copy();
}
return theme;
}
// Method Description:
// - Create a new instance of this class from a serialized JsonObject.
// Arguments:
// - json: an object which should be a serialization of a ColorScheme object.
// Return Value:
// - Returns nullptr for invalid JSON.
winrt::com_ptr<Theme> Theme::FromJson(const Json::Value& json)
{
auto result = winrt::make_self<Theme>();
result->LayerJson(json);
return result;
}
void Theme::LayerJson(const Json::Value& json)
{
if (json.isString())
{
// We found a string, not an object. Just secretly promote that string
// to a theme object with just the applicationTheme set from that value.
JsonUtils::GetValue(json, _Name);
winrt::Windows::UI::Xaml::ElementTheme requestedTheme{ winrt::Windows::UI::Xaml::ElementTheme::Default };
JsonUtils::GetValue(json, requestedTheme);
auto window{ winrt::make_self<implementation::WindowTheme>() };
window->RequestedTheme(requestedTheme);
_Window = *window;
return;
}
JsonUtils::GetValueForKey(json, NameKey, _Name);
// This will use each of the ConversionTrait's from above to quickly parse the sub-objects
#define THEME_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
{ \
std::optional<type> _val; \
_val = JsonUtils::GetValueForKey<std::optional<type>>(json, jsonKey); \
if (_val) \
_##name = *_val; \
else \
_##name = nullptr; \
}
MTSM_THEME_SETTINGS(THEME_SETTINGS_LAYER_JSON)
#undef THEME_SETTINGS_LAYER_JSON
}
// Method Description:
// - Create a new serialized JsonObject from an instance of this class
// Arguments:
// - <none>
// Return Value:
// - the JsonObject representing this instance
Json::Value Theme::ToJson() const
{
Json::Value json{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(json, NameKey, _Name);
// Don't serialize anything if the object is null.
#define THEME_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
if (_##name) \
JsonUtils::SetValueForKey(json, jsonKey, _##name);
MTSM_THEME_SETTINGS(THEME_SETTINGS_TO_JSON)
#undef THEME_SETTINGS_TO_JSON
return json;
}
winrt::hstring Theme::ToString()
{
return Name();
}
// Method Description:
// - A helper for retrieving the RequestedTheme out of the window property.
// There's a bunch of places throughout the app that all ask for the
// RequestedTheme, this saves some hassle. If there wasn't a `window` defined
// for this theme, this'll quickly just return `system`, to use the OS theme.
// Return Value:
// - the set applicationTheme for this Theme, otherwise the system theme.
winrt::Windows::UI::Xaml::ElementTheme Theme::RequestedTheme() const noexcept
{
return _Window ? _Window.RequestedTheme() : winrt::Windows::UI::Xaml::ElementTheme::Default;
}

View File

@@ -0,0 +1,110 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- Theme.hpp
Abstract:
- A Theme represents a collection of settings which control the appearance of
the Terminal window itself. Things like the color of the titlebar, the style
of the tabs.
Author(s):
- Mike Griese - March 2022
--*/
#pragma once
#include "../../inc/conattrs.hpp"
#include "DefaultSettings.h"
#include "IInheritable.h"
#include "MTSMSettings.h"
#include "ThemeColor.g.h"
#include "WindowTheme.g.h"
#include "TabRowTheme.g.h"
#include "Theme.g.h"
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct ThemeColor : ThemeColorT<ThemeColor>
{
public:
ThemeColor() noexcept = default;
static winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromColor(const winrt::Microsoft::Terminal::Core::Color& coreColor) noexcept;
static winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromAccent() noexcept;
static winrt::Microsoft::Terminal::Settings::Model::ThemeColor FromTerminalBackground() noexcept;
static til::color ColorFromBrush(const winrt::Windows::UI::Xaml::Media::Brush& brush);
winrt::Windows::UI::Xaml::Media::Brush Evaluate(const winrt::Windows::UI::Xaml::ResourceDictionary& res,
const winrt::Windows::UI::Xaml::Media::Brush& terminalBackground,
const bool forTitlebar);
WINRT_PROPERTY(til::color, Color);
WINRT_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::ThemeColorType, ColorType);
};
#define THEME_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
WINRT_PROPERTY(type, name, ##__VA_ARGS__)
#define THEME_SETTINGS_COPY(type, name, jsonKey, ...) \
result->_##name = _##name;
#define COPY_THEME_OBJECT(T, macro) \
winrt::com_ptr<T> Copy() \
{ \
auto result{ winrt::make_self<T>() }; \
macro(THEME_SETTINGS_COPY); \
return result; \
}
struct WindowTheme : WindowThemeT<WindowTheme>
{
MTSM_THEME_WINDOW_SETTINGS(THEME_SETTINGS_INITIALIZE);
public:
COPY_THEME_OBJECT(WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
};
struct TabRowTheme : TabRowThemeT<TabRowTheme>
{
MTSM_THEME_TABROW_SETTINGS(THEME_SETTINGS_INITIALIZE);
public:
COPY_THEME_OBJECT(TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
};
struct Theme : ThemeT<Theme>
{
public:
Theme() noexcept;
Theme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) noexcept;
com_ptr<Theme> Copy() const;
hstring ToString();
static com_ptr<Theme> FromJson(const Json::Value& json);
void LayerJson(const Json::Value& json);
Json::Value ToJson() const;
winrt::Windows::UI::Xaml::ElementTheme RequestedTheme() const noexcept;
WINRT_PROPERTY(winrt::hstring, Name);
MTSM_THEME_SETTINGS(THEME_SETTINGS_INITIALIZE)
private:
};
#undef THEME_SETTINGS_INITIALIZE
#undef THEME_SETTINGS_COPY
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(ThemeColor);
BASIC_FACTORY(Theme);
}

View File

@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Model
{
enum ThemeColorType
{
Accent,
Color,
TerminalBackground
};
runtimeclass ThemeColor
{
ThemeColor();
static ThemeColor FromColor(Microsoft.Terminal.Core.Color color);
static ThemeColor FromAccent();
static ThemeColor FromTerminalBackground();
Microsoft.Terminal.Core.Color Color { get; };
ThemeColorType ColorType;
static Microsoft.Terminal.Core.Color ColorFromBrush(Windows.UI.Xaml.Media.Brush brush);
Windows.UI.Xaml.Media.Brush Evaluate(Windows.UI.Xaml.ResourceDictionary res,
Windows.UI.Xaml.Media.Brush terminalBackground,
Boolean forTitlebar);
}
runtimeclass WindowTheme {
Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
Boolean UseMica { get; };
}
runtimeclass TabRowTheme {
ThemeColor Background { get; };
}
[default_interface] runtimeclass Theme : Windows.Foundation.IStringable {
Theme();
Theme(Windows.UI.Xaml.ElementTheme requestedTheme);
String Name;
// window.* Namespace
WindowTheme Window { get; };
// tabRow.* Namespace
TabRowTheme TabRow { get; };
// A helper for retrieving the RequestedTheme out of the window property
Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
}
}

View File

@@ -280,6 +280,26 @@
"brightWhite": "#EEEEEC"
}
],
"themes": [
{
"name": "light",
"window":{
"applicationTheme": "light"
}
},
{
"name": "dark",
"window":{
"applicationTheme": "dark"
}
},
{
"name": "system",
"window":{
"applicationTheme": "system"
}
}
],
"actions":
[
// Application-level Keys

View File

@@ -390,6 +390,11 @@ void AppHost::Initialize()
}
});
// Load bearing: make sure the PropertyChanged handler is added before we
// call Create, so that when the app sets up the titlebar brush, we're
// already prepared to listen for the change notification
_revokers.PropertyChanged = _logic.PropertyChanged(winrt::auto_revoke, { this, &AppHost::_PropertyChangedHandler });
_logic.Create();
_revokers.TitleChanged = _logic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged });
@@ -702,8 +707,12 @@ void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspecta
{
if (_useNonClientArea)
{
(static_cast<NonClientIslandWindow*>(_window.get()))->SetTitlebarContent(arg);
auto nonClientWindow{ static_cast<NonClientIslandWindow*>(_window.get()) };
nonClientWindow->SetTitlebarContent(arg);
nonClientWindow->SetTitlebarBackground(_logic.TitlebarBrush());
}
_updateTheme();
}
// Method Description:
@@ -714,9 +723,9 @@ void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspecta
// - arg: the ElementTheme to use as the new theme for the UI
// Return Value:
// - <none>
void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg)
void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& /*arg*/)
{
_window->OnApplicationThemeChanged(arg);
_updateTheme();
}
void AppHost::_FocusModeChanged(const winrt::Windows::Foundation::IInspectable&,
@@ -906,8 +915,15 @@ void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable&
args.ResultTargetWindowName(targetWindow.WindowName());
}
winrt::fire_and_forget AppHost::_WindowActivated()
winrt::fire_and_forget AppHost::_WindowActivated(bool activated)
{
_logic.WindowActivated(activated);
if (!activated)
{
co_return;
}
co_await winrt::resume_background();
if (auto peasant{ _windowManager.CurrentWindow() })
@@ -1330,6 +1346,16 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou
}
}
void AppHost::_updateTheme()
{
auto theme = _logic.Theme();
_window->OnApplicationThemeChanged(theme.RequestedTheme());
int attribute = theme.Window().UseMica() ? /* DWMSBT_MAINWINDOW */ 2 : /*DWMSBT_NONE*/ 1;
DwmSetWindowAttribute(_window->GetHandle(), /* DWMWA_SYSTEMBACKDROP_TYPE */ 38, &attribute, sizeof(attribute));
}
void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
@@ -1361,6 +1387,7 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta
}
_window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea());
_updateTheme();
}
void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&,
@@ -1576,6 +1603,16 @@ void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*
_logic.CloseWindow(pos);
}
void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& e)
{
if (e.PropertyName() == L"TitlebarBrush")
{
auto nonClientWindow{ static_cast<NonClientIslandWindow*>(_window.get()) };
nonClientWindow->SetTitlebarBackground(_logic.TitlebarBrush());
}
}
void AppHost::_AppInitializedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*arg*/)
{

View File

@@ -59,7 +59,7 @@ private:
void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& arg);
void _WindowMouseWheeled(const til::point coord, const int32_t delta);
winrt::fire_and_forget _WindowActivated();
winrt::fire_and_forget _WindowActivated(bool activated);
void _WindowMoved();
void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender,
@@ -126,6 +126,11 @@ private:
void _HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _updateTheme();
void _PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
std::unique_ptr<NotificationIcon> _notificationIcon;
@@ -172,5 +177,6 @@ private:
winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested;
winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested;
winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested;
winrt::TerminalApp::AppLogic::PropertyChanged_revoker PropertyChanged;
} _revokers{};
};

View File

@@ -433,10 +433,8 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
case WM_ACTIVATE:
{
// wparam = 0 indicates the window was deactivated
if (LOWORD(wparam) != 0)
{
_WindowActivatedHandlers();
}
const bool activated = LOWORD(wparam) != 0;
_WindowActivatedHandlers(activated);
break;
}

View File

@@ -67,7 +67,7 @@ public:
WINRT_CALLBACK(DragRegionClicked, winrt::delegate<>);
WINRT_CALLBACK(WindowCloseButtonClicked, winrt::delegate<>);
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
WINRT_CALLBACK(WindowActivated, winrt::delegate<void()>);
WINRT_CALLBACK(WindowActivated, winrt::delegate<void(bool)>);
WINRT_CALLBACK(HotkeyPressed, winrt::delegate<void(long)>);
WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate<void()>);
WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate<void()>);

View File

@@ -1130,3 +1130,8 @@ bool NonClientIslandWindow::_IsTitlebarVisible() const
{
return !(_fullscreen || _borderless);
}
void NonClientIslandWindow::SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush)
{
_titlebar.Background(brush);
}

View File

@@ -47,6 +47,8 @@ public:
void SetTitlebarContent(winrt::Windows::UI::Xaml::UIElement content);
void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) override;
void SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush);
private:
std::optional<COORD> _oldIslandPos;

View File

@@ -64,7 +64,9 @@ Abstract:
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.ui.xaml.data.h>
#include <winrt/Windows.ui.xaml.media.h>
#include <winrt/Windows.ui.xaml.data.h>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include <winrt/Windows.UI.Composition.h>

View File

@@ -87,31 +87,43 @@ std::string Utils::ColorToHexString(const til::color color)
// the correct format, throws E_INVALIDARG
til::color Utils::ColorFromHexString(const std::string_view str)
{
THROW_HR_IF(E_INVALIDARG, str.size() != 7 && str.size() != 4);
THROW_HR_IF(E_INVALIDARG, str.size() != 9 && str.size() != 7 && str.size() != 4);
THROW_HR_IF(E_INVALIDARG, str.at(0) != '#');
std::string rStr;
std::string gStr;
std::string bStr;
std::string aStr;
if (str.size() == 4)
{
rStr = std::string(2, str.at(1));
gStr = std::string(2, str.at(2));
bStr = std::string(2, str.at(3));
aStr = "ff";
}
else
else if (str.size() == 7)
{
rStr = std::string(&str.at(1), 2);
gStr = std::string(&str.at(3), 2);
bStr = std::string(&str.at(5), 2);
aStr = "ff";
}
else if (str.size() == 9)
{
// #aarrggbb
aStr = std::string(&str.at(1), 2);
rStr = std::string(&str.at(3), 2);
gStr = std::string(&str.at(5), 2);
bStr = std::string(&str.at(7), 2);
}
const auto r = gsl::narrow_cast<BYTE>(std::stoul(rStr, nullptr, 16));
const auto g = gsl::narrow_cast<BYTE>(std::stoul(gStr, nullptr, 16));
const auto b = gsl::narrow_cast<BYTE>(std::stoul(bStr, nullptr, 16));
const auto a = gsl::narrow_cast<BYTE>(std::stoul(aStr, nullptr, 16));
return til::color{ r, g, b };
return til::color{ r, g, b, a };
}
// Routine Description: