mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-10 14:26:47 +01:00
Compare commits
115 Commits
dev/crloew
...
v0.83.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8ad4fa804 | ||
|
|
5d77874382 | ||
|
|
5b1e5107ee | ||
|
|
1be3b6c087 | ||
|
|
63625a1cee | ||
|
|
4fee37c35a | ||
|
|
d40367a860 | ||
|
|
84def18ed5 | ||
|
|
ac14ad3458 | ||
|
|
0a9e889b1b | ||
|
|
7479ef6e65 | ||
|
|
3652e3627a | ||
|
|
07c4972c2c | ||
|
|
9e3ac70897 | ||
|
|
1b27500231 | ||
|
|
16a1fb7981 | ||
|
|
ca97e01d59 | ||
|
|
70d3d5f16e | ||
|
|
af6916a538 | ||
|
|
0c00106d5a | ||
|
|
96642b6525 | ||
|
|
9bea986f3d | ||
|
|
6fdc86ed2d | ||
|
|
78d53ffb10 | ||
|
|
7d8af7bbbb | ||
|
|
7808033436 | ||
|
|
98cfeb0776 | ||
|
|
a3e193e56e | ||
|
|
d668a659b5 | ||
|
|
fb8765b54d | ||
|
|
b9f6ef6ee4 | ||
|
|
7457ff5202 | ||
|
|
fb36e6ced9 | ||
|
|
f1ca65ca78 | ||
|
|
b7c8bb201b | ||
|
|
c1e8b70a64 | ||
|
|
0ddff0fcf7 | ||
|
|
c87d8c37e1 | ||
|
|
c24000ec41 | ||
|
|
2ca70e31c9 | ||
|
|
d27ac581ac | ||
|
|
5159c76976 | ||
|
|
8d2ee4a8d2 | ||
|
|
5bcc6fbd86 | ||
|
|
c8cf9af3c7 | ||
|
|
d64642f29c | ||
|
|
57b06fa431 | ||
|
|
7b37eba0f9 | ||
|
|
24bed6f0bb | ||
|
|
74fbc519ab | ||
|
|
3b7adbe6ac | ||
|
|
8b8c75b9a5 | ||
|
|
5c257fb3db | ||
|
|
6e141f89c9 | ||
|
|
62c7b0a66d | ||
|
|
9509d7c1cc | ||
|
|
6043898ee5 | ||
|
|
136b239f96 | ||
|
|
c148b51698 | ||
|
|
1ae8327a43 | ||
|
|
aa977f7579 | ||
|
|
dcbff83d8c | ||
|
|
5c631bd2c7 | ||
|
|
c252d87573 | ||
|
|
92483aee1a | ||
|
|
2eec4c5a98 | ||
|
|
109f0b210a | ||
|
|
f019163083 | ||
|
|
deb6234d72 | ||
|
|
cdf5677eb9 | ||
|
|
bc0811e6a1 | ||
|
|
fc32fe29ba | ||
|
|
2e85a14e72 | ||
|
|
1016154851 | ||
|
|
d7c021bf84 | ||
|
|
651f2e4bd8 | ||
|
|
3e07b9b8f4 | ||
|
|
d3aa5028aa | ||
|
|
638f91f75d | ||
|
|
e3f5fba870 | ||
|
|
126a03e3b0 | ||
|
|
bf42abc328 | ||
|
|
1d27c5b422 | ||
|
|
36d2f81e89 | ||
|
|
ed249bc0e1 | ||
|
|
fb7a85ec81 | ||
|
|
a80896d51b | ||
|
|
4badf2d24a | ||
|
|
d3d333319b | ||
|
|
b6f0c69e13 | ||
|
|
37059d4db0 | ||
|
|
eaf38ddcf6 | ||
|
|
114e53ecd6 | ||
|
|
a54dc0e52a | ||
|
|
949df59c9e | ||
|
|
e993ea3c64 | ||
|
|
4558d2e01b | ||
|
|
c2b6dceb18 | ||
|
|
c00f37c8d7 | ||
|
|
e2f1ad6d40 | ||
|
|
be89da899c | ||
|
|
1fb632d4a6 | ||
|
|
f859eac18a | ||
|
|
420e097e24 | ||
|
|
92e8b06068 | ||
|
|
4c9e18116c | ||
|
|
29ce15bb8a | ||
|
|
d0d2f3cd9c | ||
|
|
8bb5a33572 | ||
|
|
13b57e32f4 | ||
|
|
59f9785296 | ||
|
|
8fce41da06 | ||
|
|
f3540174cd | ||
|
|
dc91407625 | ||
|
|
16e68aff41 |
@@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"xamlstyler.console": {
|
||||
"version": "3.2206.4",
|
||||
"version": "3.2404.2",
|
||||
"commands": [
|
||||
"xstyler"
|
||||
]
|
||||
|
||||
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
@@ -4,7 +4,7 @@
|
||||
/.github/actions/spell-check/
|
||||
|
||||
# locking down pipeline folder
|
||||
/.pipelines @crutkas @DHowett @ethanfangg
|
||||
/.pipelines/ @crutkas @DHowett @ethanfangg
|
||||
|
||||
# locking down nuget config
|
||||
nuget.config @crutkas @DHowett @ethanfangg
|
||||
@@ -12,5 +12,5 @@ packages.config @crutkas @DHowett @ethanfangg
|
||||
|
||||
# locking down files that should not change
|
||||
LICENSE @crutkas @DHowett @ethanfangg
|
||||
SECURITY.MD @crutkas @DHowett @ethanfangg
|
||||
CODE_OF_CONDUCT.MD @crutkas @DHowett @ethanfangg
|
||||
SECURITY.md @crutkas @DHowett @ethanfangg
|
||||
CODE_OF_CONDUCT.md @crutkas @DHowett @ethanfangg
|
||||
|
||||
92
.github/actions/spell-check/allow/code.txt
vendored
92
.github/actions/spell-check/allow/code.txt
vendored
@@ -39,6 +39,7 @@ nupkg
|
||||
petabyte
|
||||
resw
|
||||
resx
|
||||
srt
|
||||
Stereolithography
|
||||
terabyte
|
||||
UYVY
|
||||
@@ -127,6 +128,92 @@ XBUTTONDOWN
|
||||
XBUTTONUP
|
||||
XDOWN
|
||||
|
||||
# User32.SYSTEM_METRICS_INDEX.cs
|
||||
|
||||
CLEANBOOT
|
||||
CMOUSEBUTTONS
|
||||
CONVERTIBLESLATEMODE
|
||||
CXBORDER
|
||||
CXCURSOR
|
||||
CXDLGFRAME
|
||||
CXDLGFRAME
|
||||
CXDOUBLECLK
|
||||
CXDRAG
|
||||
CXEDGE
|
||||
CXFIXEDFRAME
|
||||
CXFOCUSBORDER
|
||||
CXFRAME
|
||||
CXFRAME
|
||||
CXFULLSCREEN
|
||||
CXHSCROLL
|
||||
CXHTHUMB
|
||||
CXICON
|
||||
CXICONSPACING
|
||||
CXMAXIMIZED
|
||||
CXMAXTRACK
|
||||
CXMENUCHECK
|
||||
CXMENUSIZE
|
||||
CXMIN
|
||||
CXMINIMIZED
|
||||
CXMINSPACING
|
||||
CXMINTRACK
|
||||
CXPADDEDBORDER
|
||||
CXSIZE
|
||||
CXSIZEFRAME
|
||||
CXSMSIZE
|
||||
CXVSCROLL
|
||||
CYBORDER
|
||||
CYCAPTION
|
||||
CYCURSOR
|
||||
CYDLGFRAME
|
||||
CYDLGFRAME
|
||||
CYDOUBLECLK
|
||||
CYDRAG
|
||||
CYEDGE
|
||||
CYFIXEDFRAME
|
||||
CYFOCUSBORDER
|
||||
CYFRAME
|
||||
CYFRAME
|
||||
CYFULLSCREEN
|
||||
CYHSCROLL
|
||||
CYICON
|
||||
CYICONSPACING
|
||||
CYKANJIWINDOW
|
||||
CYMAXIMIZED
|
||||
CYMAXTRACK
|
||||
CYMENU
|
||||
CYMENUCHECK
|
||||
CYMENUSIZE
|
||||
CYMIN
|
||||
CYMINIMIZED
|
||||
CYMINSPACING
|
||||
CYMINTRACK
|
||||
CYSIZE
|
||||
CYSIZEFRAME
|
||||
CYSMCAPTION
|
||||
CYSMSIZE
|
||||
CYVSCROLL
|
||||
CYVTHUMB
|
||||
DBCSENABLED
|
||||
IMMENABLED
|
||||
MAXIMUMTOUCHES
|
||||
MEDIACENTER
|
||||
MENUDROPALIGNMENT
|
||||
MIDEASTENABLED
|
||||
MOUSEHORIZONTALWHEELPRESENT
|
||||
MOUSEPRESENT
|
||||
MOUSEWHEELPRESENT
|
||||
PENWINDOWS
|
||||
REMOTECONTROL
|
||||
REMOTESESSION
|
||||
SAMEDISPLAYFORMA
|
||||
SERVERR
|
||||
SHOWSOUNDS
|
||||
SHUTTINGDOWN
|
||||
SLOWMACHINE
|
||||
SWAPBUTTON
|
||||
SYSTEMDOCKED
|
||||
TABLETPC
|
||||
|
||||
# MATH
|
||||
|
||||
@@ -134,3 +221,8 @@ artanh
|
||||
arsinh
|
||||
arcosh
|
||||
|
||||
# Linux
|
||||
|
||||
dbus
|
||||
anypass
|
||||
gpg
|
||||
|
||||
23
.github/actions/spell-check/allow/names.txt
vendored
23
.github/actions/spell-check/allow/names.txt
vendored
@@ -28,15 +28,18 @@ videoconference
|
||||
|
||||
# USERS
|
||||
|
||||
8LWXpg
|
||||
Adoumie
|
||||
Advaith
|
||||
alekhyareddy
|
||||
Aleks
|
||||
angularsen
|
||||
Anirudha
|
||||
arjunbalgovind
|
||||
Ashish
|
||||
Baltazar
|
||||
Bao
|
||||
Bartosz
|
||||
betadele
|
||||
betsegaw
|
||||
bricelam
|
||||
@@ -51,6 +54,8 @@ crutkas
|
||||
damienleroy
|
||||
davidegiacometti
|
||||
debian
|
||||
Deibisu
|
||||
Deibisu
|
||||
Delimarsky
|
||||
Deondre
|
||||
DHowett
|
||||
@@ -62,6 +67,7 @@ gabime
|
||||
Galaxi
|
||||
Garside
|
||||
Gershaft
|
||||
Giordani
|
||||
Gokce
|
||||
Guo
|
||||
hanselman
|
||||
@@ -70,12 +76,15 @@ Heiko
|
||||
Hemmerlein
|
||||
hlaueriksson
|
||||
Horvalds
|
||||
Howett
|
||||
htcfreek
|
||||
Huynh
|
||||
Jaswal
|
||||
jefflord
|
||||
Jordi
|
||||
jyuwono
|
||||
Kairu
|
||||
Kairu
|
||||
Kamra
|
||||
Kantarci
|
||||
Karthick
|
||||
@@ -92,7 +101,9 @@ martinmoene
|
||||
Melman
|
||||
Mikhayelyan
|
||||
msft
|
||||
Mykhailo
|
||||
Myrvold
|
||||
Naro
|
||||
nathancartlidge
|
||||
Nemeth
|
||||
nielslaute
|
||||
@@ -103,9 +114,13 @@ peteblois
|
||||
phoboslab
|
||||
Ponten
|
||||
Pooja
|
||||
Pylyp
|
||||
quachpas
|
||||
Quriz
|
||||
randyrants
|
||||
ricardosantos
|
||||
riri
|
||||
riri
|
||||
ritchielawrence
|
||||
robmikh
|
||||
Rutkas
|
||||
@@ -119,22 +134,23 @@ Seraphima
|
||||
skttl
|
||||
somil
|
||||
Soref
|
||||
Sosnowski
|
||||
stefan
|
||||
Szablewski
|
||||
Tadele
|
||||
talynone
|
||||
Taras
|
||||
TBM
|
||||
tilovell
|
||||
Triet
|
||||
waaverecords
|
||||
Xpg
|
||||
ycv
|
||||
Yuniardi
|
||||
yuyoyuppe
|
||||
Zeol
|
||||
Zoltan
|
||||
Zykova
|
||||
Kairu
|
||||
Deibisu
|
||||
riri
|
||||
|
||||
# OTHERS
|
||||
|
||||
@@ -169,4 +185,3 @@ xamlstyler
|
||||
Xavalon
|
||||
Xbox
|
||||
Youdao
|
||||
|
||||
|
||||
46
.github/actions/spell-check/expect.txt
vendored
46
.github/actions/spell-check/expect.txt
vendored
@@ -95,6 +95,7 @@ AUTOUPDATE
|
||||
AValid
|
||||
awakeness
|
||||
AWAYMODE
|
||||
azcliversion
|
||||
azman
|
||||
backtracer
|
||||
bbwe
|
||||
@@ -119,11 +120,13 @@ BLURREGION
|
||||
bmi
|
||||
bms
|
||||
BNumber
|
||||
BODGY
|
||||
BOKMAL
|
||||
bootstrapper
|
||||
BOOTSTRAPPERINSTALLFOLDER
|
||||
bostrot
|
||||
BOTTOMALIGN
|
||||
boxmodel
|
||||
BPBF
|
||||
bpmf
|
||||
bpp
|
||||
@@ -149,6 +152,7 @@ Cangjie
|
||||
CANRENAME
|
||||
CAPTUREBLT
|
||||
CAPTURECHANGED
|
||||
CARETBLINKING
|
||||
CAtl
|
||||
cch
|
||||
CCHDEVICENAME
|
||||
@@ -163,6 +167,7 @@ CENTERALIGN
|
||||
ceq
|
||||
certlm
|
||||
certmgr
|
||||
cfp
|
||||
cguid
|
||||
CHANGECBCHAIN
|
||||
changecursor
|
||||
@@ -195,6 +200,7 @@ CMINVOKECOMMANDINFO
|
||||
CMINVOKECOMMANDINFOEX
|
||||
CMock
|
||||
CMONITORS
|
||||
cmph
|
||||
cmpgt
|
||||
cne
|
||||
CNF
|
||||
@@ -215,7 +221,6 @@ comdlg
|
||||
comexp
|
||||
cominterop
|
||||
commandline
|
||||
COMMANDTITLE
|
||||
commctrl
|
||||
commdlg
|
||||
compmgmt
|
||||
@@ -239,6 +244,7 @@ copiedcolorrepresentation
|
||||
cotaskmem
|
||||
COULDNOT
|
||||
countof
|
||||
cph
|
||||
CPower
|
||||
cppblog
|
||||
cppruntime
|
||||
@@ -250,6 +256,7 @@ CREATESCHEDULEDTASK
|
||||
CREATESTRUCT
|
||||
CREATEWINDOWFAILED
|
||||
CRECT
|
||||
CRH
|
||||
critsec
|
||||
Crossdevice
|
||||
CRSEL
|
||||
@@ -422,8 +429,8 @@ ENDSESSION
|
||||
ENTERSIZEMOVE
|
||||
ENU
|
||||
EOAC
|
||||
epu
|
||||
EPO
|
||||
epu
|
||||
ERASEBKGND
|
||||
EREOF
|
||||
EResize
|
||||
@@ -478,7 +485,6 @@ FILEFLAGSMASK
|
||||
FILELOCKSMITH
|
||||
FILELOCKSMITHCONTEXTMENU
|
||||
FILELOCKSMITHEXT
|
||||
FILELOCKSMITHLIB
|
||||
FILELOCKSMITHLIBINTEROP
|
||||
FILEMUSTEXIST
|
||||
FILEOP
|
||||
@@ -494,6 +500,7 @@ findfast
|
||||
FIXEDFILEINFO
|
||||
flac
|
||||
flyouts
|
||||
FMask
|
||||
FOF
|
||||
FOFX
|
||||
FOLDERID
|
||||
@@ -515,7 +522,6 @@ GCLP
|
||||
gdi
|
||||
gdiplus
|
||||
GDISCALED
|
||||
gdnbaselines
|
||||
GEmoji
|
||||
GETCLIENTAREAANIMATION
|
||||
GETDESKWALLPAPER
|
||||
@@ -535,7 +541,6 @@ gpedit
|
||||
gpo
|
||||
GPOCA
|
||||
gpp
|
||||
GPT
|
||||
gpu
|
||||
GSM
|
||||
gtm
|
||||
@@ -695,7 +700,6 @@ INSTALLSTARTMENUSHORTCUT
|
||||
INSTALLSTATE
|
||||
Inste
|
||||
Intelli
|
||||
interactable
|
||||
Interlop
|
||||
INTRESOURCE
|
||||
INVALIDARG
|
||||
@@ -724,7 +728,6 @@ IWeb
|
||||
IWIC
|
||||
iwr
|
||||
IYUV
|
||||
JArray
|
||||
jfi
|
||||
jfif
|
||||
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
|
||||
@@ -751,10 +754,11 @@ KEYEVENTF
|
||||
KEYIMAGE
|
||||
keynum
|
||||
keyremaps
|
||||
keyvault
|
||||
KILLFOCUS
|
||||
killrunner
|
||||
kmph
|
||||
Knownfolders
|
||||
ksh
|
||||
KSPROPERTY
|
||||
Kybd
|
||||
languagesjson
|
||||
@@ -790,7 +794,6 @@ LOADFROMFILE
|
||||
LOBYTE
|
||||
LOCALDISPLAY
|
||||
LOCALPACKAGE
|
||||
localport
|
||||
LOCALSYSTEM
|
||||
LOCATIONCHANGE
|
||||
LOGFONT
|
||||
@@ -804,6 +807,7 @@ LOWORD
|
||||
lparam
|
||||
LPBITMAPINFOHEADER
|
||||
LPCITEMIDLIST
|
||||
lpcmi
|
||||
LPCMINVOKECOMMANDINFO
|
||||
LPCREATESTRUCT
|
||||
LPCRECT
|
||||
@@ -828,6 +832,7 @@ lptpm
|
||||
LPTR
|
||||
LPTSTR
|
||||
LPW
|
||||
lpwcx
|
||||
lpwndpl
|
||||
LReader
|
||||
LRESULT
|
||||
@@ -845,10 +850,10 @@ lwin
|
||||
LZero
|
||||
majortype
|
||||
makecab
|
||||
MAKELANGID
|
||||
MAKEINTRESOURCE
|
||||
MAKEINTRESOURCEA
|
||||
MAKEINTRESOURCEW
|
||||
MAKELANGID
|
||||
makepri
|
||||
manifestdependency
|
||||
MAPPEDTOSAMEKEY
|
||||
@@ -868,7 +873,6 @@ mdwn
|
||||
MEDIASUBTYPE
|
||||
mediatype
|
||||
mef
|
||||
MENUBREAK
|
||||
MENUITEMINFO
|
||||
MENUITEMINFOW
|
||||
MERGECOPY
|
||||
@@ -1031,6 +1035,7 @@ NOSEARCH
|
||||
NOSENDCHANGING
|
||||
NOSIZE
|
||||
NOTIFICATIONSDLL
|
||||
NOTIFYICONDATA
|
||||
NOTIFYICONDATAW
|
||||
NOTIMPL
|
||||
notlike
|
||||
@@ -1067,7 +1072,6 @@ oldtheme
|
||||
oleaut
|
||||
OLECHAR
|
||||
onebranch
|
||||
OOBEPT
|
||||
opencode
|
||||
OPENFILENAME
|
||||
opensource
|
||||
@@ -1091,7 +1095,6 @@ OVERLAPPEDWINDOW
|
||||
overlaywindow
|
||||
Oversampling
|
||||
OWNDC
|
||||
OWNERDRAW
|
||||
Packagemanager
|
||||
PACL
|
||||
PAINTSTRUCT
|
||||
@@ -1105,7 +1108,6 @@ PARTIALCONFIRMATIONDIALOGTITLE
|
||||
PATCOPY
|
||||
pathcch
|
||||
PATHMUSTEXIST
|
||||
Pathto
|
||||
PATINVERT
|
||||
PATPAINT
|
||||
PAUDIO
|
||||
@@ -1154,6 +1156,7 @@ ploca
|
||||
plocm
|
||||
pluginsmodel
|
||||
PMSIHANDLE
|
||||
pnid
|
||||
Pnp
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
@@ -1246,8 +1249,6 @@ QUERYENDSESSION
|
||||
QUERYOPEN
|
||||
QUEUESYNC
|
||||
QUNS
|
||||
qwertyuiopasdfghjklzxcvbnm
|
||||
qwrtyuiopsghjklzxvnm
|
||||
raf
|
||||
RAII
|
||||
RAlt
|
||||
@@ -1264,7 +1265,6 @@ RECTDESTINATION
|
||||
rectp
|
||||
RECTSOURCE
|
||||
recyclebin
|
||||
redirectedfrom
|
||||
Redist
|
||||
redistributable
|
||||
reencode
|
||||
@@ -1335,6 +1335,9 @@ RRF
|
||||
rrr
|
||||
rsop
|
||||
Rsp
|
||||
rstringalnum
|
||||
rstringalpha
|
||||
rstringdigit
|
||||
Rstrtmgr
|
||||
RTB
|
||||
RTLREADING
|
||||
@@ -1343,12 +1346,11 @@ runas
|
||||
rundll
|
||||
rungameid
|
||||
RUNLEVEL
|
||||
runsettings
|
||||
runspace
|
||||
runtimeclass
|
||||
runtimeobject
|
||||
runtimepack
|
||||
runtimes
|
||||
ruuid
|
||||
rvm
|
||||
rwin
|
||||
rwl
|
||||
@@ -1364,6 +1366,7 @@ SCID
|
||||
Scip
|
||||
scipbe
|
||||
Scode
|
||||
screensaver
|
||||
screenshots
|
||||
scrollviewer
|
||||
sddl
|
||||
@@ -1576,7 +1579,6 @@ TDevice
|
||||
telem
|
||||
telephon
|
||||
templatenamespace
|
||||
testhost
|
||||
testprocess
|
||||
TEXCOORD
|
||||
TEXTEXTRACTOR
|
||||
@@ -1601,7 +1603,6 @@ tlb
|
||||
tlbimp
|
||||
TMPVAR
|
||||
TNP
|
||||
toggleswitch
|
||||
Toolhelp
|
||||
toolkitconverters
|
||||
Toolset
|
||||
@@ -1631,9 +1632,11 @@ TYPESHORTCUT
|
||||
UAC
|
||||
UAL
|
||||
uap
|
||||
UCallback
|
||||
udit
|
||||
uefi
|
||||
uesc
|
||||
UFlags
|
||||
UHash
|
||||
UIA
|
||||
UIEx
|
||||
@@ -1671,6 +1674,7 @@ USESHOWWINDOW
|
||||
USESTDHANDLES
|
||||
USRDLL
|
||||
UType
|
||||
uuidv
|
||||
uwp
|
||||
uxtheme
|
||||
vabdq
|
||||
|
||||
3
.github/actions/spell-check/patterns.txt
vendored
3
.github/actions/spell-check/patterns.txt
vendored
@@ -40,6 +40,9 @@
|
||||
# tabs in c#
|
||||
\$"\\t
|
||||
|
||||
# Hexadecimal character pattern in code
|
||||
\\x[0-9a-fA-F][0-9a-fA-F]
|
||||
|
||||
# windows line breaks in strings
|
||||
\\r\\n
|
||||
|
||||
|
||||
96
.github/workflows/msstore-submissions.yml
vendored
96
.github/workflows/msstore-submissions.yml
vendored
@@ -5,56 +5,80 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
|
||||
microsoft_store:
|
||||
name: Publish Microsoft Store
|
||||
environment: store
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: BODGY - Set up Gnome Keyring for future Cert Auth
|
||||
run: |-
|
||||
sudo apt-get install -y gnome-keyring
|
||||
export $(dbus-launch --sh-syntax)
|
||||
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --unlock)
|
||||
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)
|
||||
|
||||
- name: Log in to Azure
|
||||
uses: azure/login@v2
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
enable-AzPSSession: true
|
||||
|
||||
- name: Get latest URL from public releases
|
||||
id: releaseVars
|
||||
run: |
|
||||
release=$(curl https://api.github.com/repos/Microsoft/PowerToys/releases | jq '[.[]|select(.name | contains("Release"))][0]')
|
||||
assets=$(jq -n "$release" | jq '.assets')
|
||||
powerToysSetup=$(jq -n "$assets" | jq '[.[]|select(.name | contains("PowerToysSetup"))]')
|
||||
echo ::set-output name=powerToysInstallerX64Url::$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("x64"))][0].browser_download_url')
|
||||
echo ::set-output name=powerToysInstallerArm64Url::$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url')
|
||||
powerToysSetup=$(jq -n "$assets" | jq '[.[]|select(.name | contains("PowerToysUserSetup"))]')
|
||||
echo powerToysInstallerX64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("x64"))][0].browser_download_url') >> $GITHUB_OUTPUT
|
||||
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: microsoft/setup-msstore-cli@v1
|
||||
|
||||
- name: Fetch Store Credential
|
||||
uses: azure/cli@v2
|
||||
with:
|
||||
azcliversion: latest
|
||||
inlineScript: |-
|
||||
az keyvault secret download --vault-name ${{ secrets.AZURE_KEYVAULT_NAME }} -n ${{ secrets.AZURE_AUTH_CERT_NAME }} -f cert.pfx.b64
|
||||
base64 -d < cert.pfx.b64 > cert.pfx
|
||||
|
||||
- name: Configure Store Credentials
|
||||
uses: microsoft/store-submission@v1
|
||||
with:
|
||||
command: configure
|
||||
type: win32
|
||||
seller-id: ${{ secrets.SELLER_ID }}
|
||||
product-id: ${{ secrets.PRODUCT_ID }}
|
||||
tenant-id: ${{ secrets.TENANT_ID }}
|
||||
client-id: ${{ secrets.CLIENT_ID }}
|
||||
client-secret: ${{ secrets.CLIENT_SECRET }}
|
||||
run: |-
|
||||
msstore reconfigure -cfp cert.pfx -c ${{ secrets.AZURE_CLIENT_ID }} -t ${{ secrets.AZURE_TENANT_ID }} -s ${{ secrets.SELLER_ID }}
|
||||
|
||||
- name: Update draft submission
|
||||
uses: microsoft/store-submission@v1
|
||||
with:
|
||||
command: update
|
||||
product-update: '{
|
||||
"packages":[
|
||||
{
|
||||
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerX64Url }}",
|
||||
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
|
||||
"architectures":["X64"],
|
||||
"installerParameters":"/quiet /norestart",
|
||||
"isSilentInstall":true
|
||||
},
|
||||
{
|
||||
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerArm64Url }}",
|
||||
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
|
||||
"architectures":["Arm64"],
|
||||
"installerParameters":"/quiet /norestart",
|
||||
"isSilentInstall":true
|
||||
}
|
||||
]
|
||||
}'
|
||||
run: |-
|
||||
msstore submission update ${{ secrets.PRODUCT_ID }} '{
|
||||
"packages":[
|
||||
{
|
||||
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerX64Url }}",
|
||||
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
|
||||
"architectures":["X64"],
|
||||
"installerParameters":"/quiet /norestart",
|
||||
"isSilentInstall":true
|
||||
},
|
||||
{
|
||||
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerArm64Url }}",
|
||||
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
|
||||
"architectures":["Arm64"],
|
||||
"installerParameters":"/quiet /norestart",
|
||||
"isSilentInstall":true
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
- name: Publish Submission
|
||||
uses: microsoft/store-submission@v1
|
||||
with:
|
||||
command: publish
|
||||
run: |-
|
||||
msstore submission publish ${{ secrets.PRODUCT_ID }}
|
||||
|
||||
- name: Clean up auth certificate
|
||||
if: always()
|
||||
run: |-
|
||||
rm -f cert.pfx cert.pfx.b64
|
||||
|
||||
2
.github/workflows/package-submissions.yml
vendored
2
.github/workflows/package-submissions.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
$installerMachineX64Url = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*x64' | Select -ExpandProperty browser_download_url
|
||||
$installerUserArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysUserSetup.*arm64' | Select -ExpandProperty browser_download_url
|
||||
$installerMachineArmUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'PowerToysSetup.*arm64' | Select -ExpandProperty browser_download_url
|
||||
$ver = $targetRelease.tag_name.Trim("v")
|
||||
$ver = $targetRelease.tag_name -ireplace '^v'
|
||||
|
||||
# getting latest wingetcreate file
|
||||
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
|
||||
33
.github/workflows/similarIssues.yml
vendored
33
.github/workflows/similarIssues.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: GitGudSimilarIssues comments
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
getSimilarIssues:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
message: ${{ steps.getBody.outputs.message }}
|
||||
steps:
|
||||
- id: getBody
|
||||
uses: craigloewen-msft/GitGudSimilarIssues@main
|
||||
with:
|
||||
issueTitle: ${{ github.event.issue.title }}
|
||||
issueBody: ${{ github.event.issue.body }}
|
||||
repo: ${{ github.repository }}
|
||||
similarityTolerance: "0.75"
|
||||
add-comment:
|
||||
needs: getSimilarIssues
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: needs.getSimilarIssues.outputs.message != ''
|
||||
steps:
|
||||
- name: Add comment
|
||||
run: gh issue comment "$NUMBER" --repo "$REPO" --body "$BODY"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
BODY: ${{ needs.getSimilarIssues.outputs.message }}
|
||||
@@ -41,6 +41,6 @@ jobs:
|
||||
platform: arm64
|
||||
${{ if eq(variables['System.PullRequest.IsFork'], 'False') }}:
|
||||
enableCaching: true
|
||||
- template: ./templates/run-ui-tests-ci.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
# - template: ./templates/run-ui-tests-ci.yml
|
||||
# parameters:
|
||||
# platform: x64
|
||||
|
||||
@@ -24,10 +24,10 @@ jobs:
|
||||
NODE_OPTIONS: --max_old_space_size=16384
|
||||
pool:
|
||||
demands: ImageOverride -equals SHINE-VS17-Latest
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: SHINE-OSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
timeoutInMinutes: 120
|
||||
strategy:
|
||||
maxParallel: 10
|
||||
|
||||
@@ -3,10 +3,10 @@ jobs:
|
||||
- job: Precheck
|
||||
pool:
|
||||
demands: ImageOverride -equals SHINE-VS17-Latest
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: SHINE-OSS-L
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
steps:
|
||||
- checkout: none
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ steps:
|
||||
inputs:
|
||||
testResultsFormat: VSTest
|
||||
testResultsFiles: '**/*.trx'
|
||||
condition: always()
|
||||
condition: ne(variables['BuildPlatform'],'arm64')
|
||||
|
||||
# Native dlls
|
||||
- task: VSTest@2
|
||||
|
||||
@@ -9,10 +9,10 @@ jobs:
|
||||
variables:
|
||||
SrcPath: $(Build.Repository.LocalPath)
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
name: SHINE-OSS-Testing-x64
|
||||
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
|
||||
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
|
||||
name: SHINE-INT-Testing-x64
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-Testing-x64
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
searchFolder: '$(Pipeline.Workspace)\build-${{ parameters.platform }}-${{ parameters.configuration }}'
|
||||
vstestLocationMethod: 'location' # otherwise fails to find vstest.console.exe
|
||||
#vstestLocation: '$(Agent.ToolsDirectory)\VsTest\**\${{ parameters.platform }}\tools\net462\Common7\IDE\Extensions\TestPlatform'
|
||||
vstestLocation: '$(Agent.ToolsDirectory)\VsTest\17.10.0-release-24177-07\x64\tools\net462\Common7\IDE\Extensions\TestPlatform'
|
||||
vstestLocation: '$(Agent.ToolsDirectory)\VsTest\17.10.0\x64\tools\net462\Common7\IDE\Extensions\TestPlatform'
|
||||
uiTests: true
|
||||
rerunFailedTests: true
|
||||
testAssemblyVer2: |
|
||||
|
||||
@@ -43,7 +43,7 @@ steps:
|
||||
inputs:
|
||||
solution: "**/installer/PowerToysSetup.sln"
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: /p:CIBuild=true /target:PowerToysInstaller /bl:$(Build.SourcesDirectory)\msbuild.binlog /p:RunBuildEvents=false /p:PerUser=${{parameters.perUserArg}}
|
||||
msbuildArgs: /p:CIBuild=true /p:BuildProjectReferences=false /target:PowerToysInstaller /bl:$(Build.SourcesDirectory)\msbuild.binlog /p:RunBuildEvents=false /p:PerUser=${{parameters.perUserArg}}
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: false # don't undo our hard work above by deleting the CustomActions dll
|
||||
|
||||
@@ -76,7 +76,7 @@ extends:
|
||||
NODE_OPTIONS: --max_old_space_size=16384
|
||||
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations
|
||||
SkipCppCodeAnalysis: 1 # Skip the code analysis to speed up release CI. It runs on PR CI, anyway
|
||||
IsExperimentationLive: 1 # The build and installer use this to turn on experimentation
|
||||
# IsExperimentationLive: 1 # The build and installer use this to turn on experimentation
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
|
||||
18
COMMUNITY.md
18
COMMUNITY.md
@@ -27,6 +27,9 @@ Heiko has helped triaging, discussing, and creating a substantial number of issu
|
||||
### [@Jay-o-Way](https://github.com/Jay-o-Way) - Jay
|
||||
Jay has helped triaging, discussing, creating a substantial number of issues and PRs.
|
||||
|
||||
### [@jefflord](https://github.com/Jjefflord) - Jeff Lord
|
||||
Jeff added in multiple new features into Keyboard manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com)
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
@@ -34,14 +37,12 @@ Joe has helped triaging, discussing, issues as well as fixing bugs and building
|
||||
Helping keep our spelling correct :)
|
||||
|
||||
### [@martinchrzan](https://github.com/martinchrzan/) - Martin Chrzan
|
||||
|
||||
Color Picker is from Martin.
|
||||
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
|
||||
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
|
||||
### [@royvou](https://github.com/royvou)
|
||||
@@ -153,14 +154,25 @@ Other contributors:
|
||||
## PowerToys core team
|
||||
|
||||
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
|
||||
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Lead
|
||||
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Product Manager
|
||||
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
|
||||
- [@plante-msft](https://github.com/plante-msft) - Connor Plante - Product Manager
|
||||
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
|
||||
- [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie - Product Manager
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
|
||||
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
|
||||
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
|
||||
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
|
||||
# Former PowerToys core team members
|
||||
|
||||
- [@indierawk2k2](https://github.com/indierawk2k2) - Mike Harsh - Product Manager
|
||||
- [@enricogior](https://github.com/enricogior) - Enrico Giordani - Dev Lead
|
||||
- [@bzoz](https://github.com/bzoz) - Bartosz Sosnowski - Dev
|
||||
- [@ivan100sic](https://github.com/ivan100sic) - Ivan Stošić - Dev
|
||||
- [@mykhailopylyp](https://github.com/mykhailopylyp) - Mykhailo Pylyp - Dev
|
||||
- [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev
|
||||
- [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
<PreferredToolArchitecture Condition="'$(PROCESSOR_ARCHITECTURE)' == 'ARM64' or '$(PROCESSOR_ARCHITEW6432)' == 'ARM64'">arm64</PreferredToolArchitecture>
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
<ReplaceWildcardsInProjectItems>true</ReplaceWildcardsInProjectItems>
|
||||
<ExternalIncludePath>$(MSBuildThisFileFullPath)\..\deps\;$(MSBuildThisFileFullPath)\..\packages\;$(ExternalIncludePath)</ExternalIncludePath>
|
||||
<!-- Enable control flow guard for C++ projects that don't consume any C++ files -->
|
||||
<!-- This covers the case where a .dll exports a .lib, but doesn't have any ClCompile entries. -->
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<UsePrecompiledHeaders Condition="'$(TF_BUILD)' != ''">false</UsePrecompiledHeaders>
|
||||
|
||||
<!-- Change this to bust the cache -->
|
||||
<MSBuildCacheCacheUniverse Condition="'$(MSBuildCacheCacheUniverse)' == ''">202310210737</MSBuildCacheCacheUniverse>
|
||||
<MSBuildCacheCacheUniverse Condition="'$(MSBuildCacheCacheUniverse)' == ''">202407200737</MSBuildCacheCacheUniverse>
|
||||
|
||||
<!--
|
||||
Visual Studio telemetry reads various ApplicationInsights.config files and other files after the project is finished, likely in a detached process.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.Markdown" Version="7.1.2" />
|
||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||
<PackageVersion Include="coverlet.collector" Version="1.3.0" />
|
||||
<PackageVersion Include="DotNetSeleniumExtras.WaitHelpers" Version="3.11.0" />
|
||||
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
||||
<PackageVersion Include="HelixToolkit.Core.Wpf" Version="2.24.0" />
|
||||
@@ -28,32 +27,30 @@
|
||||
<PackageVersion Include="Mages" Version="2.0.2" />
|
||||
<PackageVersion Include="Markdig.Signed" Version="0.34.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.7" />
|
||||
<PackageVersion Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.336902" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2365.46" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="8.0.7" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
|
||||
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.0.4" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.Contracts" Version="10.0.19041.1" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.5.240311000" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.5.240428000" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||
<!-- Moq to stay below v4.20 due to behavior change. need to be sure fixed -->
|
||||
<PackageVersion Include="Moq" Version="4.18.4" />
|
||||
<PackageVersion Include="MSTest.TestAdapter" Version="3.2.0" />
|
||||
<PackageVersion Include="MSTest.TestFramework" Version="3.2.0" />
|
||||
<PackageVersion Include="MSTest" Version="3.5.0" />
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageVersion Include="NLog" Version="5.0.4" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
|
||||
@@ -75,18 +72,19 @@
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="8.0.0" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.5" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.6" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="17.2.3" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="17.2.3" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.0-preview.9" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
<!-- Package System.Security.Cryptography.ProtectedData added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
<PackageVersion Include="UnitsNet" Version="4.145.0" />
|
||||
<PackageVersion Include="UnitsNet" Version="5.50.0" />
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
|
||||
<PackageVersion Include="Vanara.PInvoke.User32" Version="3.4.11" />
|
||||
<PackageVersion Include="Vanara.PInvoke.Shell32" Version="3.4.11" />
|
||||
|
||||
17
NOTICE.md
17
NOTICE.md
@@ -1318,28 +1318,26 @@ EXHIBIT A -Mozilla Public License.
|
||||
- Mages 2.0.2
|
||||
- Markdig.Signed 0.34.0
|
||||
- Microsoft.CodeAnalysis.NetAnalyzers 8.0.0
|
||||
- Microsoft.Data.Sqlite 8.0.0
|
||||
- Microsoft.Data.Sqlite 8.0.7
|
||||
- Microsoft.Extensions.DependencyInjection 8.0.0
|
||||
- Microsoft.Extensions.Hosting 8.0.0
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 8.0.0
|
||||
- Microsoft.Extensions.Logging 8.0.0
|
||||
- Microsoft.Extensions.Logging.Abstractions 8.0.0
|
||||
- Microsoft.NET.Test.Sdk 17.8.0
|
||||
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
|
||||
- Microsoft.Web.WebView2 1.0.2365.46
|
||||
- Microsoft.Win32.SystemEvents 8.0.0
|
||||
- Microsoft.Windows.Compatibility 8.0.1
|
||||
- Microsoft.Windows.Compatibility 8.0.7
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.0.4
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
- Microsoft.Windows.SDK.Contracts 10.0.19041.1
|
||||
- Microsoft.WindowsAppSDK 1.5.240311000
|
||||
- Microsoft.WindowsAppSDK 1.5.240428000
|
||||
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
|
||||
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
|
||||
- ModernWpfUI 0.9.4
|
||||
- Moq 4.18.4
|
||||
- MSTest.TestAdapter 3.2.0
|
||||
- MSTest.TestFramework 3.2.0
|
||||
- MSTest 3.5.0
|
||||
- NLog.Extensions.Logging 5.3.8
|
||||
- NLog.Schema 5.2.8
|
||||
- ReverseMarkdown 4.1.0
|
||||
@@ -1355,17 +1353,18 @@ EXHIBIT A -Mozilla Public License.
|
||||
- System.Data.SqlClient 4.8.6
|
||||
- System.Diagnostics.EventLog 8.0.0
|
||||
- System.Diagnostics.PerformanceCounter 8.0.0
|
||||
- System.Drawing.Common 8.0.5
|
||||
- System.Drawing.Common 8.0.6
|
||||
- System.IO.Abstractions 17.2.3
|
||||
- System.IO.Abstractions.TestingHelpers 17.2.3
|
||||
- System.Management 8.0.0
|
||||
- System.Reactive 6.0.0-preview.9
|
||||
- System.Reactive 6.0.1
|
||||
- System.Runtime.Caching 8.0.0
|
||||
- System.Security.Cryptography.ProtectedData 8.0.0
|
||||
- System.ServiceProcess.ServiceController 8.0.0
|
||||
- System.Text.Encoding.CodePages 8.0.0
|
||||
- System.Text.Json 8.0.4
|
||||
- UnicodeInformation 2.6.0
|
||||
- UnitsNet 4.145.0
|
||||
- UnitsNet 5.50.0
|
||||
- UTF.Unknown 2.5.1
|
||||
- Vanara.PInvoke.Shell32 3.4.11
|
||||
- Vanara.PInvoke.User32 3.4.11
|
||||
|
||||
@@ -146,11 +146,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerLauncher", "src\module
|
||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\tests\win-app-driver\README.md = src\tests\win-app-driver\README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "previewpane", "previewpane", "{2F305555-C296-497E-AC20-5FA1B237996A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PreviewHandlerCommon", "src\modules\previewpane\Common\PreviewHandlerCommon.csproj", "{AF2349B8-E5B6-4004-9502-687C1C7730B1}"
|
||||
@@ -176,6 +171,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\.editorconfig = src\.editorconfig
|
||||
.vsconfig = .vsconfig
|
||||
Cpp.Build.props = Cpp.Build.props
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
|
||||
204
README.md
204
README.md
@@ -8,8 +8,8 @@
|
||||
|
||||
| Architecture | Solution (Main) | Solution (Stable) | Installer (Main) |
|
||||
|--------------|-----------------|-------------------|------------------|
|
||||
| x64 | [](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=main&jobName=Build%20x64%20Release) | [](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=stable) | [](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76541&branchName=main) |
|
||||
| ARM64 | [](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=main) | [](https://dev.azure.com/ms/PowerToys/_build/latest?definitionId=219&branchName=stable) | [](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76541&branchName=main) |
|
||||
| x64 | [](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=main) | [](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=stable) | [](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76541&branchName=main) |
|
||||
| ARM64 | [](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=main) | [](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=main) | [](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76541&branchName=main) |
|
||||
|
||||
## About
|
||||
|
||||
@@ -17,14 +17,15 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
|
||||
| | Current utilities: | |
|
||||
|--------------|--------------------|--------------|
|
||||
| [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) |
|
||||
| [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) |
|
||||
| [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) |
|
||||
| [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) |
|
||||
| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [Peek](https://aka.ms/PowerToysOverview_Peek) |
|
||||
| [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) |
|
||||
| [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) |
|
||||
| [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) |
|
||||
| [Advanced Paste](https://aka.ms/PowerToysOverview_AdvancedPaste) | [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) |
|
||||
| [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) |
|
||||
| [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) |
|
||||
| [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) |
|
||||
| [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) |
|
||||
| [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) |
|
||||
| [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) |
|
||||
| [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) |
|
||||
| [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) |
|
||||
|
||||
## Installing and running Microsoft PowerToys
|
||||
|
||||
@@ -40,19 +41,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
|
||||
|
||||
<!-- items that need to be updated release to release -->
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F54
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F53
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.1/PowerToysUserSetup-0.80.1-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.1/PowerToysUserSetup-0.80.1-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.1/PowerToysSetup-0.80.1-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.80.1/PowerToysSetup-0.80.1-arm64.exe
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.83%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.82%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.1/PowerToysUserSetup-0.82.1-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.1/PowerToysUserSetup-0.82.1-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.1/PowerToysSetup-0.82.1-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.1/PowerToysSetup-0.82.1-arm64.exe
|
||||
|
||||
| Description | Filename | sha256 hash |
|
||||
|----------------|----------|-------------|
|
||||
| Per user - x64 | [PowerToysUserSetup-0.80.1-x64.exe][ptUserX64] | 23E35F7B33C6F24237BCA3D5E8EDF9B3BD4802DD656C402B40A4FC82670F8BE3 |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.80.1-arm64.exe][ptUserArm64] | C5EECF0D9D23AB8C14307F91CA28D2CF4DA5932D705F07AE93576C259F74B4D1 |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.80.1-x64.exe][ptMachineX64] | 62373A08BB8E1C1173D047509F3EA5DCC0BE1845787E07BCDA3F6A09DA2A0C17 |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.80.1-arm64.exe][ptMachineArm64] | 061EF8D1B10D68E69D04F98A2D8E1D8047436174C757770778ED23E01CC3B06C |
|
||||
| Per user - x64 | [PowerToysUserSetup-0.82.1-x64.exe][ptUserX64] | B594C9A32125079186DCE776431E2DC77B896774D2AEE2ACF51BAB8791683485 |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.82.1-arm64.exe][ptUserArm64] | 41C1D9C0E8FA7EFFCE6F605C92C143AE933F8C999A2933A4D9D1115B16F14F67 |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.82.1-x64.exe][ptMachineX64] | B8FA7E7C8F88B69E070E234F561D32807634E2E9D782EDBB9DC35F3A454F2264 |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.82.1-arm64.exe][ptMachineArm64] | 58F22306F22CF9878C6DDE6AC128388DF4DFF78B76165E38A695490E55B3C8C4 |
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
@@ -98,136 +99,137 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
|
||||
|
||||
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
|
||||
|
||||
### 0.80 - March 2024 Update
|
||||
### 0.82 - June 2024 Update
|
||||
|
||||
In this release, we focused on stability and improvements. The next release is planned to be released during [Microsoft Build 2024](https://build.microsoft.com/) (late May).
|
||||
In this release, we focused on stability and improvements.
|
||||
|
||||
**Highlights**
|
||||
|
||||
- New feature: Desired State Configuration support, allowing the use of winget configure for PowerToys. Check the [DSC documentation](https://aka.ms/powertoys-docs-dsc-configure) for more information.
|
||||
- The Windows App SDK dependency was updated to 1.5.1, fixing many underlying UI issues.
|
||||
- WebP/WebM files support was added to Peek. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Audio files support was added to Peek. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Automated UI testing for FancyZones Editor was added to CI.
|
||||
- New feature added to PowerRename to allow using sequences of random characters and UUIDs when renaming files. Thanks [@jhirvioja](https://github.com/jhirvioja)!
|
||||
- Improvements in the Paste As JSON feature to better handle other CSV delimiters and converting from ini files. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Fixed UI issues that were reported after upgrading to WPF UI on Color Picker and PowerToys Run.
|
||||
- Bug fixes and stability.
|
||||
|
||||
### General
|
||||
### Advanced Paste
|
||||
|
||||
- Added a Quick Access entry to access the flyout from PowerToys' tray icon right click menu. Thanks [@pekvasnovsky](https://github.com/pekvasnovsky)!
|
||||
- Added support for Desired State Configuration in PowerToys, allowing the use of winget configure to configure many settings.
|
||||
|
||||
### Awake
|
||||
|
||||
- Fix an issue causing the "Keep screen on" option to disable after Awake deactivated itself.
|
||||
- Fixed an issue causing external applications triggering Advanced Paste. (This was a hotfix for 0.81)
|
||||
- Added a GPO rule to disallow using online models in Advanced Paste. (This was a hotfix for 0.81)
|
||||
- Improved CSV delimiter handling and plain text parsing for the Paste as JSON feature. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Added support to convert from ini in the Paste as JSON feature. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Fixed a memory leak caused by images not being properly cleaned out from clipboard history.
|
||||
- Added an option to hide the UI when it loses focus. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Improved telemetry to get better data about token usage and if clipboard history is a popular feature. Thanks [@craigloewen-msft](https://github.com/craigloewen-msft)!
|
||||
|
||||
### Color Picker
|
||||
|
||||
- Fixed a UI issue causing the color picker modal to hide part of the color bar. Thanks [@TheChilledBuffalo](https://github.com/TheChilledBuffalo)!
|
||||
- Fixed the opaque background corners in the picker that were introduced after the upgrade to WPFUI.
|
||||
|
||||
### Command Not Found
|
||||
### Developer Files Preview (Monaco)
|
||||
|
||||
- Now tries to find a preview version of PowerShell if no stable version is found.
|
||||
- Improved the syntax highlight for .gitignore files. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Checking for the sticky scroll option in code behind was being done twice. Removed one of the checks. Thanks [@downarowiczd](https://github.com/downarowiczd)!
|
||||
|
||||
### FancyZones
|
||||
### Environment Variables Editor
|
||||
|
||||
- Fixed a crash loading the editor when there's a layout with an empty name in the configuration file.
|
||||
- Refactored layout internal data structures and common code to allow for automated testing.
|
||||
- The pressing of the shift key is now detected through raw input to fix an issue causing the shift key to be locked for some users.
|
||||
- Added clarity to the UI section tooltips. Thanks [@anson-poon](https://github.com/anson-poon)!
|
||||
|
||||
### File Explorer add-ons
|
||||
|
||||
- Fixed a crash occurring in the Monaco previewer when a file being previewed isn't found by the code behind.
|
||||
- Fixed an issue in the Markdown previewer adding a leading space to code blocks. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
|
||||
- Fixed wrong location and scaling of preview results on screens with different DPIs.
|
||||
- Added better clean up code to thumbnail handlers to prevent locking files.
|
||||
- Fixed a crash when the preview handlers received a 64-bit handle from the OS. Thanks [@z4pf1sh](https://github.com/z4pf1sh)!
|
||||
- Fixed a crash when trying to update window bounds and File Explorer already disposed the preview.
|
||||
|
||||
### File Locksmith
|
||||
### Find My Mouse
|
||||
|
||||
- Allow multiple lines to wrap when viewing the modal with selected file paths. Thanks [@sanidhyas3s](https://github.com/sanidhyas3s)!
|
||||
- Added the option to have to use the Windows + Control keys to activate. Thanks [@Gentoli](https://github.com/Gentoli)!
|
||||
|
||||
### Hosts File Editor
|
||||
|
||||
- Improved spacing definitions in the UI so that hosts name are not hidden when resizing and icons are well aligned. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Changed the additional lines dialog to show the horizontal scrollbar instead of wrapping contents. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Improved the duplication check's logic to improve performance and take into account features that were introduced after it. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
|
||||
### Installer
|
||||
|
||||
- Fixed the final directory name of the PowerToys Run VSCode Workspaces plugin in the installation directory to match the plugin name. Thanks [@zetaloop](https://github.com/zetaloop)!
|
||||
- Used more generic names for the bootstrap steps, so that "Installing PowerToys" is not shown when uninstalling.
|
||||
|
||||
### Keyboard Manager
|
||||
|
||||
- Fixed an issue that would clear out KBM mappings when certain numpad keys were used as the second key of a chord.
|
||||
- Added a comment in localization files so that translators won't translate "Text" as "SMS".
|
||||
- Fixed the remaining install failures when the folders the DSC module is to be installed in isn't accessible by the WiX installer for user scope installations.
|
||||
- Fixed an issue causing ARM64 uninstall process to not correctly find powershell 7 to run uninstall scripts.
|
||||
|
||||
### Peek
|
||||
|
||||
- Added support to .WebP/.WebM files in the image/video previewer. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Added support for audio files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed an issue causing the open file button in the title bar to be un-clickable. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed an issue when previewing a folder with a dot in the name that caused Peek to try to preview it as a file. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Prevent activating Peek when the user is renaming a file. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Added support to preview special folders like Recycle Bin and My PC instead of throwing an error.
|
||||
- Fixed a crash caused by double releasing a COM object from the module interface.
|
||||
|
||||
### Power Rename
|
||||
|
||||
- Improved apostrophe character handling for the Capitalize and Titlecase renaming flags. Thanks [@anthonymonforte](https://github.com/anthonymonforte)!
|
||||
- Added a feature to allow using sequences of random characters or UUIDs when renaming files. Thanks [@jhirvioja](https://github.com/jhirvioja)!
|
||||
|
||||
### PowerToys Run
|
||||
|
||||
- Added a setting to the Windows Search plugin to exclude files and patterns from the results. Thanks [@HydroH](https://github.com/HydroH)!
|
||||
- Fixed an issue showing thumbnails caused by a hash collision between similar images.
|
||||
- Added the "checkbox and multiline text box" additional property type for plugins and improved multiline text handling. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Improved the plugin descriptions for consistency in the UI. Thanks [@HydroH](https://github.com/HydroH)!
|
||||
- Fixed UI scaling for different dpi scenarios.
|
||||
- Fixed crash on a racing condition when updating UWP icon paths in the Program plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Fixed PowerToys Run hanging when trying to close an unresponsive window in the WindowWalker plugin. Thanks [@GhostVaibhav](https://github.com/GhostVaibhav)!
|
||||
- Fixed the example in the UnitConverter description to reduce confusion with the inches abbreviation (now uses "to" instead of "in"). Thanks [@acekirkpatrick](https://github.com/acekirkpatrick)!
|
||||
- Brought the acrylic background back and applied a proper fix to the titlebar accent showing through transparency.
|
||||
- Fixed an issue causing the transparency from the UI disappearing after some time.
|
||||
|
||||
### Quick Accent
|
||||
|
||||
- Added the Schwa character to the Italian character set. Thanks [@damantioworks](https://github.com/damantioworks)!
|
||||
|
||||
### Registry Preview
|
||||
|
||||
- Allow alternative valid names for the root keys. Thanks [@e-t-l](https://github.com/e-t-l)!
|
||||
- Fixed an issue causing many pick file windows to be opened simultaneously. Thanks [@randyrants](https://github.com/randyrants)!
|
||||
- Added support for the Crimean Tatar character set. Thanks [@cor-bee](https://github.com/cor-bee)!
|
||||
- Added the Numero symbol and double acute accent character. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Added the International Phonetic Alphabet characters. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Fixed the character description center positioning. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Added feminine and masculine ordinal indicator characters to the Portuguese character set. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
|
||||
|
||||
### Screen Ruler
|
||||
|
||||
- Updated the measure icons for clarity. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker) and [@niels9001](https://github.com/niels9001)!
|
||||
|
||||
### Shortcut Guide
|
||||
|
||||
- Updated the Emoji shortcut that is shown to the new Windows key + period (.) hotkey.
|
||||
|
||||
### Text Extractor
|
||||
|
||||
- Fixed issues creating the extract layout on certain monitor configurations.
|
||||
|
||||
### Video Conference Mute
|
||||
|
||||
- Added enable/disable telemetry to get usage data.
|
||||
- Updated the default activation hotkey to Win+Control+Shift+M, in order to not conflict with the Windows shortcut that restores minimized windows (Win+Shift+M). Thanks [@nx-frost](https://github.com/nx-frost)!
|
||||
|
||||
### Settings
|
||||
|
||||
- Added locks to some terms (like the name of some utilities) so that they aren't localized.
|
||||
- Fixed some shortcuts not being shown properly in the Flyout and Dashboard. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Updated image for Color Picker and outdated animations for utilities in OOBE. Thanks [@niels9001](https://github.com/niels9001)!
|
||||
- Disabled the UI to enable/disable clipboard history in the Advanced Paste settings page when clipboard history is disabled by GPO in the system. (This was a hotfix for 0.81)
|
||||
- Updated Advanced Paste's Settings and OOBE page to clarify that the AI use is optional and opt-in. (This was a hotfix for 0.81)
|
||||
- Corrected a spelling fix in Advanced Paste's settings page. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Added localization support for the "Configure OpenAI Key" button in Advanced Paste's settings page. Thanks [@zetaloop](https://github.com/zetaloop)!
|
||||
- Fixed extra GPO warnings being shown in Advanced Paste's settings page even if the module is disabled. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Fixed a crash when a PowerToys Run plugin icon path is badly formed.
|
||||
- Disabled the experimentation paths in code behind to improve performance, since there's no current experimentation going on.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Added FastWeb plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@CCcat8059](https://github.com/CCcat8059)!
|
||||
- Removed the old security link to MSRC from the create new issue page, since security.md is already linked there.
|
||||
- Added clarity regarding unofficial plugins to the PowerToys Run thirdPartyRunPlugins.md docs.
|
||||
- Adjusted the readme and release notes to clarify use of AI on Advanced Paste. (This was a hotfix for 0.81)
|
||||
- Added the Edge Workspaces plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@quachpas](https://github.com/quachpas)!
|
||||
- Removed the deprecated Guid plugin from PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@abduljawada](https://github.com/abduljawada)!
|
||||
- Added the PowerHexInspector plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@NaroZeol](https://github.com/NaroZeol)!
|
||||
- Fixed a broken link in the communication-with-modules.md file. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Updated COMMUNITY.md with missing and former members.
|
||||
|
||||
### Development
|
||||
|
||||
- Updated System.Drawing.Common to 8.0.3 to fix CI builds after the .NET 8.0.3 upgrade was released.
|
||||
- Adjusted the GitHub action names for releasing to winget and Microsoft Store so they're clearer in the UI.
|
||||
- Upgraded WinAppSDK to 1.5.1, fixing many related issues.
|
||||
- Consolidate the WebView2 version used by WinUI 2 in the Keyboard Manager Editor.
|
||||
- Unified the use of Precompiled Headers when building on CI. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Added UI tests for FancyZones Editor in CI.
|
||||
- Added a GitHub bot to identify possible duplicates when a new issue is created. Thanks [@craigloewen-msft](https://github.com/craigloewen-msft)!
|
||||
- Updated the WiX installer dependency to 3.14.1 to fix possible security issues.
|
||||
- Changed the pipelines to use pipeline artifacts instead of build artifacts. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Added the -graph parameter for pipelines. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Tests in the pipelines now run as part of the build step to save on CI time. Thanks [@dfederm](https://github.com/dfederm)!
|
||||
- Fixed ci UI tests to point to the correct Visual Studio vstest location after a Visual Studio upgrade. (This was a hotfix for 0.81)
|
||||
- Updated System.Drawing.Common to 8.0.6 to fix CI builds after the .NET 8.0.6 upgrade was released.
|
||||
- Removed an incorrect file reference to long removed documentation from the solution file. Thanks [@Kissaki](https://github.com/Kissaki)!
|
||||
- Upgraded Windows App SDK to 1.5.3.
|
||||
- Removed use of the BinaryFormatter API from Mouse Without Borders, which is expected to be deprecated in .NET 9.
|
||||
- The user scope installer is now sent to the Microsoft store instead of the machine scope installer.
|
||||
- Refactored Mouse Jump's internal code to allow for a future introduction of customizable appearance features. Thanks [@mikeclayton](https://github.com/mikeclayton)!
|
||||
- Removed a noisy error from spell check ci runs.
|
||||
- Improved the ci agent pool selection code.
|
||||
- Updated Xamlstyler.console to 3.2404.2. Thanks [@Jvr2022](https://github.com/Jvr2022)!
|
||||
- Updated UnitsNet to 5.50.0 Thanks [@Jvr2022](https://github.com/Jvr2022)!
|
||||
- Replaced LPINPUT with std::vector of INPUT instances in Keyboard Manager internal code. Thanks [@masaru-iritani](https://github.com/masaru-iritani)!
|
||||
- Improved the Microsoft Store submission ci action to use the proper cli and authentication.
|
||||
|
||||
#### What is being planned for version 0.81
|
||||
#### What is being planned for version 0.83
|
||||
|
||||
For [v0.81][github-next-release-work], we'll work on the items below:
|
||||
For [v0.83][github-next-release-work], we'll work on the items below:
|
||||
|
||||
- Stability / bug fixes
|
||||
- New utility: Dev Projects
|
||||
- Language selection
|
||||
- New module: File Actions Menu
|
||||
|
||||
The next release is planned to be released during Microsoft Build 2024.
|
||||
|
||||
## PowerToys Community
|
||||
|
||||
The PowerToys team is extremely grateful to have the [support of an amazing active community][community-link]. The work you do is incredibly important. PowerToys wouldn’t be nearly what it is today without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thank you and take time to recognize your work. Month by month, you directly help make PowerToys a better piece of software.
|
||||
|
||||
@@ -47,7 +47,16 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco)
|
||||
|
||||
* The id can be anything. Recommended is one of the file extensions. For example "php" or "reg".
|
||||
|
||||
4. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section.
|
||||
4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js):
|
||||
```javascript
|
||||
{token: 'token-name', foreground: 'ff0000'}
|
||||
```
|
||||
> Replace `token-name` with the name of the token and `ff0000` with the hex code of the desired color.
|
||||
> Note: you can also specify a `background` and a `fontStyle` attribute for your token.
|
||||
|
||||
* Keep in mind that these rules apply to all languages. Therefore, you should not change the colors of any default tokens. Instead, create new tokens specific to the language you are adding.
|
||||
|
||||
5. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section.
|
||||
|
||||
### Add a new file extension to an existing language
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ In order to test the remapping logic, a mocked keyboard input handler had to be
|
||||
The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/MockedInput.h) class uses a 256 size `bool` vector to store the key state for each key code. Identifying the foreground process is mocked by simply setting and getting a string value for the name of the current process.
|
||||
|
||||
[To mock the `SendInput` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L10-L110), the steps for processing the input are as follows. This implementation is based on public documentation for SendInput and the behavior of key messages and keyboard hooks:
|
||||
- Iterate over all the inputs in the INPUT array argument
|
||||
- Iterate over all the inputs in the `INPUT` vector argument.
|
||||
- If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down, otherwise it is `WM_KEYUP`.
|
||||
- If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10, otherwise it is `WM_KEYDOWN`.
|
||||
- An optional function which can be set on the `MockedInput` handler can be used to test for the number of times a key event is received by the system with a particular condition using [`sendVirtualInputCallCondition`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L48-L52).
|
||||
|
||||
@@ -71,12 +71,14 @@ The following formats are currently available:
|
||||
- All available settings for the plugin are defined in the [`TimeDateSettings`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) class of the plugin. The settings can be accessed everywhere in the plugin code via the static class instance `TimeDateSettings.Instance`.
|
||||
- We have the following settings that the user can configure to change the behavior of the plugin:
|
||||
|
||||
| Key | Default value | Name | Description |
|
||||
|--------------|-----------|------------|------------|
|
||||
| `OnlyDateTimeNowGlobal` | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. |
|
||||
| `TimeWithSeconds` | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. |
|
||||
| `DateWithWeekday` | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. |
|
||||
| `HideNumberMessageOnGlobalQuery` | `false` | Hide 'Invalid number input' error message on global queries | |
|
||||
| Key | Type | Default value | Name | Description |
|
||||
|--------------|--------------|-----------|------------|------------|
|
||||
| `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. |
|
||||
| `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | |
|
||||
| `OnlyDateTimeNowGlobal` | Checkbox | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. |
|
||||
| `TimeWithSeconds` | Checkbox | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. |
|
||||
| `DateWithWeekday` | Checkbox | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. |
|
||||
| `HideNumberMessageOnGlobalQuery` | Checkbox | `false` | Hide 'Invalid number input' error message on global queries | |
|
||||
|
||||
|
||||
## Classes
|
||||
@@ -97,6 +99,7 @@ The following formats are currently available:
|
||||
|
||||
### [`TimeAndDateHelper.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs)
|
||||
- The [`TimeAndDateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeAndDateHelper.cs) class contains methods to format/convert date and time formats/strings.
|
||||
- And it contains methods to return the `first week day` and `first week of the year rule` based on the current plugin settings.
|
||||
|
||||
### [`TimeDateSettings.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs)
|
||||
- The [`TimeDateSettings`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/TimeDateSettings.cs) class provides access to all optional plugin settings.
|
||||
@@ -129,11 +132,6 @@ On global queries the high score returned by `FuzzySearch` has negative impacts
|
||||
## [Unit Tests](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests)
|
||||
We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests) that executes various test to ensure that the plugin works as expected.
|
||||
|
||||
### [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs)
|
||||
- The [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) class contains tests to validate that the time and date values are correctly formatted/calculated.
|
||||
- That we can execute the tests at any time on any machine, we use a specified date/time value and set the thread culture always to `en-us` while executing the tests.
|
||||
- Some tests contain checks that calculate the expected result at runtime instead of using an expected value written fix in the code. This is done to get valid results on every machine at any time.
|
||||
|
||||
### [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs)
|
||||
- The [`ImageTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/ImageTests.cs) class contains tests to validate that each result shows the expected and correct image.
|
||||
- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests.
|
||||
@@ -147,4 +145,13 @@ We have a [Unit Test project](/src/modules/launcher/Plugins/Microsoft.PowerToys.
|
||||
|
||||
### [`StringParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/StringParserTests.cs)
|
||||
- The [`StringParserTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/StringParserTests.cs) class contains tests to validate that the typed string gets converted correctly into a `DateTime` object.
|
||||
- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests.
|
||||
- That we can execute the tests at any time on any machine, we set the thread culture always to `en-us` while executing the tests.
|
||||
|
||||
### [`TimeAndDateHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs)
|
||||
- The [`TimeAndDateHelperTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeAndDateHelperTests.cs) class contains tests to validate important methods form the `TimeAndDateHelper` class that are not used for string parsing.
|
||||
|
||||
### [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs)
|
||||
- The [`TimeDateResultTests.cs`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests/TimeDateResultTests.cs) class contains tests to validate that the time and date values are correctly formatted/calculated.
|
||||
- That we can execute the tests at any time on any machine, we use a specified date/time value and set the thread culture always to `en-us` while executing the tests.
|
||||
- Some tests use custom settings for the first day of week and the first week of year. (This is done in the tests for the affected results to validate them for different settings/cultures.)
|
||||
- Some tests contain checks that calculate the expected result at runtime instead of using an expected value written fix in the code. This is done to get valid results on every machine at any time.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Through runner
|
||||
- The settings process communicates changes in the UI to most modules using the runner through delegates.
|
||||
- More details on this are mentioned in [`runner-ipc.md`](settingsv2/runner-ipc.md).
|
||||
- More details on this are mentioned in [`runner-ipc.md`](runner-ipc.md).
|
||||
|
||||
## PT Run
|
||||
- Any changes to the UI are saved by the settings process in the `settings.json` file located within the `/Local/Microsoft/PowerToys/Launcher/` folder.
|
||||
@@ -12,4 +12,4 @@ Eg: The maximum number of results drop down updates the maximum number of rows i
|
||||
## Keyboard Manager
|
||||
- The Settings process and keyboard manager share access to a common `default.json` file which contains information about the remapped keys and shortcuts.
|
||||
- To ensure that there is no contention while both processes try to access the common file, there is a named file mutex.
|
||||
- The settings process expects the keyboard manager process to create the `default.json` file if it does not exist. It does not create the file in case it is not present.
|
||||
- The settings process expects the keyboard manager process to create the `default.json` file if it does not exist. It does not create the file in case it is not present.
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
---
|
||||
last-update: 3-20-2022
|
||||
last-update: 7-16-2024
|
||||
---
|
||||
|
||||
# PowerToys Awake Changelog
|
||||
|
||||
## Builds
|
||||
|
||||
The build ID can be found in `Program.cs` in the `BuildId` variable - it is a unique identifier for the current builds that allows better diagnostics (we can look up the build ID from the logs) and offers a way to triage Awake-specific issues faster independent of the PowerToys version. The build ID does not carry any significance beyond that within the PowerToys code base.
|
||||
The build ID can be found in `Core\Constants.cs` in the `BuildId` variable - it is a unique identifier for the current builds that allows better diagnostics (we can look up the build ID from the logs) and offers a way to triage Awake-specific issues faster independent of the PowerToys version. The build ID does not carry any significance beyond that within the PowerToys code base.
|
||||
|
||||
The build ID moniker is made up of two components - a reference to a [Halo](https://en.wikipedia.org/wiki/Halo_(franchise)) character, and the date when the work on the specific build started in the format of `MMDDYYYY`.
|
||||
|
||||
| Build ID | Build Date |
|
||||
|:----------------------------------------------------------|:-----------------|
|
||||
| [`DAISY023_04102024`](#DAISY023_04102024-april-10-2024) | April 10, 2024 |
|
||||
| [`ATRIOX_04132023`](#ATRIOX_04132023-april-13-2023) | April 13, 2023 |
|
||||
| [`LIBRARIAN_03202022`](#librarian_03202022-march-20-2022) | March 20, 2022 |
|
||||
| `ARBITER_01312022` | January 31, 2022 |
|
||||
|
||||
### `DAISY023_04102024` (April 10, 2024)
|
||||
|
||||
>[!NOTE]
|
||||
>See pull request: [Awake Update - `DAISY023_04102024`](https://github.com/microsoft/PowerToys/pull/32378)
|
||||
|
||||
- [#33630](https://github.com/microsoft/PowerToys/issues/33630) When in the UI and you select `0` as hours and `0` as minutes in `TIMED` awake mode, the UI becomes non-responsive whenever you try to get back to timed after it rolls back to `PASSIVE`.
|
||||
- [#12714](https://github.com/microsoft/PowerToys/issues/12714) Adds the option to keep track of Awake state through tray tooltip.
|
||||
- [#11996](https://github.com/microsoft/PowerToys/issues/11996) Adds custom icons support for mode changes in Awake.
|
||||
- Removes the dependency on `System.Windows.Forms` and instead uses native Windows APIs to create the tray icon.
|
||||
- Removes redundant/unused code that impacted application performance.
|
||||
- Updates dependent packages to their latest versions (`Microsoft.Windows.CsWinRT` and `System.Reactive`).
|
||||
|
||||
### `ATRIOX_04132023` (April 13, 2023)
|
||||
|
||||
- Moves from using `Task.Run` to spin up threads to actually using a blocking queue that properly sets thread parameters on the same thread.
|
||||
|
||||
@@ -27,7 +27,6 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| ------ | ------ | ----------- |
|
||||
| [BrowserSearch](https://github.com/TBM13/BrowserSearch) | [TBM13](https://github.com/TBM13) | Search your browser history |
|
||||
| [GitHub Emoji](https://github.com/hlaueriksson/GEmojiSharp) | [hlaueriksson](https://github.com/hlaueriksson) | Search GitHub Emoji |
|
||||
| [Guid](https://github.com/skttl/ptrun-guid) | [skttl](https://github.com/skttl) | Guid generator |
|
||||
| [PowerTranslator](https://github.com/N0I0C0K/PowerTranslator) | [N0I0C0K](https://github.com/N0I0C0K) | Text translator based on Youdao |
|
||||
| [Quick Lookup](https://github.com/GTGalaxi/quick-lookup-ptrun) | [gtgalaxi](https://github.com/GTGalaxi) | Search across multiple cyber security tools |
|
||||
| [Input Typer](https://github.com/CoreyHayward/PowerToys-Run-InputTyper) | [CoreyHayward](https://github.com/CoreyHayward) | Type the input as if sent from a keyboard |
|
||||
@@ -36,6 +35,9 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [FastWeb](https://github.com/CCcat8059/FastWeb) | [CCcat](https://github.com/CCcat8059) | Open website in browser |
|
||||
| [WebSearchShortcut](https://github.com/Daydreamer-riri/PowerToys-Run-WebSearchShortcut) | [Riri](https://github.com/Daydreamer-riri) | Select a specific search engine to perform searches. |
|
||||
| [UnicodeInput](https://github.com/nathancartlidge/powertoys-run-unicode) | [nathancartlidge](https://github.com/nathancartlidge) | Copy Unicode characters to the clipboard |
|
||||
| [PowerHexInspector](https://github.com/NaroZeol/PowerHexInspector) | [NaroZeol](https://github.com/NaroZeol) | Peek other forms of an input number |
|
||||
| [GitHubRepo](https://github.com/8LWXpg/PowerToysRun-GitHubRepo) | [8LWXpg](https://github.com/8LWXpg) | Search and open GitHub repositories |
|
||||
| [ProcessKiller](https://github.com/8LWXpg/PowerToysRun-ProcessKiller) | [8LWXpg](https://github.com/8LWXpg) | Search and kill processes |
|
||||
|
||||
## Extending software plugins
|
||||
|
||||
@@ -44,8 +46,10 @@ Below are community created plugins that target a website or software. They are
|
||||
| Plugin | Author | Description |
|
||||
| ------ | ------ | ----------- |
|
||||
| [Edge Favorite](https://github.com/davidegiacometti/PowerToys-Run-EdgeFavorite) | [davidegiacometti](https://github.com/davidegiacometti) | Open Microsoft Edge favorites |
|
||||
| [Edge Workspaces](https://github.com/quachpas/PowerToys-Run-EdgeWorkspaces) | [quachpas](https://github.com/quachpas) | Open Microsoft Edge workspaces|
|
||||
| [Everything](https://github.com/lin-ycv/EverythingPowerToys) | [Yu Chieh (Victor) Lin](https://github.com/Lin-ycv) | Get search results from Everything |
|
||||
| [GitKraken](https://github.com/davidegiacometti/PowerToys-Run-GitKraken) | [davidegiacometti](https://github.com/davidegiacometti) | Open GitKraken repositories |
|
||||
| [RDP](https://github.com/anthony81799/PowerToysRun-RDP)) | [anthony81799](https://github.com/anthony81799) | Open Remote Desktop connections |
|
||||
| [Visual Studio Recents](https://github.com/davidegiacometti/PowerToys-Run-VisualStudio) | [davidegiacometti](https://github.com/davidegiacometti) | Open Visual Studio recents |
|
||||
| [WinGet](https://github.com/bostrot/PowerToysRunPluginWinget) | [bostrot](https://github.com/bostrot) | Search and install packages from WinGet |
|
||||
| [Scoop](https://github.com/Quriz/PowerToysRunScoop) | [Quriz](https://github.com/Quriz) | Search and install packages from Scoop |
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BinDir)">
|
||||
<Component Id="powertoys_per_machine_comp" Win64="yes">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys">
|
||||
<RegistryValue Type="string" Name="InstallScope" Value="$(var.InstallScope)" />
|
||||
<RegistryValue Type="string" Name="InstallScope" Value="$(var.InstallScope)" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
<Component Id="powertoys_toast_clsid" Win64="yes">
|
||||
@@ -46,34 +46,19 @@
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<DirectoryRef Id="PersonalFolder">
|
||||
<Directory Id="WindowsPowerShellFolder" Name="PowerShell">
|
||||
<Directory Id="PowerShellModulesFolder" Name="Modules">
|
||||
<Directory Id="PowerToysDscFolder" Name="Microsoft.PowerToys.Configure">
|
||||
<Directory Id="PowerToysDscVerFolder" Name="$(var.Version)">
|
||||
<Component Id="PowerToysDSC" Win64="yes" Guid="4A033E3B-6590-43FD-8FBD-27F9DF557F7F">
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\[Manufacturer]\[ProductName]"
|
||||
Name="DSCInstalled"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes"/>
|
||||
<!-- Don't fail installation because of DSC. Files are marked as not vital. -->
|
||||
<File Vital="no" Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConf.psd1" />
|
||||
<File Vital="no" Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConf.psm1" />
|
||||
<RemoveFolder Id="RemoveThisFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemovePowerToysDscVerFolder" Directory="PowerToysDscVerFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemovePowerToysDscFolder" Directory="PowerToysDscFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemovePowerShellModulesFolder" Directory="PowerShellModulesFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveWindowsPowerShellFolder" Directory="WindowsPowerShellFolder" On="uninstall" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<DirectoryRef Id="DSCModulesReferenceFolder">
|
||||
<Component Id="PowerToysDSCReference" Win64="yes" Guid="40869ACB-0BEB-4911-AE41-5E73BC1586A9">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="DSCModulesReference" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psd1" Id="PTConfReference.psd1" />
|
||||
<File Source="$(var.RepoDir)\src\dsc\Microsoft.PowerToys.Configure\Generated\Microsoft.PowerToys.Configure\$(var.Version)\Microsoft.PowerToys.Configure.psm1" Id="PTConfReference.psm1" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?else?>
|
||||
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<!-- DSC module files for PerUser handled in InstallDSCModule custom action. -->
|
||||
<?else?>
|
||||
<DirectoryRef Id="ProgramFiles64Folder">
|
||||
<Directory Id="WindowsPowerShellFolder" Name="WindowsPowerShell">
|
||||
<Directory Id="PowerShellModulesFolder" Name="Modules">
|
||||
@@ -89,7 +74,7 @@
|
||||
</Directory>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<?endif?>
|
||||
<?endif?>
|
||||
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="PowerToysStartMenuShortcut" >
|
||||
@@ -130,23 +115,27 @@
|
||||
</Fragment>
|
||||
<Fragment>
|
||||
<ComponentGroup Id="CoreComponents">
|
||||
<Component Id="RemoveCoreFolder" Guid="9330BD69-2D12-4D98-B0C7-66C99564D619" Directory="INSTALLFOLDER" >
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveCoreFolder" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveBaseApplicationsAssetsFolder" Directory="BaseApplicationsAssetsFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveWinUI3AppsInstallFolder" Directory="WinUI3AppsInstallFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveWinUI3AppsAssetsFolder" Directory="WinUI3AppsAssetsFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveINSTALLFOLDER" Directory="INSTALLFOLDER" On="uninstall"/>
|
||||
</Component>
|
||||
<ComponentRef Id="powertoys_exe" />
|
||||
<ComponentRef Id="PowerToysStartMenuShortcut"/>
|
||||
<ComponentRef Id="powertoys_per_machine_comp" />
|
||||
<ComponentRef Id="powertoys_toast_clsid" />
|
||||
<ComponentRef Id="License_rtf" />
|
||||
<ComponentRef Id="Notice_md" />
|
||||
<ComponentRef Id="DesktopShortcut" />
|
||||
<ComponentRef Id="PowerToysDSC" />
|
||||
<Component Id="RemoveCoreFolder" Guid="9330BD69-2D12-4D98-B0C7-66C99564D619" Directory="INSTALLFOLDER" >
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveCoreFolder" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveBaseApplicationsAssetsFolder" Directory="BaseApplicationsAssetsFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveDSCModulesReferenceFolder" Directory="DSCModulesReferenceFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveWinUI3AppsInstallFolder" Directory="WinUI3AppsInstallFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveWinUI3AppsAssetsFolder" Directory="WinUI3AppsAssetsFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveINSTALLFOLDER" Directory="INSTALLFOLDER" On="uninstall"/>
|
||||
</Component>
|
||||
<ComponentRef Id="powertoys_exe" />
|
||||
<ComponentRef Id="PowerToysStartMenuShortcut"/>
|
||||
<ComponentRef Id="powertoys_per_machine_comp" />
|
||||
<ComponentRef Id="powertoys_toast_clsid" />
|
||||
<ComponentRef Id="License_rtf" />
|
||||
<ComponentRef Id="Notice_md" />
|
||||
<ComponentRef Id="DesktopShortcut" />
|
||||
<ComponentRef Id="PowerToysDSCReference" />
|
||||
<?if $(var.PerUser) = "false" ?>
|
||||
<ComponentRef Id="PowerToysDSC" />
|
||||
<?endif?>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
@@ -76,4 +76,12 @@
|
||||
<Error Condition="!Exists('..\wix.props')"
|
||||
Text="$([System.String]::Format('$(ErrorText)', '..\wix.props'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Prevents NU1503 -->
|
||||
<Target Name="_IsProjectRestoreSupported" Returns="@(_ValidProjectsForRestore)">
|
||||
<ItemGroup>
|
||||
<_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="Restore" />
|
||||
</Project>
|
||||
@@ -185,4 +185,12 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
|
||||
<Target Name="BeforeBuild">
|
||||
<HeatDirectory Directory="..\..\src\common\FilePreviewCommon\Assets\Monaco\monacoSRC" PreprocessorVariable="var.MonacoSRCHarvestPath" OutputFile="MonacoSRC.wxs" ComponentGroupName="MonacoSRCHeatGenerated" DirectoryRefId="MonacoPreviewHandlerMonacoSRCFolder" AutogenerateGuids="false" GenerateGuidsNow="true" ToolPath="$(WixToolPath)" RunAsSeparateProcess="true" SuppressFragments="false" SuppressRegistry="false" SuppressRootDirectory="true"/>
|
||||
</Target>
|
||||
|
||||
<!-- Prevents NU1503 -->
|
||||
<Target Name="_IsProjectRestoreSupported" Returns="@(_ValidProjectsForRestore)">
|
||||
<ItemGroup>
|
||||
<_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="Restore" />
|
||||
</Project>
|
||||
@@ -136,6 +136,11 @@
|
||||
<Custom Action="SetUninstallCommandNotFoundParam" Before="UninstallCommandNotFound" />
|
||||
<Custom Action="SetUpgradeCommandNotFoundParam" Before="UpgradeCommandNotFound" />
|
||||
<Custom Action="SetApplyModulesRegistryChangeSetsParam" Before="ApplyModulesRegistryChangeSets" />
|
||||
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<Custom Action="SetInstallDSCModuleParam" Before="InstallDSCModule" />
|
||||
<?endif?>
|
||||
|
||||
<Custom Action="SetUnApplyModulesRegistryChangeSetsParam" Before="UnApplyModulesRegistryChangeSets" />
|
||||
<Custom Action="CheckGPO" After="InstallInitialize">
|
||||
NOT Installed
|
||||
@@ -149,7 +154,10 @@
|
||||
<!--<Custom Action="InstallEmbeddedMSIXTask" After="InstallFinalize">
|
||||
NOT Installed
|
||||
</Custom>-->
|
||||
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize">
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<Custom Action="InstallDSCModule" After="InstallFiles"/>
|
||||
<?endif?>
|
||||
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize">
|
||||
NOT Installed
|
||||
</Custom>
|
||||
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize">
|
||||
@@ -177,8 +185,12 @@
|
||||
<!--<Custom Action="UninstallEmbeddedMSIXTask" After="InstallFinalize">
|
||||
Installed AND (REMOVE="ALL")
|
||||
</Custom>-->
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<Custom Action="UninstallDSCModule" After="InstallFinalize">
|
||||
Installed AND (REMOVE="ALL")
|
||||
</Custom>
|
||||
<?endif?>
|
||||
<Custom Action="TerminateProcesses" Before="InstallValidate" />
|
||||
|
||||
<Custom Action="LaunchPowerToys" Before="InstallFinalize">NOT Installed</Custom>
|
||||
|
||||
</InstallExecuteSequence>
|
||||
@@ -211,6 +223,10 @@
|
||||
Property="UnApplyModulesRegistryChangeSets"
|
||||
Value="[INSTALLFOLDER]" />
|
||||
|
||||
<CustomAction Id="SetInstallDSCModuleParam"
|
||||
Property="InstallDSCModule"
|
||||
Value="[INSTALLFOLDER]" />
|
||||
|
||||
<CustomAction Id="SetUninstallCommandNotFoundParam"
|
||||
Property="UninstallCommandNotFound"
|
||||
Value="[INSTALLFOLDER]" />
|
||||
@@ -265,6 +281,21 @@
|
||||
DllEntry="UninstallEmbeddedMSIXCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="InstallDSCModule"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
Execute="deferred"
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="InstallDSCModuleCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="UninstallDSCModule"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="UninstallDSCModuleCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="UninstallServicesTask"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
@@ -407,6 +438,7 @@
|
||||
<Directory Id="INSTALLFOLDER" Name="PowerToys">
|
||||
<Directory Id="BaseApplicationsAssetsFolder" Name="Assets">
|
||||
</Directory>
|
||||
<Directory Id="DSCModulesReferenceFolder" Name="DSCModules" />
|
||||
<Directory Id="WinUI3AppsInstallFolder" Name="WinUI3Apps">
|
||||
<Directory Id="WinUI3AppsMicrosoftUIXamlInstallFolder" Name="Microsoft.UI.Xaml">
|
||||
<Directory Id="WinUI3AppsMicrosoftUIXamlAssetsInstallFolder" Name="Assets" />
|
||||
@@ -421,9 +453,6 @@
|
||||
<Directory Id="ApplicationProgramsFolder" Name="PowerToys (Preview)"/>
|
||||
</Directory>
|
||||
<Directory Id="DesktopFolder" Name="Desktop" />
|
||||
<?if $(var.PerUser) = "true" ?>
|
||||
<Directory Id="PersonalFolder" Name="UserHomeDocuments" />
|
||||
<?endif?>
|
||||
</Directory>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
@@ -21,7 +21,7 @@ $fileWxs = Get-Content $wxsFilePath;
|
||||
|
||||
$fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
|
||||
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "*.pri")
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "customTokenColors.js", "*.pri")
|
||||
|
||||
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
|
||||
|
||||
|
||||
@@ -139,6 +139,23 @@ LExit:
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
static std::filesystem::path GetUserPowerShellModulesPath()
|
||||
{
|
||||
PWSTR myDocumentsBlockPtr;
|
||||
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &myDocumentsBlockPtr)))
|
||||
{
|
||||
const std::wstring myDocuments{ myDocumentsBlockPtr };
|
||||
CoTaskMemFree(myDocumentsBlockPtr);
|
||||
return std::filesystem::path(myDocuments) / "PowerShell" / "Modules";
|
||||
}
|
||||
else
|
||||
{
|
||||
CoTaskMemFree(myDocumentsBlockPtr);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
@@ -162,7 +179,7 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
|
||||
BOOL isSystemUser = IsLocalSystem();
|
||||
|
||||
if (isSystemUser) {
|
||||
|
||||
|
||||
auto action = [&commandLine](HANDLE userToken) {
|
||||
STARTUPINFO startupInfo{ .cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL };
|
||||
PROCESS_INFORMATION processInformation;
|
||||
@@ -317,6 +334,125 @@ LExit:
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
const wchar_t* DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
|
||||
const wchar_t* DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
|
||||
|
||||
UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
std::wstring installationFolder;
|
||||
|
||||
hr = WcaInitialize(hInstall, "InstallDSCModuleCA");
|
||||
ExitOnFailure(hr, "Failed to initialize");
|
||||
|
||||
hr = getInstallFolder(hInstall, installationFolder);
|
||||
ExitOnFailure(hr, "Failed to get installFolder.");
|
||||
|
||||
{
|
||||
const auto baseModulesPath = GetUserPowerShellModulesPath();
|
||||
if (baseModulesPath.empty())
|
||||
{
|
||||
hr = E_FAIL;
|
||||
ExitOnFailure(hr, "Unable to determine Powershell modules path");
|
||||
}
|
||||
|
||||
const auto modulesPath = baseModulesPath / L"Microsoft.PowerToys.Configure" / get_product_version(false);
|
||||
|
||||
std::error_code errorCode;
|
||||
fs::create_directories(modulesPath, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
hr = E_FAIL;
|
||||
ExitOnFailure(hr, "Unable to create Powershell modules folder");
|
||||
}
|
||||
|
||||
for (const auto* filename : { DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME })
|
||||
{
|
||||
fs::copy_file(fs::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, fs::copy_options::overwrite_existing, errorCode);
|
||||
|
||||
if (errorCode)
|
||||
{
|
||||
hr = E_FAIL;
|
||||
ExitOnFailure(hr, "Unable to copy Powershell modules file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LExit:
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
er = ERROR_SUCCESS;
|
||||
Logger::info(L"DSC module was installed!");
|
||||
}
|
||||
else
|
||||
{
|
||||
er = ERROR_INSTALL_FAILURE;
|
||||
Logger::error(L"Couldn't install DSC module!");
|
||||
}
|
||||
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
|
||||
hr = WcaInitialize(hInstall, "UninstallDSCModuleCA");
|
||||
ExitOnFailure(hr, "Failed to initialize");
|
||||
|
||||
{
|
||||
const auto baseModulesPath = GetUserPowerShellModulesPath();
|
||||
if (baseModulesPath.empty())
|
||||
{
|
||||
hr = E_FAIL;
|
||||
ExitOnFailure(hr, "Unable to determine Powershell modules path");
|
||||
}
|
||||
|
||||
const auto powerToysModulePath = baseModulesPath / L"Microsoft.PowerToys.Configure";
|
||||
const auto versionedModulePath = powerToysModulePath / get_product_version(false);
|
||||
|
||||
std::error_code errorCode;
|
||||
|
||||
for (const auto* filename : { DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME })
|
||||
{
|
||||
fs::remove(versionedModulePath / filename, errorCode);
|
||||
|
||||
if (errorCode)
|
||||
{
|
||||
hr = E_FAIL;
|
||||
ExitOnFailure(hr, "Unable to delete DSC file");
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto* modulePath : { &versionedModulePath, &powerToysModulePath })
|
||||
{
|
||||
fs::remove(*modulePath, errorCode);
|
||||
|
||||
if (errorCode)
|
||||
{
|
||||
hr = E_FAIL;
|
||||
ExitOnFailure(hr, "Unable to delete DSC folder");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LExit:
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
er = ERROR_SUCCESS;
|
||||
Logger::info(L"DSC module was uninstalled!");
|
||||
}
|
||||
else
|
||||
{
|
||||
er = ERROR_INSTALL_FAILURE;
|
||||
Logger::error(L"Couldn't uninstall DSC module!");
|
||||
}
|
||||
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
@@ -472,9 +608,19 @@ UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
|
||||
hr = getInstallFolder(hInstall, installationFolder);
|
||||
ExitOnFailure(hr, "Failed to get installFolder.");
|
||||
|
||||
#ifdef _M_ARM64
|
||||
command = "powershell.exe";
|
||||
command += " ";
|
||||
command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted";
|
||||
command += " -Command ";
|
||||
command += "\"[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'User'), 'Process');";
|
||||
command += "pwsh.exe -NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File '" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "'\"";
|
||||
#else
|
||||
command = "pwsh.exe";
|
||||
command += " ";
|
||||
command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
|
||||
#endif
|
||||
|
||||
|
||||
system(command.c_str());
|
||||
|
||||
|
||||
@@ -18,10 +18,12 @@ EXPORTS
|
||||
CertifyVirtualCameraDriverCA
|
||||
InstallVirtualCameraDriverCA
|
||||
InstallEmbeddedMSIXCA
|
||||
InstallDSCModuleCA
|
||||
UnApplyModulesRegistryChangeSetsCA
|
||||
UninstallVirtualCameraDriverCA
|
||||
UnRegisterContextMenuPackagesCA
|
||||
UninstallEmbeddedMSIXCA
|
||||
UninstallDSCModuleCA
|
||||
UninstallServicesCA
|
||||
UninstallCommandNotFoundModuleCA
|
||||
UpgradeCommandNotFoundModuleCA
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.271-preview" />
|
||||
<package id="Microsoft.MSBuildCache.Local" version="0.1.271-preview" />
|
||||
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.271-preview" />
|
||||
<package id="Microsoft.MSBuildCache.AzurePipelines" version="0.1.283-preview" />
|
||||
<package id="Microsoft.MSBuildCache.Local" version="0.1.283-preview" />
|
||||
<package id="Microsoft.MSBuildCache.SharedCompilation" version="0.1.283-preview" />
|
||||
</packages>
|
||||
@@ -12,5 +12,10 @@ namespace Common.UI
|
||||
{
|
||||
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= 22000;
|
||||
}
|
||||
|
||||
public static bool IsGreaterThanWindows11_21H2()
|
||||
{
|
||||
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build > 22000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/^#.*$/, 'comment'],
|
||||
[/^\s*!.*/, 'invalid'],
|
||||
[/^\s*[^#]+/, "tag"]
|
||||
[/.*((?<!(^|\/))\*\*.*|\*\*(?!(\/|$))).*/, 'invalid'],
|
||||
[/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-gitignore.negation', 'tag', 'invalid']]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export const customTokenColors = [
|
||||
{token: 'custom-gitignore.negation', foreground: 'c00ce0'}
|
||||
];
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
// Get URL parameters:
|
||||
// `code` contains the code of the file in base64 encoded
|
||||
// `theme` can be "light" or "dark"
|
||||
// `theme` can be "vs" for light theme or "vs-dark" for dark theme
|
||||
// `lang` is the language of the file
|
||||
// `wrap` if the editor is wrapping or not
|
||||
|
||||
|
||||
var theme = ("[[PT_THEME]]" == "dark") ? "vs-dark" : "vs";
|
||||
var lang = "[[PT_LANG]]";
|
||||
var wrap = ([[PT_WRAP]] == 1) ? true : false;
|
||||
@@ -19,11 +19,29 @@
|
||||
var stickyScroll = ([[PT_STICKY_SCROLL]] == 1) ? true : false;
|
||||
|
||||
var fontSize = [[PT_FONT_SIZE]];
|
||||
|
||||
var contextMenu = ([[PT_CONTEXTMENU]] == 1) ? true : false;
|
||||
|
||||
var editor;
|
||||
|
||||
// Code taken from https://stackoverflow.com/a/30106551/14774889
|
||||
var code = decodeURIComponent(atob(base64code).split('').map(function(c) {
|
||||
var code = decodeURIComponent(atob(base64code).split('').map(function (c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
|
||||
function runToggleTextWrapCommand() {
|
||||
if (wrap) {
|
||||
editor.updateOptions({ wordWrap: 'off' })
|
||||
} else {
|
||||
editor.updateOptions({ wordWrap: 'on' })
|
||||
}
|
||||
wrap = !wrap;
|
||||
}
|
||||
|
||||
function runCopyCommand() {
|
||||
editor.focus();
|
||||
document.execCommand('copy');
|
||||
}
|
||||
|
||||
</script>
|
||||
<!-- Set browser to Edge-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
@@ -33,45 +51,57 @@
|
||||
<title>Previewer for developer Files</title>
|
||||
<style>
|
||||
/* Fits content to window size */
|
||||
html, body{
|
||||
padding:0;
|
||||
html, body {
|
||||
padding: 0;
|
||||
}
|
||||
#container,.monaco-editor {
|
||||
position:fixed;
|
||||
height:100%;
|
||||
left:0;
|
||||
top:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
|
||||
#container, .monaco-editor {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.overflowingContentWidgets{
|
||||
|
||||
.overflowingContentWidgets {
|
||||
/*Hides alert box */
|
||||
display:none!important
|
||||
}
|
||||
display: none !important
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body oncontextmenu="onContextMenu()">
|
||||
<body>
|
||||
<!-- Container for the editor -->
|
||||
<div id="container"></div>
|
||||
<!-- Script -->
|
||||
<script src="http://[[PT_URL]]/monacoSRC/min/vs/loader.js"></script>
|
||||
<script src="http://[[PT_URL]]/monacoSpecialLanguages.js" type="module"></script>
|
||||
<script type="module">
|
||||
var editor;
|
||||
import { registerAdditionalLanguages } from "http://[[PT_URL]]/monacoSpecialLanguages.js"
|
||||
<script type="module">
|
||||
import { registerAdditionalLanguages } from 'http://[[PT_URL]]/monacoSpecialLanguages.js';
|
||||
import { customTokenColors } from 'http://[[PT_URL]]/customTokenColors.js';
|
||||
require.config({ paths: { vs: 'http://[[PT_URL]]/monacoSRC/min/vs' } });
|
||||
require(['vs/editor/editor.main'], async function () {
|
||||
await registerAdditionalLanguages(monaco)
|
||||
|
||||
// Creates a theme to handle custom tokens
|
||||
monaco.editor.defineTheme('theme', {
|
||||
base: theme, // Sets the base theme to "vs" or "vs-dark" depending on the user's preference
|
||||
inherit: true,
|
||||
rules: customTokenColors,
|
||||
colors: {} // `colors` is a required attribute
|
||||
});
|
||||
|
||||
// Creates the editor
|
||||
// For all parameters: https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html
|
||||
// For all parameters: https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html
|
||||
editor = monaco.editor.create(document.getElementById('container'), {
|
||||
value: code, // Sets content of the editor
|
||||
language: lang, // Sets language fof the code
|
||||
language: lang, // Sets language of the code
|
||||
readOnly: true, // Sets to readonly
|
||||
theme: theme, // Sets editor theme
|
||||
minimap: {enabled: false}, // Disables minimap
|
||||
lineNumbersMinChars: "3", //Width of the line numbers
|
||||
theme: 'theme', // Sets editor theme
|
||||
minimap: { enabled: false }, // Disables minimap
|
||||
lineNumbersMinChars: '3', // Width of the line numbers
|
||||
contextmenu: contextMenu,
|
||||
scrollbar: {
|
||||
// Deactivate shadows
|
||||
shadows: false,
|
||||
@@ -79,17 +109,16 @@
|
||||
// Render scrollbar automatically
|
||||
vertical: 'auto',
|
||||
horizontal: 'auto',
|
||||
|
||||
},
|
||||
stickyScroll: {enabled: stickyScroll},
|
||||
stickyScroll: { enabled: stickyScroll },
|
||||
fontSize: fontSize,
|
||||
wordWrap: (wrap?"on":"off") // Word wraps
|
||||
wordWrap: (wrap ? 'on' : 'off') // Word wraps
|
||||
});
|
||||
window.onresize = function (){
|
||||
window.onresize = () => {
|
||||
editor.layout();
|
||||
};
|
||||
|
||||
// Add switch wrap button to context menu
|
||||
// Add toggle wrap button to context menu
|
||||
editor.addAction({
|
||||
id: 'text-wrap',
|
||||
|
||||
@@ -101,30 +130,25 @@
|
||||
// A rule to evaluate on top of the precondition in order to dispatch the keybindings.
|
||||
keybindingContext: null,
|
||||
|
||||
contextMenuGroupId: 'cutcopypaste',
|
||||
contextMenuGroupId: 'cutcopypaste',
|
||||
|
||||
contextMenuOrder: 100,
|
||||
contextMenuOrder: 100,
|
||||
|
||||
// Method that will be executed when the action is triggered.
|
||||
// @param editor The editor instance is passed in as a convenience
|
||||
run: function (ed) {
|
||||
if(wrap){
|
||||
editor.updateOptions({ wordWrap: "off" })
|
||||
}else{
|
||||
editor.updateOptions({ wordWrap: "on" })
|
||||
}
|
||||
wrap = !wrap;
|
||||
runToggleTextWrapCommand();
|
||||
}
|
||||
});
|
||||
|
||||
onContextMenu();
|
||||
});
|
||||
|
||||
function onContextMenu(){
|
||||
function onContextMenu() {
|
||||
// Hide context menu items
|
||||
// Code modified from https://stackoverflow.com/questions/48745208/disable-cut-and-copy-in-context-menu-in-monaco-editor/65413517#65413517
|
||||
let menus = require('vs/platform/actions/common/actions').MenuRegistry._menuItems
|
||||
let contextMenuEntry = [...menus].find(entry => entry[0].id == "EditorContext")
|
||||
let contextMenuEntry = [...menus].find(entry => entry[0].id == 'EditorContext')
|
||||
let contextMenuLinks = contextMenuEntry[1]
|
||||
|
||||
let removableIds = ['editor.action.clipboardCutAction', 'editor.action.formatDocument', 'editor.action.formatSelection', 'editor.action.quickCommand', 'editor.action.quickOutline', 'editor.action.refactor', 'editor.action.sourceAction', 'editor.action.rename', undefined, 'editor.action.revealDefinition', 'editor.action.revealDeclaration', 'editor.action.goToTypeDefinition', 'editor.action.goToImplementation', 'editor.action.goToReferences', 'editor.action.changeAll']
|
||||
@@ -142,4 +166,4 @@
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
<None Update="Assets\Monaco\monacoSpecialLanguages.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Monaco\customTokenColors.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="Assets\Monaco\monacoSRC\**">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -172,4 +172,44 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredQoiThumbnailsEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteOnlineAIModelsValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOnlineAIModelsValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbClipboardSharingEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbClipboardSharingEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbFileTransferEnabledValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbFileTransferEnabledValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbUseOriginalUserInterfaceValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbUseOriginalUserInterfaceValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbDisallowBlockingScreensaverValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbDisallowBlockingScreensaverValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbSameSubnetOnlyValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbSameSubnetOnlyValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbValidateRemoteIpValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbValidateRemoteIpValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredMwbDisableUserDefinedIpMappingRulesValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMwbDisableUserDefinedIpMappingRulesValue());
|
||||
}
|
||||
winrt::hstring GPOWrapper::GetConfiguredMwbPolicyDefinedIpMappingRules()
|
||||
{
|
||||
// Assuming powertoys_gpo::getConfiguredMwbPolicyDefinedIpMappingRules() returns a std::wstring
|
||||
std::wstring rules = powertoys_gpo::getConfiguredMwbPolicyDefinedIpMappingRules();
|
||||
|
||||
// Convert std::wstring to winrt::hstring
|
||||
return to_hstring(rules.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,15 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredQoiPreviewEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredQoiThumbnailsEnabledValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteOnlineAIModelsValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbClipboardSharingEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbFileTransferEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbUseOriginalUserInterfaceValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbDisallowBlockingScreensaverValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbSameSubnetOnlyValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbValidateRemoteIpValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbDisableUserDefinedIpMappingRulesValue();
|
||||
static winrt::hstring GPOWrapper::GetConfiguredMwbPolicyDefinedIpMappingRules();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,15 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredQoiPreviewEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredQoiThumbnailsEnabledValue();
|
||||
static GpoRuleConfigured GetAllowedAdvancedPasteOnlineAIModelsValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbClipboardSharingEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbFileTransferEnabledValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbUseOriginalUserInterfaceValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbDisallowBlockingScreensaverValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbSameSubnetOnlyValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbValidateRemoteIpValue();
|
||||
static GpoRuleConfigured GetConfiguredMwbDisableUserDefinedIpMappingRulesValue();
|
||||
static String GetConfiguredMwbPolicyDefinedIpMappingRules();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,15 @@ namespace UnitTestsVersionHelper
|
||||
Assert::AreEqual(25ull, sut->minor);
|
||||
Assert::AreEqual(1ull, sut->revision);
|
||||
}
|
||||
TEST_METHOD (stringConstructorShouldProperlyInitializationVersionNumbersWithUppercaseV)
|
||||
{
|
||||
auto sut = VersionHelper::fromString(L"V2.25.1");
|
||||
Assert::IsTrue(sut.has_value());
|
||||
|
||||
Assert::AreEqual(2ull, sut->major);
|
||||
Assert::AreEqual(25ull, sut->minor);
|
||||
Assert::AreEqual(1ull, sut->revision);
|
||||
}
|
||||
TEST_METHOD (emptyStringNotAccepted)
|
||||
{
|
||||
auto sut = VersionHelper::fromString("");
|
||||
|
||||
@@ -47,9 +47,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="MSTest.TestAdapter" />
|
||||
<PackageReference Include="MSTest.TestFramework" />
|
||||
<PackageReference Include="MSTest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <Windows.h>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace powertoys_gpo {
|
||||
enum gpo_rule_configured_t {
|
||||
@@ -70,12 +71,26 @@ namespace powertoys_gpo {
|
||||
// The registry value names for other PowerToys policies.
|
||||
const std::wstring POLICY_ALLOW_EXPERIMENTATION = L"AllowExperimentation";
|
||||
const std::wstring POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER_ALL_PLUGINS = L"PowerLauncherAllPluginsEnabledState";
|
||||
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_ONLINE_AI_MODELS = L"AllowPowerToysAdvancedPasteOnlineAIModels";
|
||||
const std::wstring POLICY_MWB_CLIPBOARD_SHARING_ENABLED = L"MwbClipboardSharingEnabled";
|
||||
const std::wstring POLICY_MWB_FILE_TRANSFER_ENABLED = L"MwbFileTransferEnabled";
|
||||
const std::wstring POLICY_MWB_USE_ORIGINAL_USER_INTERFACE = L"MwbUseOriginalUserInterface";
|
||||
const std::wstring POLICY_MWB_DISALLOW_BLOCKING_SCREENSAVER = L"MwbDisallowBlockingScreensaver";
|
||||
const std::wstring POLICY_MWB_SAME_SUBNET_ONLY = L"MwbSameSubnetOnly";
|
||||
const std::wstring POLICY_MWB_VALIDATE_REMOTE_IP = L"MwbValidateRemoteIp";
|
||||
const std::wstring POLICY_MWB_DISABLE_USER_DEFINED_IP_MAPPING_RULES = L"MwbDisableUserDefinedIpMappingRules";
|
||||
const std::wstring POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES = L"MwbPolicyDefinedIpMappingRules";
|
||||
|
||||
|
||||
inline std::optional<std::wstring> readRegistryStringValue(HKEY hRootKey, const std::wstring& subKey, const std::wstring& value_name)
|
||||
inline std::optional<std::wstring> readRegistryStringValue(HKEY hRootKey, const std::wstring& subKey, const std::wstring& value_name, const bool is_multi_line_text = false)
|
||||
{
|
||||
// Set value type
|
||||
DWORD reg_value_type = REG_SZ;
|
||||
DWORD reg_flags = RRF_RT_REG_SZ;
|
||||
if (is_multi_line_text)
|
||||
{
|
||||
reg_value_type = REG_MULTI_SZ;
|
||||
reg_flags = RRF_RT_REG_MULTI_SZ;
|
||||
}
|
||||
|
||||
DWORD string_buffer_capacity;
|
||||
// Request required buffer capacity / string length
|
||||
@@ -97,8 +112,26 @@ namespace powertoys_gpo {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Convert buffer to std::wstring, delete buffer and return REG_SZ value
|
||||
std::wstring string_value = temp_buffer;
|
||||
// Convert buffer to std::wstring
|
||||
std::wstring string_value = L"";
|
||||
if (reg_value_type == REG_MULTI_SZ)
|
||||
{
|
||||
// If it is REG_MULTI_SZ handle this way
|
||||
wchar_t* currentString = temp_buffer;
|
||||
while (*currentString != L'\0')
|
||||
{
|
||||
// If first entry then assign the string, else add to the string
|
||||
string_value = (string_value == L"") ? currentString : (string_value + L"\r\n" + currentString);
|
||||
currentString += wcslen(currentString) + 1; // Move to the next string
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it is REG_SZ handle this way
|
||||
string_value = temp_buffer;
|
||||
}
|
||||
|
||||
// delete buffer, return string value
|
||||
delete temp_buffer;
|
||||
return string_value;
|
||||
}
|
||||
@@ -470,4 +503,64 @@ namespace powertoys_gpo {
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_QOI_THUMBNAILS);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getAllowedAdvancedPasteOnlineAIModelsValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_ALLOW_ADVANCED_PASTE_ONLINE_AI_MODELS);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbClipboardSharingEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_CLIPBOARD_SHARING_ENABLED);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbFileTransferEnabledValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_FILE_TRANSFER_ENABLED);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbUseOriginalUserInterfaceValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_USE_ORIGINAL_USER_INTERFACE);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbDisallowBlockingScreensaverValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_DISALLOW_BLOCKING_SCREENSAVER);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbSameSubnetOnlyValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_SAME_SUBNET_ONLY);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbValidateRemoteIpValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_VALIDATE_REMOTE_IP);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredMwbDisableUserDefinedIpMappingRulesValue()
|
||||
{
|
||||
return getUtilityEnabledValue(POLICY_MWB_DISABLE_USER_DEFINED_IP_MAPPING_RULES);
|
||||
}
|
||||
|
||||
inline std::wstring getConfiguredMwbPolicyDefinedIpMappingRules()
|
||||
{
|
||||
// Important: HKLM has priority over HKCU
|
||||
auto mapping_rules = readRegistryStringValue(HKEY_LOCAL_MACHINE, POLICIES_PATH, POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES, true);
|
||||
if (!mapping_rules.has_value())
|
||||
{
|
||||
mapping_rules = readRegistryStringValue(HKEY_CURRENT_USER, POLICIES_PATH, POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES, true);
|
||||
}
|
||||
|
||||
// return value
|
||||
if (mapping_rules.has_value())
|
||||
{
|
||||
return mapping_rules.value();
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::wstring ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ struct Constants;
|
||||
template<>
|
||||
struct Constants<char>
|
||||
{
|
||||
static inline const char* V = "v";
|
||||
static inline const char* LOWER_V = "v";
|
||||
static inline const char* UPPER_V = "V";
|
||||
static inline const char* DOT = ".";
|
||||
static inline const char SPACE = ' ';
|
||||
};
|
||||
@@ -26,7 +27,8 @@ struct Constants<char>
|
||||
template<>
|
||||
struct Constants<wchar_t>
|
||||
{
|
||||
static inline const wchar_t* V = L"v";
|
||||
static inline const wchar_t* LOWER_V = L"v";
|
||||
static inline const wchar_t* UPPER_V = L"V";
|
||||
static inline const wchar_t* DOT = L".";
|
||||
static inline const wchar_t SPACE = L' ';
|
||||
};
|
||||
@@ -36,7 +38,8 @@ std::optional<VersionHelper> fromString(std::basic_string_view<CharT> str)
|
||||
{
|
||||
try
|
||||
{
|
||||
str = left_trim<CharT>(trim<CharT>(str), Constants<CharT>::V);
|
||||
str = left_trim<CharT>(trim<CharT>(str), Constants<CharT>::LOWER_V);
|
||||
str = left_trim<CharT>(trim<CharT>(str), Constants<CharT>::UPPER_V);
|
||||
std::basic_string<CharT> spacedStr{ str };
|
||||
replace_chars<CharT>(spacedStr, Constants<CharT>::DOT, Constants<CharT>::SPACE);
|
||||
|
||||
|
||||
@@ -27,18 +27,18 @@ enum class version_architecture
|
||||
version_architecture get_current_architecture();
|
||||
const wchar_t* get_architecture_string(const version_architecture);
|
||||
|
||||
inline std::wstring get_product_version()
|
||||
inline std::wstring get_product_version(bool includeV = true)
|
||||
{
|
||||
static std::wstring version = L"v" + std::to_wstring(VERSION_MAJOR) +
|
||||
static std::wstring version = (includeV ? L"v" : L"") + std::to_wstring(VERSION_MAJOR) +
|
||||
L"." + std::to_wstring(VERSION_MINOR) +
|
||||
L"." + std::to_wstring(VERSION_REVISION);
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
inline std::wstring get_std_product_version()
|
||||
inline std::wstring get_std_product_version(bool includeV = true)
|
||||
{
|
||||
static std::wstring version = L"v" + std::to_wstring(VERSION_MAJOR) +
|
||||
static std::wstring version = (includeV ? L"v" : L"") + std::to_wstring(VERSION_MAJOR) +
|
||||
L"." + std::to_wstring(VERSION_MINOR) +
|
||||
L"." + std::to_wstring(VERSION_REVISION) + L".0";
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) Microsoft Corporation.
|
||||
Licensed under the MIT License. -->
|
||||
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.9" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
||||
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.11" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
||||
<policyNamespaces>
|
||||
<target prefix="powertoys" namespace="Microsoft.Policies.PowerToys" />
|
||||
</policyNamespaces>
|
||||
<resources minRequiredRevision="1.9"/><!-- Last changed with PowerToys v0.81.0 -->
|
||||
<resources minRequiredRevision="1.11"/><!-- Last changed with PowerToys v0.83.0 -->
|
||||
<supportedOn>
|
||||
<definitions>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_64_0" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0)"/>
|
||||
@@ -18,6 +18,8 @@
|
||||
<definition name="SUPPORTED_POWERTOYS_0_77_0" displayName="$(string.SUPPORTED_POWERTOYS_0_77_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_78_0" displayName="$(string.SUPPORTED_POWERTOYS_0_78_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_81_0" displayName="$(string.SUPPORTED_POWERTOYS_0_81_0)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_81_1" displayName="$(string.SUPPORTED_POWERTOYS_0_81_1)"/>
|
||||
<definition name="SUPPORTED_POWERTOYS_0_83_0" displayName="$(string.SUPPORTED_POWERTOYS_0_83_0)"/>
|
||||
</definitions>
|
||||
</supportedOn>
|
||||
<categories>
|
||||
@@ -28,10 +30,20 @@
|
||||
<category name="PowerToysRun" displayName="$(string.PowerToysRun)">
|
||||
<parentCategory ref="PowerToys" />
|
||||
</category>
|
||||
<category name="AdvancedPaste" displayName="$(string.AdvancedPaste)">
|
||||
<parentCategory ref="PowerToys" />
|
||||
</category>
|
||||
<category name="MouseWithoutBorders" displayName="$(string.MouseWithoutBorders)">
|
||||
<parentCategory ref="PowerToys" />
|
||||
</category>
|
||||
<category name="GeneralSettings" displayName="$(string.GeneralSettings)">
|
||||
<parentCategory ref="PowerToys" />
|
||||
</category>
|
||||
</categories>
|
||||
<policies>
|
||||
|
||||
<policy name="ConfigureGlobalUtilityEnabledState" class="Both" displayName="$(string.ConfigureGlobalUtilityEnabledState)" explainText="$(string.ConfigureGlobalUtilityEnabledStateDescription)" key="Software\Policies\PowerToys" valueName="ConfigureGlobalUtilityEnabledState">
|
||||
<!--The name (id) of the policy is different to sort it as first policy in edit dialog. The order is sorted alphabetically based on the "name" property.-->
|
||||
<policy name="ConfigureAllUtilityGlobalEnabledState" class="Both" displayName="$(string.ConfigureAllUtilityGlobalEnabledState)" explainText="$(string.ConfigureAllUtilityGlobalEnabledStateDescription)" key="Software\Policies\PowerToys" valueName="ConfigureGlobalUtilityEnabledState">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />
|
||||
<enabledValue>
|
||||
@@ -463,7 +475,7 @@
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="AllowExperimentation" class="Both" displayName="$(string.AllowExperimentation)" explainText="$(string.AllowExperimentationDescription)" key="Software\Policies\PowerToys" valueName="AllowExperimentation">
|
||||
<parentCategory ref="PowerToys" />
|
||||
<parentCategory ref="GeneralSettings" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_68_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
@@ -489,5 +501,93 @@
|
||||
<list id="PowerToysRunIndividualPluginEnabledList" explicitValue="true" />
|
||||
</elements>
|
||||
</policy>
|
||||
<policy name="AllowPowerToysAdvancedPasteOnlineAIModels" class="Both" displayName="$(string.AllowPowerToysAdvancedPasteOnlineAIModels)" explainText="$(string.AllowPowerToysAdvancedPasteOnlineAIModelsDescription)" key="Software\Policies\PowerToys" valueName="AllowPowerToysAdvancedPasteOnlineAIModels">
|
||||
<parentCategory ref="AdvancedPaste" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_81_1" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbClipboardSharingEnabled" class="Both" displayName="$(string.MwbClipboardSharingEnabled)" explainText="$(string.MwbClipboardSharingEnabledDescription)" key="Software\Policies\PowerToys" valueName="MwbClipboardSharingEnabled">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbFileTransferEnabled" class="Both" displayName="$(string.MwbFileTransferEnabled)" explainText="$(string.MwbFileTransferEnabledDescription)" key="Software\Policies\PowerToys" valueName="MwbFileTransferEnabled">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbUseOriginalUserInterface" class="Both" displayName="$(string.MwbUseOriginalUserInterface)" explainText="$(string.MwbUseOriginalUserInterfaceDescription)" key="Software\Policies\PowerToys" valueName="MwbUseOriginalUserInterface">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbDisallowBlockingScreensaver" class="Both" displayName="$(string.MwbDisallowBlockingScreensaver)" explainText="$(string.MwbDisallowBlockingScreensaverDescription)" key="Software\Policies\PowerToys" valueName="MwbDisallowBlockingScreensaver">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbSameSubnetOnly" class="Both" displayName="$(string.MwbSameSubnetOnly)" explainText="$(string.MwbSameSubnetOnlyDescription)" key="Software\Policies\PowerToys" valueName="MwbSameSubnetOnly">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbValidateRemoteIp" class="Both" displayName="$(string.MwbValidateRemoteIp)" explainText="$(string.MwbValidateRemoteIpDescription)" key="Software\Policies\PowerToys" valueName="MwbValidateRemoteIp">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbDisableUserDefinedIpMappingRules" class="Both" displayName="$(string.MwbDisableUserDefinedIpMappingRules)" explainText="$(string.MwbDisableUserDefinedIpMappingRulesDescription)" key="Software\Policies\PowerToys" valueName="MwbDisableUserDefinedIpMappingRules">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="MwbPolicyDefinedIpMappingRules" class="Both" displayName="$(string.MwbPolicyDefinedIpMappingRules)" explainText="$(string.MwbPolicyDefinedIpMappingRulesDescription)" presentation="$(presentation.MwbPolicyDefinedIpMappingRules)" key="Software\Policies\PowerToys">
|
||||
<parentCategory ref="MouseWithoutBorders" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_83_0" />
|
||||
<elements>
|
||||
<!--Max length means here max length per line. We support 65 characters per line. (A string containing the hostname, a space and an IPv6 Address is max. 55 characters long.)-->
|
||||
<multiText id="MwbPolicyDefinedIpMappingsList" valueName="MwbPolicyDefinedIpMappingRules" maxLength="65" required="true"/>
|
||||
</elements>
|
||||
</policy>
|
||||
</policies>
|
||||
</policyDefinitions>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) Microsoft Corporation.
|
||||
Licensed under the MIT License. -->
|
||||
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.9" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
||||
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.11" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
||||
<displayName>PowerToys</displayName>
|
||||
<description>PowerToys</description>
|
||||
<resources>
|
||||
@@ -9,6 +9,9 @@
|
||||
<string id="PowerToys">Microsoft PowerToys</string>
|
||||
<string id="InstallerUpdates">Installer and Updates</string>
|
||||
<string id="PowerToysRun">PowerToys Run</string>
|
||||
<string id="AdvancedPaste">Advanced Paste</string>
|
||||
<string id="MouseWithoutBorders">Mouse Without Borders</string>
|
||||
<string id="GeneralSettings">General settings</string>
|
||||
|
||||
<string id="SUPPORTED_POWERTOYS_0_64_0">PowerToys version 0.64.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_68_0">PowerToys version 0.68.0 or later</string>
|
||||
@@ -20,8 +23,10 @@
|
||||
<string id="SUPPORTED_POWERTOYS_0_77_0">PowerToys version 0.77.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_78_0">PowerToys version 0.78.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_81_0">PowerToys version 0.81.0 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_81_1">PowerToys version 0.81.1 or later</string>
|
||||
<string id="SUPPORTED_POWERTOYS_0_83_0">PowerToys version 0.83.0 or later</string>
|
||||
|
||||
<string id="ConfigureGlobalUtilityEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
|
||||
<string id="ConfigureAllUtilityGlobalEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
|
||||
|
||||
If you enable this setting, all utilities will be always enabled and the user won't be able to disable it.
|
||||
|
||||
@@ -119,7 +124,72 @@ You can set the enabled state for all plugins not configured by this policy usin
|
||||
|
||||
Note: Changes require a restart of PowerToys Run.
|
||||
</string>
|
||||
<string id="ConfigureGlobalUtilityEnabledState">Configure global utility enabled state</string>
|
||||
<string id="AllowPowerToysAdvancedPasteOnlineAIModelsDescription">This policy allows you to disable Advanced Paste online AI models.
|
||||
|
||||
If you enable or don't configure this policy, the user takes control over the enabled state of the Enable paste with AI Advanced Paste setting.
|
||||
|
||||
If you disable this policy, the user won't be able to enable Enable paste with AI Advanced Paste setting and use Advanced Paste AI prompt nor set up the Open AI key in PowerToys Settings.
|
||||
</string>
|
||||
<string id="MwbClipboardSharingEnabledDescription">This policy configures if the user can share the clipboard between machines.
|
||||
|
||||
If you enable or don't configure this policy, the user takes control over the clipboard sharing setting.
|
||||
|
||||
If you disable this policy, the user won't be able to enable the clipboard sharing setting.
|
||||
</string>
|
||||
<string id="MwbFileTransferEnabledDescription">This policy configures if the user can transfer files between machines.
|
||||
|
||||
If you enable or don't configure this policy, the user takes control over the file sharing setting.
|
||||
|
||||
If you disable this policy, the user won't be able to enable the file sharing Settings.
|
||||
|
||||
Note: The file sharing feature depends on the clipboard sharing feature. Disabling clipboard sharing automatically disables file sharing too.
|
||||
</string>
|
||||
<string id="MwbUseOriginalUserInterfaceDescription">This policy configures if the user can use the old Mouse Without Borders user interface.
|
||||
|
||||
If you enable or don't configure this policy, the user takes control over the setting and can enable or disable the old user interface.
|
||||
|
||||
If you disable this policy, the user won't be able to enable the old user interface.
|
||||
</string>
|
||||
<string id="MwbDisallowBlockingScreensaverDescription">This policy configures if the user is allowed to disable the screensaver on the remote machines.
|
||||
|
||||
If you enable this policy, the user won't be able to enable the "block screensaver" screensaver setting and the screensaver is not blocked.
|
||||
|
||||
If you disable or don't configure this policy, the user takes control over the setting and can block the screensaver.
|
||||
</string>
|
||||
|
||||
<string id="MwbSameSubnetOnlyDescription">This policy configures if connections are only allowed in the same subnet.
|
||||
|
||||
If you enable this policy, the setting is enabled and only connections in the same subnet are allowed.
|
||||
|
||||
If you disable this policy, the setting is disabled and all connections are allowed.
|
||||
|
||||
If you don't configure this policy, the user takes control over the setting and can enable or disable it.
|
||||
</string>
|
||||
<string id="MwbValidateRemoteIpDescription">This policy configures if reverse DNS lookup is used to validate the remote machine IP Address.
|
||||
|
||||
If you enable this policy, the setting is enabled and the IP Address is validated.
|
||||
|
||||
If you disable this policy, the setting is disabled and the IP Address is not validated.
|
||||
|
||||
If you don't configure this policy, the user takes control over the setting and can enable or disable it.
|
||||
</string>
|
||||
<string id="MwbDisableUserDefinedIpMappingRulesDescription">This policy configures if the user can define IP Address mapping rules.
|
||||
|
||||
If you enable this policy, the setting is disabled and the user can't define rules or use existing ones.
|
||||
|
||||
If you disable or don't configure this policy, the user takes control over the setting.
|
||||
|
||||
Note: Enabling this policy does not prevent policy defined mapping rules from working.
|
||||
</string>
|
||||
<string id="MwbPolicyDefinedIpMappingRulesDescription">This policy allows you to define IP Address mapping rules.
|
||||
|
||||
If you enable this policy, you can define IP Address mapping rules that the user can't change or disable.
|
||||
Please enter one mapping per line in the format: "hostname IP"
|
||||
|
||||
If you disable or don't configure this policy, no predefined rules are applied.
|
||||
</string>
|
||||
|
||||
<string id="ConfigureAllUtilityGlobalEnabledState">Configure global utility enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityAdvancedPaste">Advanced Paste: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityAlwaysOnTop">Always On Top: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityAwake">Awake: Configure enabled state</string>
|
||||
@@ -165,12 +235,24 @@ Note: Changes require a restart of PowerToys Run.
|
||||
<string id="PowerToysRunIndividualPluginEnabledState">Configure enabled state for individual plugins</string>
|
||||
<string id="ConfigureEnabledUtilityFileExplorerQOIPreview">QOI file preview: Configure enabled state</string>
|
||||
<string id="ConfigureEnabledUtilityFileExplorerQOIThumbnails">QOI file thumbnail: Configure enabled state</string>
|
||||
<string id="AllowPowerToysAdvancedPasteOnlineAIModels">Allow using online AI models</string>
|
||||
<string id="MwbClipboardSharingEnabled">Clipboard sharing enabled</string>
|
||||
<string id="MwbFileTransferEnabled">File transfer enabled</string>
|
||||
<string id="MwbUseOriginalUserInterface">Original user interface is available</string>
|
||||
<string id="MwbDisallowBlockingScreensaver">Disallow blocking screensaver on other machines</string>
|
||||
<string id="MwbSameSubnetOnly">Connect only in same subnet</string>
|
||||
<string id="MwbValidateRemoteIp">Validate remote machine IP Address</string>
|
||||
<string id="MwbDisableUserDefinedIpMappingRules">Disable user defined IP Address mapping rules</string>
|
||||
<string id="MwbPolicyDefinedIpMappingRules">Predefined IP Address mapping rules</string>
|
||||
</stringTable>
|
||||
|
||||
<presentationTable>
|
||||
<presentation id="PowerToysRunIndividualPluginEnabledState">
|
||||
<listBox refId="PowerToysRunIndividualPluginEnabledList">List of managed plugins:</listBox>
|
||||
</presentation>
|
||||
<presentation id="MwbPolicyDefinedIpMappingRules">
|
||||
<multiTextBox refId="MwbPolicyDefinedIpMappingsList">List of IP Address mappings:</multiTextBox>
|
||||
</presentation>
|
||||
</presentationTable>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<PackageReference Include="Azure.AI.OpenAI" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
@@ -74,7 +74,8 @@
|
||||
<!-- HACK: To align Microsoft.Bcl.AsyncInterfaces.dll version with PowerToys.Settings.csproj. -->
|
||||
<PackageReference Include="StreamJsonRpc" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
|
||||
<!-- HACK: To make sure the version pulled in by Microsoft.Extensions.Hosting is current. -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.ViewModels;
|
||||
using ManagedCommon;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Graphics;
|
||||
using WinUIEx;
|
||||
using static AdvancedPaste.Helpers.NativeMethods;
|
||||
@@ -47,6 +46,7 @@ namespace AdvancedPaste
|
||||
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
|
||||
{
|
||||
services.AddSingleton<OptionsViewModel>();
|
||||
services.AddSingleton<IUserSettings, UserSettings>();
|
||||
}).Build();
|
||||
|
||||
viewModel = GetService<OptionsViewModel>();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
@@ -20,14 +19,13 @@ namespace AdvancedPaste.Controls
|
||||
public sealed partial class PromptBox : Microsoft.UI.Xaml.Controls.UserControl
|
||||
{
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
private UserSettings _userSettings;
|
||||
private readonly IUserSettings _userSettings;
|
||||
|
||||
public static readonly DependencyProperty PromptProperty = DependencyProperty.Register(
|
||||
nameof(Prompt),
|
||||
typeof(string),
|
||||
typeof(PromptBox),
|
||||
new PropertyMetadata(defaultValue: string.Empty));
|
||||
nameof(Prompt),
|
||||
typeof(string),
|
||||
typeof(PromptBox),
|
||||
new PropertyMetadata(defaultValue: string.Empty));
|
||||
|
||||
public OptionsViewModel ViewModel { get; private set; }
|
||||
|
||||
@@ -38,10 +36,10 @@ namespace AdvancedPaste.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register(
|
||||
nameof(PlaceholderText),
|
||||
typeof(string),
|
||||
typeof(PromptBox),
|
||||
new PropertyMetadata(defaultValue: string.Empty));
|
||||
nameof(PlaceholderText),
|
||||
typeof(string),
|
||||
typeof(PromptBox),
|
||||
new PropertyMetadata(defaultValue: string.Empty));
|
||||
|
||||
public string PlaceholderText
|
||||
{
|
||||
@@ -50,10 +48,10 @@ namespace AdvancedPaste.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(
|
||||
nameof(Footer),
|
||||
typeof(object),
|
||||
typeof(PromptBox),
|
||||
new PropertyMetadata(defaultValue: null));
|
||||
nameof(Footer),
|
||||
typeof(object),
|
||||
typeof(PromptBox),
|
||||
new PropertyMetadata(defaultValue: null));
|
||||
|
||||
public object Footer
|
||||
{
|
||||
@@ -65,7 +63,7 @@ namespace AdvancedPaste.Controls
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
_userSettings = new UserSettings();
|
||||
_userSettings = App.GetService<IUserSettings>();
|
||||
|
||||
ViewModel = App.GetService<OptionsViewModel>();
|
||||
}
|
||||
@@ -80,8 +78,6 @@ namespace AdvancedPaste.Controls
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomFormatEvent());
|
||||
|
||||
VisualStateManager.GoToState(this, "LoadingState", true);
|
||||
string inputInstructions = InputTxtBox.Text;
|
||||
ViewModel.SaveQuery(inputInstructions);
|
||||
|
||||
@@ -3,21 +3,20 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Settings;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using WinUIEx;
|
||||
using WinUIEx.Messaging;
|
||||
using static AdvancedPaste.Helpers.NativeMethods;
|
||||
|
||||
namespace AdvancedPaste
|
||||
{
|
||||
public sealed partial class MainWindow : WindowEx, IDisposable
|
||||
{
|
||||
private WindowMessageMonitor _msgMonitor;
|
||||
private readonly WindowMessageMonitor _msgMonitor;
|
||||
private readonly IUserSettings _userSettings;
|
||||
|
||||
private bool _disposedValue;
|
||||
|
||||
@@ -25,6 +24,8 @@ namespace AdvancedPaste
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
_userSettings = App.GetService<IUserSettings>();
|
||||
|
||||
AppWindow.SetIcon("Assets/AdvancedPaste/AdvancedPaste.ico");
|
||||
this.ExtendsContentIntoTitleBar = true;
|
||||
this.SetTitleBar(titleBar);
|
||||
@@ -32,6 +33,8 @@ namespace AdvancedPaste
|
||||
var loader = ResourceLoaderInstance.ResourceLoader;
|
||||
Title = loader.GetString("WindowTitle");
|
||||
|
||||
Activated += OnActivated;
|
||||
|
||||
_msgMonitor = new WindowMessageMonitor(this);
|
||||
_msgMonitor.WindowMessageReceived += (_, e) =>
|
||||
{
|
||||
@@ -47,6 +50,14 @@ namespace AdvancedPaste
|
||||
WindowHelpers.BringToForeground(this.GetWindowHandle());
|
||||
}
|
||||
|
||||
private void OnActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
if (_userSettings.CloseAfterLosingFocus && args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
@@ -66,11 +77,15 @@ namespace AdvancedPaste
|
||||
|
||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||
{
|
||||
Windows.Win32.PInvoke.ShowWindow((Windows.Win32.Foundation.HWND)this.GetWindowHandle(), Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
|
||||
|
||||
Hide();
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void Hide()
|
||||
{
|
||||
Windows.Win32.PInvoke.ShowWindow(new Windows.Win32.Foundation.HWND(this.GetWindowHandle()), Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
|
||||
}
|
||||
|
||||
public void SetFocus()
|
||||
{
|
||||
MainPage.CustomFormatTextBox.InputTxtBox.Focus(FocusState.Programmatic);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
@@ -86,6 +87,11 @@ namespace AdvancedPaste.Pages
|
||||
|
||||
_dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
// Clear to avoid leaks due to Garbage Collection not clearing the bitmap from memory. Fix for https://github.com/microsoft/PowerToys/issues/33423
|
||||
clipboardHistory.Where(x => x.Image is not null)
|
||||
.ToList()
|
||||
.ForEach(x => x.Image.ClearValue(BitmapImage.UriSourceProperty));
|
||||
|
||||
clipboardHistory.Clear();
|
||||
|
||||
foreach (var item in items)
|
||||
@@ -225,6 +231,7 @@ namespace AdvancedPaste.Pages
|
||||
var item = e.ClickedItem as ClipboardItem;
|
||||
if (item is not null)
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteClipboardItemClicked());
|
||||
if (!string.IsNullOrEmpty(item.Content))
|
||||
{
|
||||
ClipboardHelper.SetClipboardTextContent(item.Content);
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace AdvancedPaste.Helpers
|
||||
|
||||
private string _openAIKey;
|
||||
|
||||
private string _modelName = "gpt-3.5-turbo-instruct";
|
||||
|
||||
public bool IsAIEnabled => !string.IsNullOrEmpty(this._openAIKey);
|
||||
|
||||
public AICompletionsHelper()
|
||||
@@ -69,14 +71,14 @@ namespace AdvancedPaste.Helpers
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public string GetAICompletion(string systemInstructions, string userMessage)
|
||||
private Response<Completions> GetAICompletion(string systemInstructions, string userMessage)
|
||||
{
|
||||
OpenAIClient azureAIClient = new OpenAIClient(_openAIKey);
|
||||
|
||||
var response = azureAIClient.GetCompletions(
|
||||
new CompletionsOptions()
|
||||
{
|
||||
DeploymentName = "gpt-3.5-turbo-instruct",
|
||||
DeploymentName = _modelName,
|
||||
Prompts =
|
||||
{
|
||||
systemInstructions + "\n\n" + userMessage,
|
||||
@@ -90,7 +92,7 @@ namespace AdvancedPaste.Helpers
|
||||
Console.WriteLine("Cut off due to length constraints");
|
||||
}
|
||||
|
||||
return response.Value.Choices[0].Text;
|
||||
return response;
|
||||
}
|
||||
|
||||
public AICompletionsResponse AIFormatString(string inputInstructions, string inputString)
|
||||
@@ -109,10 +111,16 @@ Output:
|
||||
";
|
||||
|
||||
string aiResponse = null;
|
||||
Response<Completions> rawAIResponse = null;
|
||||
int apiRequestStatus = (int)HttpStatusCode.OK;
|
||||
try
|
||||
{
|
||||
aiResponse = this.GetAICompletion(systemInstructions, userMessage);
|
||||
rawAIResponse = this.GetAICompletion(systemInstructions, userMessage);
|
||||
aiResponse = rawAIResponse.Value.Choices[0].Text;
|
||||
|
||||
int promptTokens = rawAIResponse.Value.Usage.PromptTokens;
|
||||
int completionTokens = rawAIResponse.Value.Usage.CompletionTokens;
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomFormatEvent(promptTokens, completionTokens, _modelName));
|
||||
}
|
||||
catch (Azure.RequestFailedException error)
|
||||
{
|
||||
|
||||
@@ -9,5 +9,7 @@ namespace AdvancedPaste.Settings
|
||||
public bool ShowCustomPreview { get; }
|
||||
|
||||
public bool SendPasteKeyCombination { get; }
|
||||
|
||||
public bool CloseAfterLosingFocus { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using ManagedCommon;
|
||||
@@ -14,6 +16,22 @@ namespace AdvancedPaste.Helpers
|
||||
{
|
||||
internal static class JsonHelper
|
||||
{
|
||||
// Ini parts regex
|
||||
private static readonly Regex IniSectionNameRegex = new Regex(@"^\[(.+)\]");
|
||||
private static readonly Regex IniValueLineRegex = new Regex(@"(.+?)\s*=\s*(.*)");
|
||||
|
||||
// List of supported CSV delimiters and Regex to detect separator property
|
||||
private static readonly char[] CsvDelimArry = [',', ';', '\t'];
|
||||
private static readonly Regex CsvSepIdentifierRegex = new Regex(@"^sep=(.)$", RegexOptions.IgnoreCase);
|
||||
|
||||
// CSV: Split on every occurrence of the delimiter except if it is enclosed by " and ignore two " as escaped "
|
||||
private static readonly string CsvDelimSepRegexStr = @"(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
|
||||
|
||||
// CSV: Regex to remove/replace quotation marks
|
||||
private static readonly Regex CsvRemoveSingleQuotationMarksRegex = new Regex(@"^""(?!"")|(?<!"")""$|^""""$");
|
||||
private static readonly Regex CsvRemoveStartAndEndQuotationMarksRegex = new Regex(@"^""(?=(""{2})+)|(?<=(""{2})+)""$");
|
||||
private static readonly Regex CsvReplaceDoubleQuotationMarksRegex = new Regex(@"""{2}");
|
||||
|
||||
internal static string ToJsonFromXmlOrCsv(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
@@ -39,6 +57,7 @@ namespace AdvancedPaste.Helpers
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(text);
|
||||
Logger.LogDebug("Converted from XML.");
|
||||
jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.Indented);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -46,18 +65,109 @@ namespace AdvancedPaste.Helpers
|
||||
Logger.LogError("Failed parsing input as xml", ex);
|
||||
}
|
||||
|
||||
// Try convert Ini
|
||||
// (Must come before CSV that ini is not false detected as CSV.)
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonText))
|
||||
{
|
||||
var ini = new Dictionary<string, Dictionary<string, string>>();
|
||||
var lastSectionName = string.Empty;
|
||||
|
||||
string[] lines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Skipp comment lines.
|
||||
// (Comments are lines that starts with a semicolon.
|
||||
// This also skips commented key-value-pairs.)
|
||||
lines = lines.Where(l => !l.StartsWith(';')).ToArray();
|
||||
|
||||
// Validate content as ini
|
||||
// (First line is a section name and second line is a section name or a key-value-pair.
|
||||
// For the second line we check both, in case the first ini section is empty.)
|
||||
if (lines.Length >= 2 && IniSectionNameRegex.IsMatch(lines[0]) &&
|
||||
(IniSectionNameRegex.IsMatch(lines[1]) || IniValueLineRegex.IsMatch(lines[1])))
|
||||
{
|
||||
// Parse and convert Ini
|
||||
foreach (string line in lines)
|
||||
{
|
||||
Match lineSectionNameCheck = IniSectionNameRegex.Match(line);
|
||||
Match lineKeyValuePairCheck = IniValueLineRegex.Match(line);
|
||||
|
||||
if (lineSectionNameCheck.Success)
|
||||
{
|
||||
// Section name (Group 1)
|
||||
lastSectionName = lineSectionNameCheck.Groups[1].Value.Trim();
|
||||
if (string.IsNullOrWhiteSpace(lastSectionName))
|
||||
{
|
||||
throw new FormatException("Invalid ini file format: Empty section name.");
|
||||
}
|
||||
|
||||
ini.Add(lastSectionName, new Dictionary<string, string>());
|
||||
}
|
||||
else if (!lineKeyValuePairCheck.Success)
|
||||
{
|
||||
// Fail if it is not a key-value-pair (and was not detected as section name before).
|
||||
throw new FormatException("Invalid ini file format: Invalid line.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Key-value-pair (Group 1=Key; Group 2=Value)
|
||||
string iniKeyName = lineKeyValuePairCheck.Groups[1].Value.Trim();
|
||||
if (string.IsNullOrWhiteSpace(iniKeyName))
|
||||
{
|
||||
throw new FormatException("Invalid ini file format: Empty value name (key).");
|
||||
}
|
||||
|
||||
string iniValueData = lineKeyValuePairCheck.Groups[2].Value;
|
||||
ini[lastSectionName].Add(iniKeyName, iniValueData);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
Logger.LogDebug("Converted from Ini.");
|
||||
jsonText = JsonConvert.SerializeObject(ini, Newtonsoft.Json.Formatting.Indented);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed parsing input as ini", ex);
|
||||
}
|
||||
|
||||
// Try convert CSV
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonText))
|
||||
{
|
||||
var csv = new List<string[]>();
|
||||
var csv = new List<IEnumerable<string>>();
|
||||
|
||||
foreach (var line in text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
|
||||
string[] lines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Detect the csv delimiter and the count of occurrence based on the first two csv lines.
|
||||
GetCsvDelimiter(lines, out char delim, out int delimCount);
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
csv.Add(line.Split(","));
|
||||
// If line is separator property line, then skip it
|
||||
if (CsvSepIdentifierRegex.IsMatch(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// A CSV line is valid, if the delimiter occurs equal times in every line compared to the first data line
|
||||
// and if every line contains no or an even count of quotation marks.
|
||||
if (Regex.Count(line, delim + CsvDelimSepRegexStr) == delimCount && int.IsEvenInteger(line.Count(x => x == '"')))
|
||||
{
|
||||
string[] dataCells = Regex.Split(line, delim + CsvDelimSepRegexStr, RegexOptions.IgnoreCase);
|
||||
csv.Add(dataCells.Select(x => ReplaceQuotationMarksInCsvData(x)));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException("Invalid CSV format: Number of delimiters wrong in the current line.");
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogDebug("Convert from csv.");
|
||||
jsonText = JsonConvert.SerializeObject(csv, Newtonsoft.Json.Formatting.Indented);
|
||||
}
|
||||
}
|
||||
@@ -66,7 +176,100 @@ namespace AdvancedPaste.Helpers
|
||||
Logger.LogError("Failed parsing input as csv", ex);
|
||||
}
|
||||
|
||||
// Try convert Plain Text
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonText))
|
||||
{
|
||||
var plainText = new List<string>();
|
||||
|
||||
foreach (var line in text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
plainText.Add(line);
|
||||
}
|
||||
|
||||
Logger.LogDebug("Convert from plain text.");
|
||||
jsonText = JsonConvert.SerializeObject(plainText, Newtonsoft.Json.Formatting.Indented);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed parsing input as plain text", ex);
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(jsonText) ? text : jsonText;
|
||||
}
|
||||
|
||||
private static void GetCsvDelimiter(in string[] csvLines, out char delimiter, out int delimiterCount)
|
||||
{
|
||||
delimiter = '\0'; // Unicode "null" character.
|
||||
delimiterCount = 0;
|
||||
|
||||
if (csvLines.Length > 1)
|
||||
{
|
||||
// Try to select the delimiter based on the separator property.
|
||||
Match matchChar = CsvSepIdentifierRegex.Match(csvLines[0]);
|
||||
if (matchChar.Success)
|
||||
{
|
||||
// We can do matchChar[0] as the match only returns one character.
|
||||
// We get the count from the second line, as the first one only contains the character definition and not a CSV data line.
|
||||
char delimChar = matchChar.Groups[1].Value.Trim()[0];
|
||||
delimiter = delimChar;
|
||||
delimiterCount = Regex.Count(csvLines[1], delimChar + CsvDelimSepRegexStr, RegexOptions.IgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
if (csvLines.Length > 0 && delimiterCount == 0)
|
||||
{
|
||||
// Try to select the correct delimiter based on the first two CSV lines from a list of predefined delimiters.
|
||||
foreach (char c in CsvDelimArry)
|
||||
{
|
||||
int cntFirstLine = Regex.Count(csvLines[0], c + CsvDelimSepRegexStr, RegexOptions.IgnoreCase);
|
||||
int cntNextLine = 0; // Default to 0 that the 'second line' check is always true.
|
||||
|
||||
// Additional count if we have more than one line
|
||||
if (csvLines.Length >= 2)
|
||||
{
|
||||
cntNextLine = Regex.Count(csvLines[1], c + CsvDelimSepRegexStr, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
// The delimiter is found if the count is bigger as from the last selected delimiter
|
||||
// and if the next csv line does not exist or has the same number of occurrences of the delimiter.
|
||||
// (We check the next line to prevent false positives.)
|
||||
if (cntFirstLine > delimiterCount && (cntNextLine == 0 || cntNextLine == cntFirstLine))
|
||||
{
|
||||
delimiter = c;
|
||||
delimiterCount = cntFirstLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the delimiter count is 0, we can't detect it and it is no valid CSV.
|
||||
if (delimiterCount == 0)
|
||||
{
|
||||
throw new FormatException("Invalid CSV format: Failed to detect the delimiter.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove and replace quotation marks used as control sequences. (Enclosing quotation marks and escaping quotation marks.)
|
||||
/// </summary>
|
||||
/// <param name="str">CSV cell data to manipulate.</param>
|
||||
/// <returns>Manipulated string.</returns>
|
||||
private static string ReplaceQuotationMarksInCsvData(string str)
|
||||
{
|
||||
// Remove first and last single quotation mark (enclosing quotation marks) and remove quotation marks of an empty data set ("").
|
||||
str = CsvRemoveSingleQuotationMarksRegex.Replace(str, string.Empty);
|
||||
|
||||
// Remove first quotation mark if followed by pairs of quotation marks
|
||||
// and remove last quotation mark if precede by pairs of quotation marks.
|
||||
// (Removes enclosing quotation marks around the cell data for data like /"""abc"""/.)
|
||||
str = CsvRemoveStartAndEndQuotationMarksRegex.Replace(str, string.Empty);
|
||||
|
||||
// Replace pairs of two quotation marks with a single quotation mark. (Escaped quotation marks.)
|
||||
str = CsvReplaceDoubleQuotationMarksRegex.Replace(str, "\"");
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,15 @@ namespace AdvancedPaste.Settings
|
||||
|
||||
public bool SendPasteKeyCombination { get; private set; }
|
||||
|
||||
public bool CloseAfterLosingFocus { get; private set; }
|
||||
|
||||
public UserSettings()
|
||||
{
|
||||
_settingsUtils = new SettingsUtils();
|
||||
|
||||
ShowCustomPreview = true;
|
||||
SendPasteKeyCombination = true;
|
||||
CloseAfterLosingFocus = false;
|
||||
|
||||
LoadSettingsFromJson();
|
||||
|
||||
@@ -61,6 +64,7 @@ namespace AdvancedPaste.Settings
|
||||
{
|
||||
ShowCustomPreview = settings.Properties.ShowCustomPreview;
|
||||
SendPasteKeyCombination = settings.Properties.SendPasteKeyCombination;
|
||||
CloseAfterLosingFocus = settings.Properties.CloseAfterLosingFocus;
|
||||
}
|
||||
|
||||
retry = false;
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
<value>Clipboard data is not text</value>
|
||||
</data>
|
||||
<data name="OpenAINotConfigured" xml:space="preserve">
|
||||
<value>To custom with AI not enabled</value>
|
||||
<value>To custom with AI is not enabled</value>
|
||||
</data>
|
||||
<data name="OpenAIApiKeyUnauthorized" xml:space="preserve">
|
||||
<value>Invalid API key or endpoint</value>
|
||||
@@ -225,4 +225,7 @@
|
||||
<data name="TermsLink.Text" xml:space="preserve">
|
||||
<value>OpenAI Terms</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="OpenAIGpoDisabled" xml:space="preserve">
|
||||
<value>To custom with AI is disabled by your organization</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace AdvancedPaste.Telemetry
|
||||
{
|
||||
[EventData]
|
||||
public class AdvancedPasteClipboardItemClicked : EventBase, IEvent
|
||||
{
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,19 @@ namespace AdvancedPaste.Telemetry
|
||||
[EventData]
|
||||
public class AdvancedPasteGenerateCustomFormatEvent : EventBase, IEvent
|
||||
{
|
||||
public int PromptTokens { get; set; }
|
||||
|
||||
public int CompletionTokens { get; set; }
|
||||
|
||||
public string ModelName { get; set; }
|
||||
|
||||
public AdvancedPasteGenerateCustomFormatEvent(int promptTokens, int completionTokens, string modelName)
|
||||
{
|
||||
this.PromptTokens = promptTokens;
|
||||
this.CompletionTokens = completionTokens;
|
||||
ModelName = modelName;
|
||||
}
|
||||
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Win32;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using WinUIEx;
|
||||
@@ -26,13 +25,12 @@ namespace AdvancedPaste.ViewModels
|
||||
public partial class OptionsViewModel : ObservableObject
|
||||
{
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
private readonly IUserSettings _userSettings;
|
||||
|
||||
private App app = App.Current as App;
|
||||
|
||||
private AICompletionsHelper aiHelper;
|
||||
|
||||
private UserSettings _userSettings;
|
||||
|
||||
public DataPackageView ClipboardData { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -51,10 +49,10 @@ namespace AdvancedPaste.ViewModels
|
||||
[NotifyPropertyChangedFor(nameof(InputTxtBoxErrorText))]
|
||||
private int _apiRequestStatus;
|
||||
|
||||
public OptionsViewModel()
|
||||
public OptionsViewModel(IUserSettings userSettings)
|
||||
{
|
||||
aiHelper = new AICompletionsHelper();
|
||||
_userSettings = new UserSettings();
|
||||
_userSettings = userSettings;
|
||||
|
||||
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
|
||||
|
||||
@@ -81,32 +79,40 @@ namespace AdvancedPaste.ViewModels
|
||||
{
|
||||
GetClipboardData();
|
||||
|
||||
var openAIKey = AICompletionsHelper.LoadOpenAIKey();
|
||||
var currentKey = aiHelper.GetKey();
|
||||
bool keyChanged = openAIKey != currentKey;
|
||||
|
||||
if (keyChanged)
|
||||
if (PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteOnlineAIModelsValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
app.GetMainWindow().StartLoading();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
aiHelper.SetOpenAIKey(openAIKey);
|
||||
}).ContinueWith(
|
||||
(t) =>
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
app.GetMainWindow().FinishLoading(aiHelper.IsAIEnabled);
|
||||
OnPropertyChanged(nameof(InputTxtBoxPlaceholderText));
|
||||
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
|
||||
});
|
||||
},
|
||||
TaskScheduler.Default);
|
||||
IsCustomAIEnabled = false;
|
||||
OnPropertyChanged(nameof(InputTxtBoxPlaceholderText));
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
|
||||
var openAIKey = AICompletionsHelper.LoadOpenAIKey();
|
||||
var currentKey = aiHelper.GetKey();
|
||||
bool keyChanged = openAIKey != currentKey;
|
||||
|
||||
if (keyChanged)
|
||||
{
|
||||
app.GetMainWindow().StartLoading();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
aiHelper.SetOpenAIKey(openAIKey);
|
||||
}).ContinueWith(
|
||||
(t) =>
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
app.GetMainWindow().FinishLoading(aiHelper.IsAIEnabled);
|
||||
OnPropertyChanged(nameof(InputTxtBoxPlaceholderText));
|
||||
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
|
||||
});
|
||||
},
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
ClipboardHistoryEnabled = IsClipboardHistoryEnabled();
|
||||
@@ -146,7 +152,11 @@ namespace AdvancedPaste.ViewModels
|
||||
{
|
||||
app.GetMainWindow().ClearInputText();
|
||||
|
||||
if (!aiHelper.IsAIEnabled)
|
||||
if (PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteOnlineAIModelsValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAIGpoDisabled");
|
||||
}
|
||||
else if (!aiHelper.IsAIEnabled)
|
||||
{
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAINotConfigured");
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
<!-- HACK: To make sure the version pulled in by Microsoft.Extensions.Hosting is current. -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<!-- HACK: To make sure the version pulled in by Microsoft.Extensions.Hosting is current. -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
<value>New profile</value>
|
||||
</data>
|
||||
<data name="ProfilesDescriptionLbl.Text" xml:space="preserve">
|
||||
<value>You can create profiles to quickly apply a set of preconfigured variables</value>
|
||||
<value>Create profiles to quickly apply a set of preconfigured variables. Profile variables have precedence over User and System variables.</value>
|
||||
</data>
|
||||
<data name="ProfilesLbl.Text" xml:space="preserve">
|
||||
<value>Profiles</value>
|
||||
@@ -194,7 +194,7 @@
|
||||
<value>Add</value>
|
||||
</data>
|
||||
<data name="AppliedVariablesDescriptionLbl.Text" xml:space="preserve">
|
||||
<value>List of applied variables</value>
|
||||
<value>Applied variables list shows the current state of the environment, including Profile, User, and System variables.</value>
|
||||
</data>
|
||||
<data name="AppliedVariablesLbl.Text" xml:space="preserve">
|
||||
<value>Applied variables</value>
|
||||
@@ -242,7 +242,7 @@
|
||||
<value>Add variable</value>
|
||||
</data>
|
||||
<data name="DefaultVariablesDescriptionLbl.Text" xml:space="preserve">
|
||||
<value>Add, remove or edit USER and SYSTEM variables</value>
|
||||
<value>Add, edit, or remove User and System variables.</value>
|
||||
</data>
|
||||
<data name="EditItem.Text" xml:space="preserve">
|
||||
<value>Edit</value>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h FileLocksmithContextMenu.base.rc FileLocksmithContextMenu.rc" />
|
||||
</Target>
|
||||
@@ -98,7 +99,30 @@ MakeAppx.exe pack /d . /p $(OutDir)FileLocksmithContextMenuPackage.msix /nv</Com
|
||||
<None Include="Resources.resx" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Assets\FileLocksmith\**" CopyToOutputDirectory="PreserveNewest" />
|
||||
<None Include="Assets\FileLocksmith\FileLocksmith.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\LargeTile.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\SmallTile.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\SplashScreen.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\Square150x150Logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\Square44x44Logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\storelogo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\Wide310x150Logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
@@ -118,11 +142,14 @@ MakeAppx.exe pack /d . /p $(OutDir)FileLocksmithContextMenuPackage.msix /nv</Com
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -37,10 +37,38 @@
|
||||
<None Include="Resources.resx">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\FileLocksmith.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\LargeTile.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\SmallTile.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\SplashScreen.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\Square44x44Logo.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\Square150x150Logo.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\storelogo.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\FileLocksmith\Wide310x150Logo.png">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="FileLocksmithContextMenu.base.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
@@ -78,7 +79,18 @@
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -51,4 +51,7 @@
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -25,6 +25,8 @@ namespace FileLocksmithUI
|
||||
Logger.InitializeLogger("\\File Locksmith\\FileLocksmithUI\\Logs");
|
||||
|
||||
this.InitializeComponent();
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,6 +57,11 @@ namespace FileLocksmithUI
|
||||
_window.Activate();
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.LogError("Unhandled exception", e.Exception);
|
||||
}
|
||||
|
||||
private Window _window;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,27 +141,16 @@
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind user}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard ContentAlignment="Vertical">
|
||||
<tkcontrols:SettingsCard>
|
||||
<tkcontrols:SettingsCard.Header>
|
||||
<TextBlock>
|
||||
<Run x:Uid="Files" />
|
||||
<Run Text="(" /><Run Text="{x:Bind files, Converter={StaticResource fileCountConverter}}" /><Run Text=")" />
|
||||
</TextBlock>
|
||||
</tkcontrols:SettingsCard.Header>
|
||||
<ItemsRepeater ItemsSource="{x:Bind files}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{Binding}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap"
|
||||
ToolTipService.ToolTip="{Binding}" />
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
<Button Click="ShowProcessFiles_Click">
|
||||
<TextBlock x:Uid="ShowProcessFiles" />
|
||||
</Button>
|
||||
</tkcontrols:SettingsCard>
|
||||
</tkcontrols:SettingsExpander.Items>
|
||||
</tkcontrols:SettingsExpander>
|
||||
@@ -200,5 +189,13 @@
|
||||
Text="{x:Bind ViewModel.PathsToString, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</ContentDialog>
|
||||
<ContentDialog x:Name="ProcessFilesListDialog" x:Uid="ProcessFilesListDialog">
|
||||
<ScrollViewer Padding="15" HorizontalScrollBarVisibility="Auto">
|
||||
<TextBlock
|
||||
x:Name="ProcessFilesListDialogTextBlock"
|
||||
x:Uid="ProcessFilesListDialogTextBlock"
|
||||
IsTextSelectionEnabled="True" />
|
||||
</ScrollViewer>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using FileLocksmith.Interop;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using PowerToys.FileLocksmithUI.ViewModels;
|
||||
|
||||
@@ -19,9 +21,17 @@ namespace PowerToys.FileLocksmithUI.Views
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
|
||||
private async void ShowSelectedPathsButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
private async void ShowSelectedPathsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await SelectedFilesListDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ShowProcessFiles_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var processResult = (ProcessResult)((FrameworkElement)sender).DataContext;
|
||||
ProcessFilesListDialogTextBlock.Text = string.Join(Environment.NewLine, processResult.files);
|
||||
|
||||
await ProcessFilesListDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,13 @@
|
||||
<data name="Files.Text" xml:space="preserve">
|
||||
<value>Files</value>
|
||||
</data>
|
||||
<data name="ProcessFilesListDialog.Title" xml:space="preserve">
|
||||
<value>Files</value>
|
||||
</data>
|
||||
<data name="ProcessFilesListDialog.CloseButtonText" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
<comment>As in, close a dialog prompt.</comment>
|
||||
</data>
|
||||
<data name="PathsTooltipDescription.Text" xml:space="preserve">
|
||||
<value>Click to see the entire list of paths.</value>
|
||||
<comment>Paths as in file paths that were selected for the utility to check.</comment>
|
||||
@@ -164,4 +171,8 @@
|
||||
<value>Administrator: File Locksmith</value>
|
||||
<comment>Title of the window when running as administrator.</comment>
|
||||
</data>
|
||||
<data name="ShowProcessFiles.Text" xml:space="preserve">
|
||||
<value>Show files</value>
|
||||
<comment>Show files for the selected process</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="MSTest.TestAdapter" />
|
||||
<PackageReference Include="MSTest.TestFramework" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" />
|
||||
<PackageReference Include="System.Diagnostics.EventLog">
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
<!-- HACK: To make sure the version pulled in by Microsoft.Extensions.Hosting is current. -->
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace Hosts
|
||||
services.AddSingleton<IHostsService, HostsService>();
|
||||
services.AddSingleton<IUserSettings, Hosts.Settings.UserSettings>();
|
||||
services.AddSingleton<IElevationHelper, ElevationHelper>();
|
||||
services.AddSingleton<IDuplicateService, DuplicateService>();
|
||||
|
||||
// Views and ViewModels
|
||||
services.AddSingleton<ILogger, LoggerWrapper>();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
x:Uid="Window"
|
||||
Width="680"
|
||||
MinWidth="480"
|
||||
MinWidth="520"
|
||||
MinHeight="320"
|
||||
mc:Ignorable="d">
|
||||
<Window.SystemBackdrop>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System;
|
||||
using System.IO.Abstractions;
|
||||
using System.Threading;
|
||||
using HostsUILib.Helpers;
|
||||
using HostsUILib.Settings;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
@@ -45,6 +44,8 @@ namespace Hosts.Settings
|
||||
// Moved from Settings.UI.Library
|
||||
public HostsEncoding Encoding { get; set; }
|
||||
|
||||
public event EventHandler LoopbackDuplicatesChanged;
|
||||
|
||||
public UserSettings()
|
||||
{
|
||||
_settingsUtils = new SettingsUtils();
|
||||
@@ -58,8 +59,6 @@ namespace Hosts.Settings
|
||||
_watcher = Helper.GetFileWatcher(HostsModuleName, "settings.json", () => LoadSettingsFromJson());
|
||||
}
|
||||
|
||||
public event EventHandler LoopbackDuplicatesChanged;
|
||||
|
||||
private void LoadSettingsFromJson()
|
||||
{
|
||||
lock (_loadingSettingsLock)
|
||||
|
||||
165
src/modules/Hosts/HostsUILib/Helpers/DuplicateService.cs
Normal file
165
src/modules/Hosts/HostsUILib/Helpers/DuplicateService.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using HostsUILib.Models;
|
||||
using HostsUILib.Settings;
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public class DuplicateService : IDuplicateService, IDisposable
|
||||
{
|
||||
private record struct Check(string Address, string[] Hosts);
|
||||
|
||||
private readonly IUserSettings _userSettings;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly Queue<Check> _checkQueue;
|
||||
private readonly ManualResetEvent _checkEvent;
|
||||
private readonly Thread _queueThread;
|
||||
|
||||
private readonly string[] _loopbackAddresses =
|
||||
{
|
||||
"0.0.0.0",
|
||||
"::",
|
||||
"::0",
|
||||
"0:0:0:0:0:0:0:0",
|
||||
"127.0.0.1",
|
||||
"::1",
|
||||
"0:0:0:0:0:0:0:1",
|
||||
};
|
||||
|
||||
private ReadOnlyCollection<Entry> _entries;
|
||||
private bool _disposed;
|
||||
|
||||
public DuplicateService(IUserSettings userSettings)
|
||||
{
|
||||
_userSettings = userSettings;
|
||||
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_checkQueue = new Queue<Check>();
|
||||
_checkEvent = new ManualResetEvent(false);
|
||||
|
||||
_queueThread = new Thread(ProcessQueue);
|
||||
_queueThread.IsBackground = true;
|
||||
_queueThread.Start();
|
||||
}
|
||||
|
||||
public void Initialize(IList<Entry> entries)
|
||||
{
|
||||
_entries = entries.AsReadOnly();
|
||||
|
||||
if (_checkQueue.Count > 0)
|
||||
{
|
||||
_checkQueue.Clear();
|
||||
}
|
||||
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
if (!_userSettings.LoopbackDuplicates && _loopbackAddresses.Contains(entry.Address))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_checkQueue.Enqueue(new Check(entry.Address, entry.SplittedHosts));
|
||||
}
|
||||
|
||||
_checkEvent.Set();
|
||||
}
|
||||
|
||||
public void CheckDuplicates(string address, string[] hosts)
|
||||
{
|
||||
_checkQueue.Enqueue(new Check(address, hosts));
|
||||
_checkEvent.Set();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void ProcessQueue()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_checkEvent.WaitOne();
|
||||
|
||||
while (_checkQueue.Count > 0)
|
||||
{
|
||||
var check = _checkQueue.Dequeue();
|
||||
FindDuplicates(check.Address, check.Hosts);
|
||||
}
|
||||
|
||||
_checkEvent.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindDuplicates(string address, string[] hosts)
|
||||
{
|
||||
var entries = _entries.Where(e =>
|
||||
string.Equals(e.Address, address, StringComparison.OrdinalIgnoreCase)
|
||||
|| hosts.Intersect(e.SplittedHosts, StringComparer.OrdinalIgnoreCase).Any());
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
SetDuplicate(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDuplicate(Entry entry)
|
||||
{
|
||||
if (!_userSettings.LoopbackDuplicates && _loopbackAddresses.Contains(entry.Address))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
entry.Duplicate = false;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var duplicate = false;
|
||||
|
||||
/*
|
||||
* Duplicate are based on the following criteria:
|
||||
* Entries with the same type and at least one host in common
|
||||
* Entries with the same type and address, except when there is only one entry with less than 9 hosts for that type and address
|
||||
*/
|
||||
if (_entries.Any(e => e != entry
|
||||
&& e.Type == entry.Type
|
||||
&& entry.SplittedHosts.Intersect(e.SplittedHosts, StringComparer.OrdinalIgnoreCase).Any()))
|
||||
{
|
||||
duplicate = true;
|
||||
}
|
||||
else if (_entries.Any(e => e != entry
|
||||
&& e.Type == entry.Type
|
||||
&& string.Equals(e.Address, entry.Address, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
duplicate = entry.SplittedHosts.Length < Consts.MaxHostsCount
|
||||
&& _entries.Count(e => e.Type == entry.Type
|
||||
&& string.Equals(e.Address, entry.Address, StringComparison.OrdinalIgnoreCase)
|
||||
&& e.SplittedHosts.Length < Consts.MaxHostsCount) > 1;
|
||||
}
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() => entry.Duplicate = duplicate);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_checkEvent?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/modules/Hosts/HostsUILib/Helpers/IDuplicateService.cs
Normal file
16
src/modules/Hosts/HostsUILib/Helpers/IDuplicateService.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using HostsUILib.Models;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public interface IDuplicateService
|
||||
{
|
||||
void Initialize(IList<Entry> entries);
|
||||
|
||||
void CheckDuplicates(string address, string[] hosts);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using HostsUILib.Models;
|
||||
|
||||
namespace HostsUILib.Helpers
|
||||
{
|
||||
public interface IHostsService : IDisposable
|
||||
public interface IHostsService
|
||||
{
|
||||
string HostsFilePath { get; }
|
||||
|
||||
|
||||
@@ -412,23 +412,23 @@
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:Entry">
|
||||
<Grid
|
||||
Margin="0"
|
||||
AutomationProperties.Name="{x:Bind Address, Mode=OneWay}"
|
||||
Background="Transparent"
|
||||
ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="256" />
|
||||
<ColumnDefinition Width="*" MinWidth="150" />
|
||||
<!-- Address -->
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" MinWidth="120" />
|
||||
<!-- Comment -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<!-- Status -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<!-- Duplicate -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- ToggleSwitch -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- DeleteEntry -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
@@ -511,7 +511,7 @@
|
||||
Grid.Column="4"
|
||||
Width="40"
|
||||
MinWidth="0"
|
||||
HorizontalAlignment="Right"
|
||||
HorizontalAlignment="Center"
|
||||
GotFocus="Entries_GotFocus"
|
||||
IsOn="{x:Bind Active, Mode=TwoWay}"
|
||||
OffContent=""
|
||||
@@ -705,10 +705,13 @@
|
||||
Padding="16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
AcceptsReturn="True"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollMode="Auto"
|
||||
ScrollViewer.IsHorizontalRailEnabled="True"
|
||||
ScrollViewer.IsVerticalRailEnabled="True"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
||||
ScrollViewer.VerticalScrollMode="Enabled"
|
||||
TextWrapping="Wrap" />
|
||||
TextWrapping="NoWrap" />
|
||||
</ContentDialog>
|
||||
|
||||
<TeachingTip
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace HostsUILib.Settings
|
||||
{
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -23,21 +22,14 @@ using static HostsUILib.Settings.IUserSettings;
|
||||
|
||||
namespace HostsUILib.ViewModels
|
||||
{
|
||||
public partial class MainViewModel : ObservableObject, IDisposable
|
||||
public partial class MainViewModel : ObservableObject
|
||||
{
|
||||
private readonly IHostsService _hostsService;
|
||||
private readonly IUserSettings _userSettings;
|
||||
private readonly IDuplicateService _duplicateService;
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
private readonly string[] _loopbackAddresses =
|
||||
{
|
||||
"127.0.0.1",
|
||||
"::1",
|
||||
"0:0:0:0:0:0:0:1",
|
||||
};
|
||||
|
||||
private bool _readingHosts;
|
||||
private bool _disposed;
|
||||
private CancellationTokenSource _tokenSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private Entry _selected;
|
||||
@@ -95,10 +87,16 @@ namespace HostsUILib.ViewModels
|
||||
|
||||
private OpenSettingsFunction _openSettingsFunction;
|
||||
|
||||
public MainViewModel(IHostsService hostService, IUserSettings userSettings, ILogger logger, OpenSettingsFunction openSettingsFunction)
|
||||
public MainViewModel(
|
||||
IHostsService hostService,
|
||||
IUserSettings userSettings,
|
||||
IDuplicateService duplicateService,
|
||||
ILogger logger,
|
||||
OpenSettingsFunction openSettingsFunction)
|
||||
{
|
||||
_hostsService = hostService;
|
||||
_userSettings = userSettings;
|
||||
_duplicateService = duplicateService;
|
||||
|
||||
_hostsService.FileChanged += (s, e) => _dispatcherQueue.TryEnqueue(() => FileChanged = true);
|
||||
_userSettings.LoopbackDuplicatesChanged += (s, e) => ReadHosts();
|
||||
@@ -111,8 +109,7 @@ namespace HostsUILib.ViewModels
|
||||
{
|
||||
entry.PropertyChanged += Entry_PropertyChanged;
|
||||
_entries.Add(entry);
|
||||
|
||||
FindDuplicates(entry.Address, entry.SplittedHosts);
|
||||
_duplicateService.CheckDuplicates(entry.Address, entry.SplittedHosts);
|
||||
}
|
||||
|
||||
public void Update(int index, Entry entry)
|
||||
@@ -126,8 +123,8 @@ namespace HostsUILib.ViewModels
|
||||
existingEntry.Hosts = entry.Hosts;
|
||||
existingEntry.Active = entry.Active;
|
||||
|
||||
FindDuplicates(oldAddress, oldHosts);
|
||||
FindDuplicates(entry.Address, entry.SplittedHosts);
|
||||
_duplicateService.CheckDuplicates(oldAddress, oldHosts);
|
||||
_duplicateService.CheckDuplicates(entry.Address, entry.SplittedHosts);
|
||||
}
|
||||
|
||||
public void DeleteSelected()
|
||||
@@ -135,8 +132,7 @@ namespace HostsUILib.ViewModels
|
||||
var address = Selected.Address;
|
||||
var hosts = Selected.SplittedHosts;
|
||||
_entries.Remove(Selected);
|
||||
|
||||
FindDuplicates(address, hosts);
|
||||
_duplicateService.CheckDuplicates(address, hosts);
|
||||
}
|
||||
|
||||
public void UpdateAdditionalLines(string lines)
|
||||
@@ -169,8 +165,7 @@ namespace HostsUILib.ViewModels
|
||||
var address = entry.Address;
|
||||
var hosts = entry.SplittedHosts;
|
||||
_entries.Remove(entry);
|
||||
|
||||
FindDuplicates(address, hosts);
|
||||
_duplicateService.CheckDuplicates(address, hosts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,9 +208,7 @@ namespace HostsUILib.ViewModels
|
||||
});
|
||||
_readingHosts = false;
|
||||
|
||||
_tokenSource?.Cancel();
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
FindDuplicates(_tokenSource.Token);
|
||||
_duplicateService.Initialize(_entries);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -294,12 +287,6 @@ namespace HostsUILib.ViewModels
|
||||
_ = Task.Run(SaveAsync);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Entry_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (Filtered && (e.PropertyName == nameof(Entry.Hosts)
|
||||
@@ -326,82 +313,6 @@ namespace HostsUILib.ViewModels
|
||||
_ = Task.Run(SaveAsync);
|
||||
}
|
||||
|
||||
private void FindDuplicates(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var entry in _entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (!_userSettings.LoopbackDuplicates && _loopbackAddresses.Contains(entry.Address))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SetDuplicate(entry);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
LoggerInstance.Logger.LogInfo("FindDuplicates cancelled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FindDuplicates(string address, IEnumerable<string> hosts)
|
||||
{
|
||||
var entries = _entries.Where(e =>
|
||||
string.Equals(e.Address, address, StringComparison.OrdinalIgnoreCase)
|
||||
|| hosts.Intersect(e.SplittedHosts, StringComparer.OrdinalIgnoreCase).Any());
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
SetDuplicate(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDuplicate(Entry entry)
|
||||
{
|
||||
if (!_userSettings.LoopbackDuplicates && _loopbackAddresses.Contains(entry.Address))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
entry.Duplicate = false;
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var duplicate = false;
|
||||
|
||||
/*
|
||||
* Duplicate are based on the following criteria:
|
||||
* Entries with the same type and at least one host in common
|
||||
* Entries with the same type and address, except when there is only one entry with less than 9 hosts for that type and address
|
||||
*/
|
||||
if (_entries.Any(e => e != entry
|
||||
&& e.Type == entry.Type
|
||||
&& entry.SplittedHosts.Intersect(e.SplittedHosts, StringComparer.OrdinalIgnoreCase).Any()))
|
||||
{
|
||||
duplicate = true;
|
||||
}
|
||||
else if (_entries.Any(e => e != entry
|
||||
&& e.Type == entry.Type
|
||||
&& string.Equals(e.Address, entry.Address, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
duplicate = entry.SplittedHosts.Length < Consts.MaxHostsCount
|
||||
&& _entries.Count(e => e.Type == entry.Type
|
||||
&& string.Equals(e.Address, entry.Address, StringComparison.OrdinalIgnoreCase)
|
||||
&& e.SplittedHosts.Length < Consts.MaxHostsCount) > 1;
|
||||
}
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
entry.Duplicate = duplicate;
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
bool error = true;
|
||||
@@ -444,17 +355,5 @@ namespace HostsUILib.ViewModels
|
||||
IsReadOnly = isReadOnly;
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_hostsService?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -141,7 +141,7 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -152,7 +152,7 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240311000\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.5.240428000\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -3,5 +3,5 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.5.240311000" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.5.240428000" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -82,9 +82,9 @@ private:
|
||||
{
|
||||
Logger::info("MeasureTool is going to use default shortcut");
|
||||
m_hotkey.win = true;
|
||||
m_hotkey.ctrl = true;
|
||||
m_hotkey.alt = false;
|
||||
m_hotkey.shift = true;
|
||||
m_hotkey.ctrl = false;
|
||||
m_hotkey.key = 'M';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ protected:
|
||||
|
||||
bool m_destroyed = false;
|
||||
FindMyMouseActivationMethod m_activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD;
|
||||
bool m_includeWinKey = FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY;
|
||||
bool m_doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
int m_sonarRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS;
|
||||
int m_sonarZoomFactor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
|
||||
@@ -146,6 +147,7 @@ private:
|
||||
void OnMouseTimer();
|
||||
|
||||
void DetectShake();
|
||||
bool KeyboardInputCanActivate();
|
||||
|
||||
void StartSonar();
|
||||
void StopSonar();
|
||||
@@ -352,7 +354,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
break;
|
||||
|
||||
case SonarState::ControlUp1:
|
||||
if (pressed)
|
||||
if (pressed && KeyboardInputCanActivate())
|
||||
{
|
||||
auto now = GetTickCount64();
|
||||
auto doubleClickInterval = now - m_lastKeyTime;
|
||||
@@ -438,6 +440,12 @@ void SuperSonar<D>::DetectShake()
|
||||
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
bool SuperSonar<D>::KeyboardInputCanActivate()
|
||||
{
|
||||
return !m_includeWinKey || (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
{
|
||||
@@ -762,6 +770,7 @@ public:
|
||||
m_backgroundColor = settings.backgroundColor;
|
||||
m_spotlightColor = settings.spotlightColor;
|
||||
m_activationMethod = settings.activationMethod;
|
||||
m_includeWinKey = settings.includeWinKey;
|
||||
m_doNotActivateOnGameMode = settings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = settings.animationDurationMs > 0 ? settings.animationDurationMs : 1;
|
||||
m_finalAlphaNumerator = settings.overlayOpacity;
|
||||
@@ -791,6 +800,7 @@ public:
|
||||
m_backgroundColor = localSettings.backgroundColor;
|
||||
m_spotlightColor = localSettings.spotlightColor;
|
||||
m_activationMethod = localSettings.activationMethod;
|
||||
m_includeWinKey = localSettings.includeWinKey;
|
||||
m_doNotActivateOnGameMode = localSettings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = localSettings.animationDurationMs > 0 ? localSettings.animationDurationMs : 1;
|
||||
m_finalAlphaNumerator = localSettings.overlayOpacity;
|
||||
|
||||
@@ -18,6 +18,7 @@ constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS = 100;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;
|
||||
constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleLeftControlKey;
|
||||
constexpr bool FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY = false;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE = 1000;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS = 1000;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR = 400; // 400 percent
|
||||
@@ -25,6 +26,7 @@ constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR = 400; // 400 percent
|
||||
struct FindMyMouseSettings
|
||||
{
|
||||
FindMyMouseActivationMethod activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD;
|
||||
bool includeWinKey = FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY;
|
||||
bool doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
winrt::Windows::UI::Color backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR;
|
||||
winrt::Windows::UI::Color spotlightColor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace
|
||||
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t JSON_KEY_ACTIVATION_METHOD[] = L"activation_method";
|
||||
const wchar_t JSON_KEY_INCLUDE_WIN_KEY[] = L"include_win_key";
|
||||
const wchar_t JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE[] = L"do_not_activate_on_game_mode";
|
||||
const wchar_t JSON_KEY_BACKGROUND_COLOR[] = L"background_color";
|
||||
const wchar_t JSON_KEY_SPOTLIGHT_COLOR[] = L"spotlight_color";
|
||||
@@ -237,6 +238,15 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
Logger::warn("Failed to initialize Activation Method from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_INCLUDE_WIN_KEY);
|
||||
findMyMouseSettings.includeWinKey = jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to get 'include windows key with ctrl' setting");
|
||||
}
|
||||
try
|
||||
{
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE);
|
||||
findMyMouseSettings.doNotActivateOnGameMode = jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE);
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Helpers;
|
||||
using MouseJumpUI.Common.Imaging;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
using MouseJumpUI.Helpers;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Helpers;
|
||||
|
||||
[TestClass]
|
||||
public static class DrawingHelperTests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class GetPreviewLayoutTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(PreviewStyle previewStyle, List<RectangleInfo> screens, PointInfo activatedLocation, string desktopImageFilename, string expectedImageFilename)
|
||||
{
|
||||
this.PreviewStyle = previewStyle;
|
||||
this.Screens = screens;
|
||||
this.ActivatedLocation = activatedLocation;
|
||||
this.DesktopImageFilename = desktopImageFilename;
|
||||
this.ExpectedImageFilename = expectedImageFilename;
|
||||
}
|
||||
|
||||
public PreviewStyle PreviewStyle { get; }
|
||||
|
||||
public List<RectangleInfo> Screens { get; }
|
||||
|
||||
public PointInfo ActivatedLocation { get; }
|
||||
|
||||
public string DesktopImageFilename { get; }
|
||||
|
||||
public string ExpectedImageFilename { get; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
/* 4-grid */
|
||||
yield return new object[]
|
||||
{
|
||||
new TestCase(
|
||||
previewStyle: StyleHelper.DefaultPreviewStyle,
|
||||
screens: new List<RectangleInfo>()
|
||||
{
|
||||
new(0, 0, 500, 500),
|
||||
new(500, 0, 500, 500),
|
||||
new(500, 500, 500, 500),
|
||||
new(0, 500, 500, 500),
|
||||
},
|
||||
activatedLocation: new(x: 50, y: 50),
|
||||
desktopImageFilename: "Common/Helpers/_test-4grid-desktop.png",
|
||||
expectedImageFilename: "Common/Helpers/_test-4grid-expected.png"),
|
||||
};
|
||||
/* win 11 */
|
||||
yield return new object[]
|
||||
{
|
||||
new TestCase(
|
||||
previewStyle: StyleHelper.DefaultPreviewStyle,
|
||||
screens: new List<RectangleInfo>()
|
||||
{
|
||||
new(5120, 349, 1920, 1080),
|
||||
new(0, 0, 5120, 1440),
|
||||
},
|
||||
activatedLocation: new(x: 50, y: 50),
|
||||
desktopImageFilename: "Common/Helpers/_test-win11-desktop.png",
|
||||
expectedImageFilename: "Common/Helpers/_test-win11-expected.png"),
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
// load the fake desktop image
|
||||
using var desktopImage = GetPreviewLayoutTests.LoadImageResource(data.DesktopImageFilename);
|
||||
|
||||
// draw the preview image
|
||||
var previewLayout = LayoutHelper.GetPreviewLayout(
|
||||
previewStyle: data.PreviewStyle,
|
||||
screens: data.Screens,
|
||||
activatedLocation: data.ActivatedLocation);
|
||||
var imageCopyService = new StaticImageRegionCopyService(desktopImage);
|
||||
using var actual = DrawingHelper.RenderPreview(previewLayout, imageCopyService);
|
||||
|
||||
// load the expected image
|
||||
var expected = GetPreviewLayoutTests.LoadImageResource(data.ExpectedImageFilename);
|
||||
|
||||
// compare the images
|
||||
var screens = System.Windows.Forms.Screen.AllScreens;
|
||||
AssertImagesEqual(expected, actual);
|
||||
}
|
||||
|
||||
private static Bitmap LoadImageResource(string filename)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
|
||||
var resourceName = $"Microsoft.{assemblyName.Name}.{filename.Replace("/", ".")}";
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
if (!resourceNames.Contains(resourceName))
|
||||
{
|
||||
throw new InvalidOperationException($"Embedded resource '{resourceName}' does not exist.");
|
||||
}
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(resourceName)
|
||||
?? throw new InvalidOperationException();
|
||||
var image = (Bitmap)Image.FromStream(stream);
|
||||
return image;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Naive / brute force image comparison - we can optimise this later :-)
|
||||
/// </summary>
|
||||
private static void AssertImagesEqual(Bitmap expected, Bitmap actual)
|
||||
{
|
||||
Assert.AreEqual(
|
||||
expected.Width,
|
||||
actual.Width,
|
||||
$"expected width: {expected.Width}, actual width: {actual.Width}");
|
||||
Assert.AreEqual(
|
||||
expected.Height,
|
||||
actual.Height,
|
||||
$"expected height: {expected.Height}, actual height: {actual.Height}");
|
||||
for (var y = 0; y < expected.Height; y++)
|
||||
{
|
||||
for (var x = 0; x < expected.Width; x++)
|
||||
{
|
||||
var expectedPixel = expected.GetPixel(x, y);
|
||||
var actualPixel = actual.GetPixel(x, y);
|
||||
|
||||
// allow a small tolerance for rounding differences in gdi
|
||||
Assert.IsTrue(
|
||||
(Math.Abs(expectedPixel.A - actualPixel.A) <= 1) &&
|
||||
(Math.Abs(expectedPixel.R - actualPixel.R) <= 1) &&
|
||||
(Math.Abs(expectedPixel.G - actualPixel.G) <= 1) &&
|
||||
(Math.Abs(expectedPixel.B - actualPixel.B) <= 1),
|
||||
$"images differ at pixel ({x}, {y}) - expected: {expectedPixel}, actual: {actualPixel}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text.Json;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Helpers;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Layout;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Helpers;
|
||||
|
||||
[TestClass]
|
||||
public static class LayoutHelperTests
|
||||
{
|
||||
/*
|
||||
[TestClass]
|
||||
public sealed class OldLayoutTests
|
||||
{
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// check we handle rounding errors in scaling the preview form
|
||||
// that might make the form *larger* than the current screen -
|
||||
// e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768
|
||||
// with a 5px form padding border:
|
||||
//
|
||||
// ((decimal)1014 / 7168) * 7168 = 1014.0000000000000000000000002
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | 1 +-------+
|
||||
// | | 0 |
|
||||
// +----------------+-------+
|
||||
layoutConfig = new LayoutConfig(
|
||||
virtualScreenBounds: new(0, 0, 7168, 1440),
|
||||
screens: new List<ScreenInfo>
|
||||
{
|
||||
new(HMONITOR.Null, false, new(6144, 0, 1024, 768), new(6144, 0, 1024, 768)),
|
||||
new(HMONITOR.Null, false, new(0, 0, 6144, 1440), new(0, 0, 6144, 1440)),
|
||||
},
|
||||
activatedLocation: new(6656, 384),
|
||||
activatedScreenIndex: 0,
|
||||
activatedScreenNumber: 1,
|
||||
maximumFormSize: new(1600, 1200),
|
||||
formPadding: new(5, 5, 5, 5),
|
||||
previewPadding: new(0, 0, 0, 0));
|
||||
layoutInfo = new LayoutInfo(
|
||||
layoutConfig: layoutConfig,
|
||||
formBounds: new(6144, 277.14732M, 1024, 213.70535M),
|
||||
previewBounds: new(0, 0, 1014, 203.70535M),
|
||||
screenBounds: new List<RectangleInfo>
|
||||
{
|
||||
new(869.14285M, 0, 144.85714M, 108.642857M),
|
||||
new(0, 0, 869.142857M, 203.705357M),
|
||||
},
|
||||
activatedScreenBounds: new(6144, 0, 1024, 768));
|
||||
yield return new object[] { new TestCase(layoutConfig, layoutInfo) };
|
||||
|
||||
// check we handle rounding errors in scaling the preview form
|
||||
// that might make the form a pixel *smaller* than the current screen -
|
||||
// e.g. a desktop 7168 x 1440 scaled to a screen 1024 x 768
|
||||
// with a 5px form padding border:
|
||||
//
|
||||
// ((decimal)1280 / 7424) * 7424 = 1279.9999999999999999999999999
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | 1 +-------+
|
||||
// | | 0 |
|
||||
// +----------------+-------+
|
||||
layoutConfig = new LayoutConfig(
|
||||
virtualScreenBounds: new(0, 0, 7424, 1440),
|
||||
screens: new List<ScreenInfo>
|
||||
{
|
||||
new(HMONITOR.Null, false, new(6144, 0, 1280, 768), new(6144, 0, 1280, 768)),
|
||||
new(HMONITOR.Null, false, new(0, 0, 6144, 1440), new(0, 0, 6144, 1440)),
|
||||
},
|
||||
activatedLocation: new(6784, 384),
|
||||
activatedScreenIndex: 0,
|
||||
activatedScreenNumber: 1,
|
||||
maximumFormSize: new(1600, 1200),
|
||||
formPadding: new(5, 5, 5, 5),
|
||||
previewPadding: new(0, 0, 0, 0));
|
||||
layoutInfo = new LayoutInfo(
|
||||
layoutConfig: layoutConfig,
|
||||
formBounds: new(
|
||||
6144,
|
||||
255.83189M, // (768 - (((decimal)(1280-10) / 7424 * 1440) + 10)) / 2
|
||||
1280,
|
||||
256.33620M // ((decimal)(1280 - 10) / 7424 * 1440) + 10
|
||||
),
|
||||
previewBounds: new(0, 0, 1270, 246.33620M),
|
||||
screenBounds: new List<RectangleInfo>
|
||||
{
|
||||
new(1051.03448M, 0, 218.96551M, 131.37931M),
|
||||
new(0, 0M, 1051.03448M, 246.33620M),
|
||||
},
|
||||
activatedScreenBounds: new(6144, 0, 1280, 768));
|
||||
yield return new object[] { new TestCase(layoutConfig, layoutInfo) };
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
[TestClass]
|
||||
public sealed class GetPreviewLayoutTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(PreviewStyle previewStyle, List<RectangleInfo> screens, PointInfo activatedLocation, PreviewLayout expectedResult)
|
||||
{
|
||||
this.PreviewStyle = previewStyle;
|
||||
this.Screens = screens;
|
||||
this.ActivatedLocation = activatedLocation;
|
||||
this.ExpectedResult = expectedResult;
|
||||
}
|
||||
|
||||
public PreviewStyle PreviewStyle { get; }
|
||||
|
||||
public List<RectangleInfo> Screens { get; }
|
||||
|
||||
public PointInfo ActivatedLocation { get; }
|
||||
|
||||
public PreviewLayout ExpectedResult { get; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// happy path - single screen with 50% scaling,
|
||||
// *has* a preview borders but *no* screenshot borders
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | 0 |
|
||||
// | |
|
||||
// +----------------+
|
||||
var previewStyle = new PreviewStyle(
|
||||
canvasSize: new(
|
||||
width: 524,
|
||||
height: 396
|
||||
),
|
||||
canvasStyle: new(
|
||||
marginStyle: MarginStyle.Empty,
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 5,
|
||||
depth: 3),
|
||||
paddingStyle: new(
|
||||
all: 1),
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(13, 87, 210), // light blue
|
||||
color2: Color.FromArgb(3, 68, 192) // darker blue
|
||||
)
|
||||
),
|
||||
screenStyle: BoxStyle.Empty);
|
||||
var screens = new List<RectangleInfo>
|
||||
{
|
||||
new(0, 0, 1024, 768),
|
||||
};
|
||||
var activatedLocation = new PointInfo(512, 384);
|
||||
var previewLayout = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 1024, 768),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
formBounds: new(250, 186, 524, 396),
|
||||
previewStyle: previewStyle,
|
||||
previewBounds: new(
|
||||
outerBounds: new(0, 0, 524, 396),
|
||||
marginBounds: new(0, 0, 524, 396),
|
||||
borderBounds: new(0, 0, 524, 396),
|
||||
paddingBounds: new(5, 5, 514, 386),
|
||||
contentBounds: new(6, 6, 512, 384)
|
||||
),
|
||||
screenshotBounds: new()
|
||||
{
|
||||
new(
|
||||
outerBounds: new(6, 6, 512, 384),
|
||||
marginBounds: new(6, 6, 512, 384),
|
||||
borderBounds: new(6, 6, 512, 384),
|
||||
paddingBounds: new(6, 6, 512, 384),
|
||||
contentBounds: new(6, 6, 512, 384)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
|
||||
// happy path - single screen with 50% scaling,
|
||||
// *no* preview borders but *has* screenshot borders
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | 0 |
|
||||
// | |
|
||||
// +----------------+
|
||||
previewStyle = new PreviewStyle(
|
||||
canvasSize: new(
|
||||
width: 512,
|
||||
height: 384
|
||||
),
|
||||
canvasStyle: BoxStyle.Empty,
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: 1),
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 5,
|
||||
depth: 3),
|
||||
paddingStyle: PaddingStyle.Empty,
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(13, 87, 210), // light blue
|
||||
color2: Color.FromArgb(3, 68, 192) // darker blue
|
||||
)
|
||||
));
|
||||
screens = new List<RectangleInfo>
|
||||
{
|
||||
new(0, 0, 1024, 768),
|
||||
};
|
||||
activatedLocation = new PointInfo(512, 384);
|
||||
previewLayout = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 1024, 768),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
formBounds: new(256, 192, 512, 384),
|
||||
previewStyle: previewStyle,
|
||||
previewBounds: new(
|
||||
outerBounds: new(0, 0, 512, 384),
|
||||
marginBounds: new(0, 0, 512, 384),
|
||||
borderBounds: new(0, 0, 512, 384),
|
||||
paddingBounds: new(0, 0, 512, 384),
|
||||
contentBounds: new(0, 0, 512, 384)
|
||||
),
|
||||
screenshotBounds: new()
|
||||
{
|
||||
new(
|
||||
outerBounds: new(0, 0, 512, 384),
|
||||
marginBounds: new(0, 0, 512, 384),
|
||||
borderBounds: new(1, 1, 510, 382),
|
||||
paddingBounds: new(6, 6, 500, 372),
|
||||
contentBounds: new(6, 6, 500, 372)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
|
||||
// primary monitor not topmost / leftmost - if there are screens
|
||||
// that are further left or higher up than the primary monitor
|
||||
// they'll have negative coordinates which has caused some
|
||||
// issues with calculations in the past. this test will make
|
||||
// sure we handle screens with negative coordinates gracefully
|
||||
//
|
||||
// +-------+
|
||||
// | 0 +----------------+
|
||||
// +-------+ |
|
||||
// | 1 |
|
||||
// | |
|
||||
// +----------------+
|
||||
previewStyle = new PreviewStyle(
|
||||
canvasSize: new(
|
||||
width: 716,
|
||||
height: 204
|
||||
),
|
||||
canvasStyle: new(
|
||||
marginStyle: MarginStyle.Empty,
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 5,
|
||||
depth: 3),
|
||||
paddingStyle: new(
|
||||
all: 1),
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(13, 87, 210), // light blue
|
||||
color2: Color.FromArgb(3, 68, 192) // darker blue
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: 1),
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 5,
|
||||
depth: 3),
|
||||
paddingStyle: PaddingStyle.Empty,
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(13, 87, 210), // light blue
|
||||
color2: Color.FromArgb(3, 68, 192) // darker blue
|
||||
)
|
||||
));
|
||||
screens = new List<RectangleInfo>
|
||||
{
|
||||
new(-1920, -480, 1920, 1080),
|
||||
new(0, 0, 5120, 1440),
|
||||
};
|
||||
activatedLocation = new(-960, 60);
|
||||
previewLayout = new PreviewLayout(
|
||||
virtualScreen: new(-1920, -480, 7040, 1920),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
formBounds: new(-1318, -42, 716, 204),
|
||||
previewStyle: previewStyle,
|
||||
previewBounds: new(
|
||||
outerBounds: new(0, 0, 716, 204),
|
||||
marginBounds: new(0, 0, 716, 204),
|
||||
borderBounds: new(0, 0, 716, 204),
|
||||
paddingBounds: new(5, 5, 706, 194),
|
||||
contentBounds: new(6, 6, 704, 192)
|
||||
),
|
||||
screenshotBounds: new()
|
||||
{
|
||||
new(
|
||||
outerBounds: new(6, 6, 192, 108),
|
||||
marginBounds: new(6, 6, 192, 108),
|
||||
borderBounds: new(7, 7, 190, 106),
|
||||
paddingBounds: new(12, 12, 180, 96),
|
||||
contentBounds: new(12, 12, 180, 96)
|
||||
),
|
||||
new(
|
||||
outerBounds: new(198, 54, 512, 144),
|
||||
marginBounds: new(198, 54, 512, 144),
|
||||
borderBounds: new(199, 55, 510, 142),
|
||||
paddingBounds: new(204, 60, 500, 132),
|
||||
contentBounds: new(204, 60, 500, 132)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
// note - even if values are within 0.0001M of each other they could
|
||||
// still round to different values - e.g.
|
||||
// (int)1279.999999999999 -> 1279
|
||||
// vs
|
||||
// (int)1280.000000000000 -> 1280
|
||||
// so we'll compare the raw values, *and* convert to an int-based
|
||||
// Rectangle to compare rounded values
|
||||
var actual = LayoutHelper.GetPreviewLayout(data.PreviewStyle, data.Screens, data.ActivatedLocation);
|
||||
var expected = data.ExpectedResult;
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
Assert.AreEqual(
|
||||
JsonSerializer.Serialize(expected, options),
|
||||
JsonSerializer.Serialize(actual, options));
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public sealed class GetBoxBoundsFromContentBoundsTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(RectangleInfo contentBounds, BoxStyle boxStyle, BoxBounds expectedResult)
|
||||
{
|
||||
this.ContentBounds = contentBounds;
|
||||
this.BoxStyle = boxStyle;
|
||||
this.ExpectedResult = expectedResult;
|
||||
}
|
||||
|
||||
public RectangleInfo ContentBounds { get; set; }
|
||||
|
||||
public BoxStyle BoxStyle { get; set; }
|
||||
|
||||
public BoxBounds ExpectedResult { get; set; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
yield return new[]
|
||||
{
|
||||
new TestCase(
|
||||
contentBounds: new(100, 100, 800, 600),
|
||||
boxStyle: new(
|
||||
marginStyle: new(3),
|
||||
borderStyle: new(Color.Red, 5, 0),
|
||||
paddingStyle: new(7),
|
||||
backgroundStyle: BackgroundStyle.Empty),
|
||||
expectedResult: new(
|
||||
outerBounds: new(85, 85, 830, 630),
|
||||
marginBounds: new(85, 85, 830, 630),
|
||||
borderBounds: new(88, 88, 824, 624),
|
||||
paddingBounds: new(93, 93, 814, 614),
|
||||
contentBounds: new(100, 100, 800, 600))),
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
var actual = LayoutHelper.GetBoxBoundsFromContentBounds(data.ContentBounds, data.BoxStyle);
|
||||
var expected = data.ExpectedResult;
|
||||
Assert.AreEqual(
|
||||
JsonSerializer.Serialize(expected),
|
||||
JsonSerializer.Serialize(actual));
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public sealed class GetBoxBoundsFromOuterBoundsTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(RectangleInfo outerBounds, BoxStyle boxStyle, BoxBounds expectedResult)
|
||||
{
|
||||
this.OuterBounds = outerBounds;
|
||||
this.BoxStyle = boxStyle;
|
||||
this.ExpectedResult = expectedResult;
|
||||
}
|
||||
|
||||
public RectangleInfo OuterBounds { get; set; }
|
||||
|
||||
public BoxStyle BoxStyle { get; set; }
|
||||
|
||||
public BoxBounds ExpectedResult { get; set; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
yield return new[]
|
||||
{
|
||||
new TestCase(
|
||||
outerBounds: new(85, 85, 830, 630),
|
||||
boxStyle: new(
|
||||
marginStyle: new(3),
|
||||
borderStyle: new(Color.Red, 5, 0),
|
||||
paddingStyle: new(7),
|
||||
backgroundStyle: BackgroundStyle.Empty),
|
||||
expectedResult: new(
|
||||
outerBounds: new(85, 85, 830, 630),
|
||||
marginBounds: new(85, 85, 830, 630),
|
||||
borderBounds: new(88, 88, 824, 624),
|
||||
paddingBounds: new(93, 93, 814, 614),
|
||||
contentBounds: new(100, 100, 800, 600))),
|
||||
};
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
var actual = LayoutHelper.GetBoxBoundsFromOuterBounds(data.OuterBounds, data.BoxStyle);
|
||||
var expected = data.ExpectedResult;
|
||||
Assert.AreEqual(
|
||||
JsonSerializer.Serialize(expected),
|
||||
JsonSerializer.Serialize(actual));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Helpers;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Helpers;
|
||||
|
||||
[TestClass]
|
||||
public static class MouseHelperTests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class GetJumpLocationTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds, PointInfo expectedResult)
|
||||
{
|
||||
this.PreviewLocation = previewLocation;
|
||||
this.PreviewSize = previewSize;
|
||||
this.DesktopBounds = desktopBounds;
|
||||
this.ExpectedResult = expectedResult;
|
||||
}
|
||||
|
||||
public PointInfo PreviewLocation { get; }
|
||||
|
||||
public SizeInfo PreviewSize { get; }
|
||||
|
||||
public RectangleInfo DesktopBounds { get; }
|
||||
|
||||
public PointInfo ExpectedResult { get; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// screen corners and midpoint with a zero origin
|
||||
yield return new object[] { new TestCase(new(0, 0), new(160, 120), new(0, 0, 1600, 1200), new(0, 0)) };
|
||||
yield return new object[] { new TestCase(new(160, 0), new(160, 120), new(0, 0, 1600, 1200), new(1600, 0)) };
|
||||
yield return new object[] { new TestCase(new(0, 120), new(160, 120), new(0, 0, 1600, 1200), new(0, 1200)) };
|
||||
yield return new object[] { new TestCase(new(160, 120), new(160, 120), new(0, 0, 1600, 1200), new(1600, 1200)) };
|
||||
yield return new object[] { new TestCase(new(80, 60), new(160, 120), new(0, 0, 1600, 1200), new(800, 600)) };
|
||||
|
||||
// screen corners and midpoint with a positive origin
|
||||
yield return new object[] { new TestCase(new(0, 0), new(160, 120), new(1000, 1000, 1600, 1200), new(1000, 1000)) };
|
||||
yield return new object[] { new TestCase(new(160, 0), new(160, 120), new(1000, 1000, 1600, 1200), new(2600, 1000)) };
|
||||
yield return new object[] { new TestCase(new(0, 120), new(160, 120), new(1000, 1000, 1600, 1200), new(1000, 2200)) };
|
||||
yield return new object[] { new TestCase(new(160, 120), new(160, 120), new(1000, 1000, 1600, 1200), new(2600, 2200)) };
|
||||
yield return new object[] { new TestCase(new(80, 60), new(160, 120), new(1000, 1000, 1600, 1200), new(1800, 1600)) };
|
||||
|
||||
// screen corners and midpoint with a negative origin
|
||||
yield return new object[] { new TestCase(new(0, 0), new(160, 120), new(-1000, -1000, 1600, 1200), new(-1000, -1000)) };
|
||||
yield return new object[] { new TestCase(new(160, 0), new(160, 120), new(-1000, -1000, 1600, 1200), new(600, -1000)) };
|
||||
yield return new object[] { new TestCase(new(0, 120), new(160, 120), new(-1000, -1000, 1600, 1200), new(-1000, 200)) };
|
||||
yield return new object[] { new TestCase(new(160, 120), new(160, 120), new(-1000, -1000, 1600, 1200), new(600, 200)) };
|
||||
yield return new object[] { new TestCase(new(80, 60), new(160, 120), new(-1000, -1000, 1600, 1200), new(-200, -400)) };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
var actual = MouseHelper.GetJumpLocation(
|
||||
data.PreviewLocation,
|
||||
data.PreviewSize,
|
||||
data.DesktopBounds);
|
||||
var expected = data.ExpectedResult;
|
||||
Assert.AreEqual(expected.X, actual.X);
|
||||
Assert.AreEqual(expected.Y, actual.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user