mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-30 00:46:20 +01:00
Compare commits
37 Commits
dev/seraph
...
yuleng/cmd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a19fc93346 | ||
|
|
85bdeeaacf | ||
|
|
f1bda8d71f | ||
|
|
21aa49cefb | ||
|
|
293fa262bb | ||
|
|
578d99f3b3 | ||
|
|
badb029bcf | ||
|
|
bec6754aa3 | ||
|
|
d986592737 | ||
|
|
662f04ed34 | ||
|
|
03bc72c436 | ||
|
|
5c8aa67781 | ||
|
|
b32c04fca1 | ||
|
|
4cb72ee126 | ||
|
|
55f8f3a53e | ||
|
|
a7994402fe | ||
|
|
41472a483c | ||
|
|
94e8559796 | ||
|
|
fe53a9c89a | ||
|
|
a708a3afaa | ||
|
|
b7f99e88ef | ||
|
|
6f43aac26a | ||
|
|
d7e826d2ac | ||
|
|
9e8754a592 | ||
|
|
49687251d3 | ||
|
|
f72ccd12fd | ||
|
|
4b44858a48 | ||
|
|
53ae118e72 | ||
|
|
31df702704 | ||
|
|
ea2542b235 | ||
|
|
c6776d0d45 | ||
|
|
d48286a3eb | ||
|
|
7a5b25cd3e | ||
|
|
1eccbc3021 | ||
|
|
ce620e427f | ||
|
|
721c84d3a6 | ||
|
|
96ba445cfa |
@@ -6,12 +6,16 @@ properties:
|
||||
directives:
|
||||
description: Enable Developer Mode
|
||||
allowPrerelease: true
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
Ensure: Present
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: vsPackage
|
||||
directives:
|
||||
description: Install Visual Studio 2022 Enterprise (Any edition will work)
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
id: Microsoft.VisualStudio.2022.Enterprise
|
||||
source: winget
|
||||
@@ -21,6 +25,8 @@ properties:
|
||||
directives:
|
||||
description: Install required VS workloads
|
||||
allowPrerelease: true
|
||||
# Requires elevation for the get and set operations
|
||||
securityContext: elevated
|
||||
settings:
|
||||
productId: Microsoft.VisualStudio.Product.Enterprise
|
||||
channelId: VisualStudio.17.Release
|
||||
|
||||
@@ -6,12 +6,16 @@ properties:
|
||||
directives:
|
||||
description: Enable Developer Mode
|
||||
allowPrerelease: true
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
Ensure: Present
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: vsPackage
|
||||
directives:
|
||||
description: Install Visual Studio 2022 Professional (Any edition will work)
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
id: Microsoft.VisualStudio.2022.Professional
|
||||
source: winget
|
||||
@@ -21,6 +25,8 @@ properties:
|
||||
directives:
|
||||
description: Install required VS workloads
|
||||
allowPrerelease: true
|
||||
# Requires elevation for the get and set operations
|
||||
securityContext: elevated
|
||||
settings:
|
||||
productId: Microsoft.VisualStudio.Product.Professional
|
||||
channelId: VisualStudio.17.Release
|
||||
|
||||
@@ -6,12 +6,16 @@ properties:
|
||||
directives:
|
||||
description: Enable Developer Mode
|
||||
allowPrerelease: true
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
Ensure: Present
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: vsPackage
|
||||
directives:
|
||||
description: Install Visual Studio 2022 Community (Any edition will work)
|
||||
# Requires elevation for the set operation
|
||||
securityContext: elevated
|
||||
settings:
|
||||
id: Microsoft.VisualStudio.2022.Community
|
||||
source: winget
|
||||
@@ -21,6 +25,8 @@ properties:
|
||||
directives:
|
||||
description: Install required VS workloads
|
||||
allowPrerelease: true
|
||||
# Requires elevation for the get and set operations
|
||||
securityContext: elevated
|
||||
settings:
|
||||
productId: Microsoft.VisualStudio.Product.Community
|
||||
channelId: VisualStudio.17.Release
|
||||
|
||||
33
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
33
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,6 +1,5 @@
|
||||
name: "🕷️ Bug report"
|
||||
description: Report errors or unexpected behavior
|
||||
type: Bug
|
||||
labels:
|
||||
- Issue-Bug
|
||||
- Needs-Triage
|
||||
@@ -8,15 +7,17 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Please make sure to [search for existing issues](https://github.com/microsoft/PowerToys/issues) before filing a new one!
|
||||
- type: input
|
||||
- id: version
|
||||
type: input
|
||||
attributes:
|
||||
label: Microsoft PowerToys version
|
||||
placeholder: 0.70.0
|
||||
placeholder: X.XX.X
|
||||
description: Hover over system tray icon or look at Settings
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
- id: installed
|
||||
type: dropdown
|
||||
attributes:
|
||||
label: Installation method
|
||||
description: How / Where was PowerToys installed from?
|
||||
@@ -33,14 +34,6 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Running as admin
|
||||
description: Are you running PowerToys as Admin?
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Area(s) with issue?
|
||||
@@ -67,7 +60,7 @@ body:
|
||||
- Keyboard Manager
|
||||
- Mouse Utilities
|
||||
- Mouse Without Borders
|
||||
- New+
|
||||
- New+
|
||||
- Peek
|
||||
- PowerRename
|
||||
- PowerToys Run
|
||||
@@ -106,6 +99,19 @@ body:
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- id: additionalInfo
|
||||
type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
placeholder: |
|
||||
OS version
|
||||
.Net version
|
||||
System Language
|
||||
User or System Installation
|
||||
Running as admin
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Other Software
|
||||
@@ -116,3 +122,4 @@ body:
|
||||
My Cool Application v0.3 (include a code snippet if it would help!)
|
||||
validations:
|
||||
required: false
|
||||
|
||||
|
||||
2
.github/actions/spell-check/allow/names.txt
vendored
2
.github/actions/spell-check/allow/names.txt
vendored
@@ -110,6 +110,7 @@ Lambson
|
||||
Laute
|
||||
laviusmotileng
|
||||
Leilei
|
||||
Loewen
|
||||
Luecking
|
||||
Mahalingam
|
||||
Markovic
|
||||
@@ -187,6 +188,7 @@ zhaoqpcn
|
||||
Zoltan
|
||||
Zykova
|
||||
Sameerjs
|
||||
ruslanlap
|
||||
|
||||
# OTHERS
|
||||
|
||||
|
||||
62
.github/actions/spell-check/expect.txt
vendored
62
.github/actions/spell-check/expect.txt
vendored
@@ -15,6 +15,7 @@ ACTIVATEAPP
|
||||
activationaction
|
||||
ACVS
|
||||
adaptivecards
|
||||
ADate
|
||||
ADDSTRING
|
||||
ADDUNDORECORD
|
||||
ADifferent
|
||||
@@ -55,12 +56,10 @@ APIIs
|
||||
Apm
|
||||
APPBARDATA
|
||||
APPEXECLINK
|
||||
APPICONREFERENCE
|
||||
APPLICATIONFRAMEHOST
|
||||
appmanifest
|
||||
APPMODEL
|
||||
APPNAME
|
||||
APPPUBLISHER
|
||||
appref
|
||||
appsettings
|
||||
appwindow
|
||||
@@ -172,7 +171,6 @@ CCHFORMNAME
|
||||
CCom
|
||||
CContext
|
||||
CDeclaration
|
||||
cdn
|
||||
CElems
|
||||
CENTERALIGN
|
||||
certlm
|
||||
@@ -199,8 +197,7 @@ CLIPCHILDREN
|
||||
CLIPSIBLINGS
|
||||
closesocket
|
||||
CLSCTX
|
||||
CLSIDs
|
||||
Clsids
|
||||
clsids
|
||||
Clusion
|
||||
cmder
|
||||
CMDNOTFOUNDMODULEINTERFACE
|
||||
@@ -254,8 +251,6 @@ createdump
|
||||
CREATEPROCESS
|
||||
CREATESCHEDULEDTASK
|
||||
CREATESTRUCT
|
||||
CREATETHREAD
|
||||
CREATEWINDOW
|
||||
CREATEWINDOWFAILED
|
||||
CRECT
|
||||
CRH
|
||||
@@ -308,11 +303,7 @@ DCOM
|
||||
DComposition
|
||||
DCR
|
||||
ddd
|
||||
DDEAPPLICATION
|
||||
DDECOMMAND
|
||||
DDEIf
|
||||
DDEIFEXEC
|
||||
DDETOPIC
|
||||
DDevice
|
||||
DDxgi
|
||||
Deact
|
||||
@@ -326,16 +317,13 @@ DEFAULTFLAGS
|
||||
DEFAULTICON
|
||||
defaultlib
|
||||
DEFAULTONLY
|
||||
DEFAULTTOFOLDER
|
||||
DEFAULTTONEAREST
|
||||
DEFAULTTONULL
|
||||
DEFAULTTOPRIMARY
|
||||
DEFAULTTOSTAR
|
||||
DEFERERASE
|
||||
DEFPUSHBUTTON
|
||||
deinitialization
|
||||
DELA
|
||||
DELEGATEEXECUTE
|
||||
DELETEDKEYIMAGE
|
||||
DELETESCANS
|
||||
deletethis
|
||||
@@ -389,7 +377,6 @@ dreamsofameaningfullife
|
||||
drivedetectionwarning
|
||||
Droid
|
||||
DROPFILES
|
||||
DROPTARGET
|
||||
DSTINVERT
|
||||
DSurface
|
||||
DTexture
|
||||
@@ -417,6 +404,7 @@ DWORDLONG
|
||||
dworigin
|
||||
dwrite
|
||||
dxgi
|
||||
eab
|
||||
easeofaccess
|
||||
ecount
|
||||
Edid
|
||||
@@ -466,6 +454,7 @@ EXAND
|
||||
EXCLUDEFROMCAPTURE
|
||||
executionpolicy
|
||||
exename
|
||||
exf
|
||||
EXITSIZEMOVE
|
||||
exlist
|
||||
EXPCMDFLAGS
|
||||
@@ -508,12 +497,10 @@ Fira
|
||||
FIXEDFILEINFO
|
||||
FIXEDSYS
|
||||
flac
|
||||
flaticon
|
||||
flyouts
|
||||
FMask
|
||||
fmtid
|
||||
FOF
|
||||
WANTNUKEWARNING
|
||||
FOFX
|
||||
FOLDERID
|
||||
folderpath
|
||||
@@ -523,7 +510,6 @@ FORCEMINIMIZE
|
||||
FORMATDLGORD
|
||||
formatetc
|
||||
FORPARSING
|
||||
fpvm
|
||||
Fqc
|
||||
FRAMECHANGED
|
||||
frm
|
||||
@@ -557,7 +543,6 @@ GETSECKEY
|
||||
GETSTICKYKEYS
|
||||
GETTEXTLENGTH
|
||||
GHND
|
||||
gifv
|
||||
GMEM
|
||||
GNumber
|
||||
gpedit
|
||||
@@ -651,7 +636,6 @@ HROW
|
||||
hsb
|
||||
HSCROLL
|
||||
hsi
|
||||
HSSH
|
||||
HTCLIENT
|
||||
hthumbnail
|
||||
HTOUCHINPUT
|
||||
@@ -689,7 +673,6 @@ iextn
|
||||
IFACEMETHOD
|
||||
IFACEMETHODIMP
|
||||
IFile
|
||||
IGNOREBASECLASS
|
||||
IGNOREUNKNOWN
|
||||
IGo
|
||||
iid
|
||||
@@ -738,8 +721,6 @@ Inste
|
||||
Interlop
|
||||
INTRESOURCE
|
||||
INVALIDARG
|
||||
INVALIDCALL
|
||||
INVALIDINDEX
|
||||
invalidoperatioexception
|
||||
ipcmanager
|
||||
IPREVIEW
|
||||
@@ -801,7 +782,6 @@ LEVELID
|
||||
LExit
|
||||
lhwnd
|
||||
LIBID
|
||||
libraryincludes
|
||||
LIMITSIZE
|
||||
LIMITTEXT
|
||||
lindex
|
||||
@@ -898,11 +878,11 @@ MARKDOWNPREVIEWHANDLERCPP
|
||||
MAXIMIZEBOX
|
||||
MAXSHORTCUTSIZE
|
||||
maxversiontested
|
||||
mber
|
||||
MBM
|
||||
MBR
|
||||
MDICHILD
|
||||
MDL
|
||||
mdpvm
|
||||
mdtext
|
||||
mdtxt
|
||||
mdwn
|
||||
@@ -1050,7 +1030,6 @@ NOCRLF
|
||||
nodeca
|
||||
NODRAWCAPTION
|
||||
NODRAWICON
|
||||
NOFIXUPS
|
||||
NOINHERITLAYOUT
|
||||
NOINTERFACE
|
||||
NOINVERT
|
||||
@@ -1066,13 +1045,11 @@ NONELEVATED
|
||||
NONINFRINGEMENT
|
||||
nonspace
|
||||
nonstd
|
||||
NOOPEN
|
||||
NOOWNERZORDER
|
||||
NOPARENTNOTIFY
|
||||
NOPREFIX
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREDRAW
|
||||
NOREMAPCLSID
|
||||
NOREMOVE
|
||||
norename
|
||||
NOREPEAT
|
||||
@@ -1091,11 +1068,9 @@ NOTIFYICONDATAW
|
||||
NOTIMPL
|
||||
NOTOPMOST
|
||||
NOTRACK
|
||||
NOTRUNCATE
|
||||
NOTSRCCOPY
|
||||
NOTSRCERASE
|
||||
NOTXORPEN
|
||||
NOUSERSETTINGS
|
||||
NOZORDER
|
||||
NPH
|
||||
npmjs
|
||||
@@ -1221,6 +1196,7 @@ Podcasts
|
||||
POINTERID
|
||||
POINTERUPDATE
|
||||
Pokedex
|
||||
Pomodoro
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
POSITIONITEM
|
||||
@@ -1315,7 +1291,6 @@ Quarternary
|
||||
QUERYENDSESSION
|
||||
QUERYOPEN
|
||||
QUEUESYNC
|
||||
QUICKTIP
|
||||
QUNS
|
||||
QXZ
|
||||
RAII
|
||||
@@ -1344,7 +1319,6 @@ REFCLSID
|
||||
REFIID
|
||||
REGCLS
|
||||
regfile
|
||||
REGISTERCLASSEX
|
||||
REGISTERCLASSFAILED
|
||||
REGISTRYHEADER
|
||||
registrypath
|
||||
@@ -1356,7 +1330,6 @@ REINSTALLMODE
|
||||
reloadable
|
||||
Relogger
|
||||
remappings
|
||||
REMAPRUNDLL
|
||||
REMAPSUCCESSFUL
|
||||
REMAPUNSUCCESSFUL
|
||||
Remotable
|
||||
@@ -1433,7 +1406,6 @@ SDDL
|
||||
SDKDDK
|
||||
sdns
|
||||
searchterm
|
||||
searchtext
|
||||
SEARCHUI
|
||||
SECONDARYDISPLAY
|
||||
secpol
|
||||
@@ -1481,8 +1453,6 @@ SHELLDLL
|
||||
shellex
|
||||
SHELLEXECUTEINFO
|
||||
SHELLEXECUTEINFOW
|
||||
SHELLEXTENSION
|
||||
SHELLNEWVALUE
|
||||
SHFILEINFO
|
||||
SHFILEOPSTRUCT
|
||||
SHGDN
|
||||
@@ -1492,7 +1462,6 @@ SHGFIICON
|
||||
SHGFILARGEICON
|
||||
shinfo
|
||||
shlwapi
|
||||
SHNAMEMAPPING
|
||||
shobjidl
|
||||
SHORTCUTATLEAST
|
||||
SHORTCUTMAXONEACTIONKEY
|
||||
@@ -1543,7 +1512,6 @@ SNAPPROCESS
|
||||
snwprintf
|
||||
softline
|
||||
SOURCECLIENTAREAONLY
|
||||
sourced
|
||||
sourcedoc
|
||||
SOURCEHEADER
|
||||
sourcesdirectory
|
||||
@@ -1682,7 +1650,6 @@ THotkey
|
||||
throughs
|
||||
TIcon
|
||||
TILEDWINDOW
|
||||
TILEINFO
|
||||
TILLSON
|
||||
timedate
|
||||
timediff
|
||||
@@ -1696,9 +1663,6 @@ TLayout
|
||||
tlb
|
||||
tlbimp
|
||||
tlc
|
||||
TPMLEFTALIGN
|
||||
TPMRETURNCMD
|
||||
TMPVAR
|
||||
TNP
|
||||
Toolhelp
|
||||
toolkitconverters
|
||||
@@ -1706,6 +1670,8 @@ toolwindow
|
||||
TOPDOWNDIB
|
||||
TOUCHEVENTF
|
||||
TOUCHINPUT
|
||||
TPMLEFTALIGN
|
||||
TPMRETURNCMD
|
||||
TRACEHANDLE
|
||||
tracelogging
|
||||
tracerpt
|
||||
@@ -1779,6 +1745,7 @@ USRDLL
|
||||
UType
|
||||
uuidv
|
||||
uwp
|
||||
uxt
|
||||
uxtheme
|
||||
vabdq
|
||||
validmodulename
|
||||
@@ -1804,7 +1771,6 @@ VERTSIZE
|
||||
VFT
|
||||
vget
|
||||
vgetq
|
||||
videourl
|
||||
viewmodel
|
||||
VIRTKEY
|
||||
VIRTUALDESK
|
||||
@@ -1838,7 +1804,7 @@ vstprintf
|
||||
VSTT
|
||||
vswhere
|
||||
Vtbl
|
||||
WANTMAPPINGHANDLE
|
||||
WANTNUKEWARNING
|
||||
WANTPALM
|
||||
wasdk
|
||||
wbem
|
||||
@@ -1859,10 +1825,10 @@ webbrowsers
|
||||
webpage
|
||||
websites
|
||||
wekyb
|
||||
wft
|
||||
wgpocpl
|
||||
WHEREID
|
||||
Wholegrain
|
||||
WIC
|
||||
wic
|
||||
wifi
|
||||
wil
|
||||
@@ -1919,6 +1885,7 @@ WNDCLASSEXW
|
||||
WNDCLASSW
|
||||
WNDPROC
|
||||
wnode
|
||||
wom
|
||||
WORKSPACESEDITOR
|
||||
WORKSPACESLAUNCHER
|
||||
WORKSPACESSNAPSHOTTOOL
|
||||
@@ -1952,7 +1919,6 @@ WUX
|
||||
Wwanpp
|
||||
XAxis
|
||||
xclip
|
||||
xdoc
|
||||
XDocument
|
||||
XElement
|
||||
xfd
|
||||
@@ -1975,13 +1941,13 @@ Yeet
|
||||
YIncrement
|
||||
yinle
|
||||
yinyue
|
||||
youtube
|
||||
YPels
|
||||
YResolution
|
||||
YStr
|
||||
YTM
|
||||
YVIRTUALSCREEN
|
||||
ZEROINIT
|
||||
Zhiwei
|
||||
zonable
|
||||
zoneset
|
||||
Zoneszonabletester
|
||||
@@ -1990,4 +1956,4 @@ zoomit
|
||||
ZOOMITX
|
||||
ZXk
|
||||
ZXNs
|
||||
zzz
|
||||
zzz
|
||||
|
||||
5
.github/workflows/msstore-submissions.yml
vendored
5
.github/workflows/msstore-submissions.yml
vendored
@@ -39,6 +39,11 @@ jobs:
|
||||
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
|
||||
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
- uses: microsoft/setup-msstore-cli@v1
|
||||
|
||||
- name: Fetch Store Credential
|
||||
|
||||
23
COMMUNITY.md
23
COMMUNITY.md
@@ -16,7 +16,7 @@ Christian contributed New+ utility
|
||||
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
|
||||
|
||||
### [@plante-msft](https://github.com/plante-msft) - Connor Plante
|
||||
Connor was the creator of Workspaces and helped create PowerToys Run v2
|
||||
Connor was the creator of Workspaces and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
|
||||
Damien has helped out by developing and contributing the Quick Accent utility.
|
||||
@@ -46,7 +46,7 @@ Jeff added in multiple new features into Keyboard manager, such as key chord sup
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
### [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie
|
||||
Jordi helped innovate amazing new features into Advanced Paste and helped create PowerToys Run v2
|
||||
Jordi helped innovate amazing new features into Advanced Paste and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/)
|
||||
Helping keep our spelling correct :)
|
||||
@@ -57,6 +57,9 @@ 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.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
|
||||
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
|
||||
|
||||
@@ -184,15 +187,11 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
|
||||
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
|
||||
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
|
||||
- [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager
|
||||
- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
|
||||
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead
|
||||
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
|
||||
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
|
||||
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
|
||||
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
|
||||
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
|
||||
@@ -206,7 +205,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
|
||||
|
||||
# Former PowerToys core team members
|
||||
## Former PowerToys core team members
|
||||
|
||||
- [@indierawk2k2](https://github.com/indierawk2k2) - Mike Harsh - Product Manager
|
||||
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
|
||||
@@ -219,3 +218,9 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev
|
||||
- [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev
|
||||
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
|
||||
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
|
||||
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
|
||||
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
|
||||
|
||||
@@ -29,24 +29,24 @@
|
||||
<PackageVersion Include="Mages" Version="3.0.0" />
|
||||
<PackageVersion Include="Markdig.Signed" Version="0.34.0" />
|
||||
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
|
||||
<PackageVersion Include="MessagePack" Version="2.5.187" />
|
||||
<PackageVersion Include="MessagePack" Version="3.1.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
||||
<!-- 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="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.120-preview" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.4" />
|
||||
<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. -->
|
||||
<!--
|
||||
@@ -61,7 +61,7 @@
|
||||
<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" Version="3.6.3" />
|
||||
<PackageVersion Include="MSTest" Version="3.8.3" />
|
||||
<PackageVersion Include="NLog" Version="5.0.4" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
|
||||
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
|
||||
@@ -69,29 +69,32 @@
|
||||
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
|
||||
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
|
||||
<PackageVersion Include="SharpCompress" Version="0.37.2" />
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.19.27" />
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<!-- Package System.CodeDom 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.Management but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.3" />
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.4" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="9.0.3" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="9.0.4" />
|
||||
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
|
||||
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
|
||||
<!-- Package System.Diagnostics.EventLog 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.EventLog" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.4" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.3" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.4" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.3" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.4" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
<PackageVersion Include="UnitsNet" Version="5.56.0" />
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
|
||||
|
||||
57
NOTICE.md
57
NOTICE.md
@@ -1393,7 +1393,6 @@ SOFTWARE.
|
||||
|
||||
## NuGet Packages used by PowerToys
|
||||
|
||||
|
||||
- AdaptiveCards.ObjectModel.WinUI3 2.0.0-beta
|
||||
- AdaptiveCards.Rendering.WinUI3 2.1.0-beta
|
||||
- AdaptiveCards.Templating 2.0.2
|
||||
@@ -1419,23 +1418,23 @@ SOFTWARE.
|
||||
- LazyCache 2.4.0
|
||||
- Mages 3.0.0
|
||||
- Markdig.Signed 0.34.0
|
||||
- MessagePack 2.5.187
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.3
|
||||
- MessagePack 3.1.3
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.4
|
||||
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
|
||||
- Microsoft.Data.Sqlite 9.0.3
|
||||
- Microsoft.Data.Sqlite 9.0.4
|
||||
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
|
||||
- Microsoft.DotNet.ILCompiler (A)
|
||||
- Microsoft.Extensions.DependencyInjection 9.0.3
|
||||
- Microsoft.Extensions.Hosting 9.0.3
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 9.0.3
|
||||
- Microsoft.Extensions.Logging 9.0.3
|
||||
- Microsoft.Extensions.Logging.Abstractions 9.0.3
|
||||
- Microsoft.Extensions.DependencyInjection 9.0.4
|
||||
- Microsoft.Extensions.Hosting 9.0.4
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 9.0.4
|
||||
- Microsoft.Extensions.Logging 9.0.4
|
||||
- Microsoft.Extensions.Logging.Abstractions 9.0.4
|
||||
- Microsoft.NET.ILLink.Tasks (A)
|
||||
- Microsoft.SemanticKernel 1.15.0
|
||||
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
|
||||
- Microsoft.Web.WebView2 1.0.2903.40
|
||||
- Microsoft.Win32.SystemEvents 9.0.3
|
||||
- Microsoft.Windows.Compatibility 9.0.3
|
||||
- Microsoft.Win32.SystemEvents 9.0.4
|
||||
- Microsoft.Windows.Compatibility 9.0.4
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.2.0
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
@@ -1445,35 +1444,39 @@ SOFTWARE.
|
||||
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
|
||||
- ModernWpfUI 0.9.4
|
||||
- Moq 4.18.4
|
||||
- MSTest 3.6.3
|
||||
- MSTest 3.8.3
|
||||
- NLog.Extensions.Logging 5.3.8
|
||||
- NLog.Schema 5.2.8
|
||||
- OpenAI 2.0.0
|
||||
- ReverseMarkdown 4.1.0
|
||||
- ScipBe.Common.Office.OneNote 3.0.1
|
||||
- SharpCompress 0.37.2
|
||||
- StreamJsonRpc 2.19.27
|
||||
- StreamJsonRpc 2.21.69
|
||||
- StyleCop.Analyzers 1.2.0-beta.556
|
||||
- System.CodeDom 9.0.3
|
||||
- System.CodeDom 9.0.4
|
||||
- System.CommandLine 2.0.0-beta4.22272.1
|
||||
- System.ComponentModel.Composition 9.0.3
|
||||
- System.Configuration.ConfigurationManager 9.0.3
|
||||
- System.Data.OleDb 9.0.3
|
||||
- System.ComponentModel.Composition 9.0.4
|
||||
- System.Configuration.ConfigurationManager 9.0.4
|
||||
- System.Data.OleDb 9.0.4
|
||||
- System.Data.SqlClient 4.8.6
|
||||
- System.Diagnostics.EventLog 9.0.3
|
||||
- System.Diagnostics.PerformanceCounter 9.0.3
|
||||
- System.Drawing.Common 9.0.3
|
||||
- System.IO.Abstractions 21.0.29
|
||||
- System.IO.Abstractions.TestingHelpers 21.0.29
|
||||
- System.Management 9.0.3
|
||||
- System.Diagnostics.EventLog 9.0.4
|
||||
- System.Diagnostics.PerformanceCounter 9.0.4
|
||||
- System.Drawing.Common 9.0.4
|
||||
- System.IO.Abstractions 22.0.13
|
||||
- System.IO.Abstractions.TestingHelpers 22.0.13
|
||||
- System.Management 9.0.4
|
||||
- System.Net.Http 4.3.4
|
||||
- System.Private.Uri 4.3.2
|
||||
- System.Reactive 6.0.1
|
||||
- System.Runtime.Caching 9.0.3
|
||||
- System.ServiceProcess.ServiceController 9.0.3
|
||||
- System.Text.Encoding.CodePages 9.0.3
|
||||
- System.Text.Json 9.0.3
|
||||
- System.Runtime.Caching 9.0.4
|
||||
- System.ServiceProcess.ServiceController 9.0.4
|
||||
- System.Text.Encoding.CodePages 9.0.4
|
||||
- System.Text.Json 9.0.4
|
||||
- System.Text.RegularExpressions 4.3.1
|
||||
- UnicodeInformation 2.6.0
|
||||
- UnitsNet 5.56.0
|
||||
- UTF.Unknown 2.5.1
|
||||
- WinUIEx 2.2.0
|
||||
- WPF-UI 3.0.5
|
||||
- WyHash 1.0.5
|
||||
|
||||
|
||||
148
README.md
148
README.md
@@ -11,14 +11,15 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
| | Current utilities: | |
|
||||
|--------------|--------------------|--------------|
|
||||
| [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) |
|
||||
| [New+](https://aka.ms/PowerToysOverview_NewPlus) | [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) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | [ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) |
|
||||
| [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | [Command Palette](https://aka.ms/PowerToysOverview_CmdPal) |
|
||||
| [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) | [New+](https://aka.ms/PowerToysOverview_NewPlus) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) |
|
||||
| [Peek](https://aka.ms/PowerToysOverview_Peek) | [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) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
|
||||
| [ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) |
|
||||
|
||||
## Installing and running Microsoft PowerToys
|
||||
|
||||
@@ -34,19 +35,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=is%3Aissue+milestone%3A%22PowerToys+0.90%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.89%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysUserSetup-0.89.0-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysUserSetup-0.89.0-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysSetup-0.89.0-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.89.0/PowerToysSetup-0.89.0-arm64.exe
|
||||
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.91%22
|
||||
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.90%22
|
||||
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.90.0-x64.exe
|
||||
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysUserSetup-0.90.0-arm64.exe
|
||||
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.90.0-x64.exe
|
||||
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.90.0/PowerToysSetup-0.90.0-arm64.exe
|
||||
|
||||
| Description | Filename | sha256 hash |
|
||||
|----------------|----------|-------------|
|
||||
| Per user - x64 | [PowerToysUserSetup-0.89.0-x64.exe][ptUserX64] | B4F130CC96F321024A257499247F6FF6DA56612215ED3882E868AAE26C689E33 |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.89.0-arm64.exe][ptUserArm64] | F69B00F4E520EB09FA0D1D1669E21910C5225FE7A2EEDC0FA7C283B201A5F9C6 |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.89.0-x64.exe][ptMachineX64] | E18AC8F9023E341CF7DAD35367FB9DDDB6565D83D8155DBCDDB40AE8A24AE731 |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.89.0-arm64.exe][ptMachineArm64] | 17DEADEC601D6061D7AF4F487595CC36D9191813003CC2ECE381017F0EC71FBB |
|
||||
| Per user - x64 | [PowerToysUserSetup-0.90.0-x64.exe][ptUserX64] | 2A6036F5B2D454084E55816C306E1E57EF1D14C916691CBDA42B469797605CE0 |
|
||||
| Per user - ARM64 | [PowerToysUserSetup-0.90.0-arm64.exe][ptUserArm64] | AB2E4DC87A9D764BE897C5170E2890E174C89CA912A1916FA3AE1E427536EA4A |
|
||||
| Machine wide - x64 | [PowerToysSetup-0.90.0-x64.exe][ptMachineX64] | 12801C44F43D0CC61E90DF1EFDC40E4F3C88341E0199D5B20791042D9B173DCF |
|
||||
| Machine wide - ARM64 | [PowerToysSetup-0.90.0-arm64.exe][ptMachineArm64] | 2998007C8FCD7BD2770767C6502AAA2CC75B85EC30DE62986EC7005EB0014EDB |
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
@@ -92,102 +93,95 @@ 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.89 - February 2025 Update
|
||||
### 0.90 - March 2025 Update
|
||||
|
||||
In this release, we focused on new features, stability, accessibility and automation.
|
||||
In this release, we focused on new features, stability, and automation.
|
||||
|
||||
**✨Highlights**
|
||||
|
||||
- Enhanced Advanced Paste by adding media transcoding support to convert different video and audio file formats! Thanks [@snickler](https://github.com/snickler) for your help!
|
||||
- Fixed crashes when loading thumbnails after the .NET 9 update and resolved PowerLauncher.exe blocking other MSI installers from creating shortcuts!
|
||||
- Fixed accessibility issues across FancyZones, Image Resizer, and Settings to improve screen reader support and clarity!
|
||||
- Enhanced UI automation framework across modules and added new tests to cover manual checks, with more improvements coming!
|
||||

|
||||
|
||||
### General
|
||||
- New module: Command Palette ("CmdPal") - Created as the evolution of PowerToys Run with extensibility at the forefront, Command Palette is a quick launcher with a richer display and additional capabilities without sacrificing performance, allowing you to start anything with the shortcut **Win+Alt+Space**! Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@michael-hawker](https://github.com/michael-hawker), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), [@ethanfangg](https://github.com/ethanfangg) and [@krschau](https://github.com/krschau)!
|
||||
- Enhanced the Color Picker by switching from WPF UI to .NET WPF, resulting in improved themes and visual consistency across different modes. Thanks [@mantaionut](https://github.com/mantaionut)! Thanks [@Jay-o-Way](https://github.com/Jay-o-Way) and [@niels9001](https://github.com/niels9001) for helping with the review!
|
||||
- Added the ability to delete files directly from Peek, enhancing file management efficiency. Thanks [@daverayment](https://github.com/daverayment) and thanks [@htcfreek](https://github.com/htcfreek) for the review!
|
||||
- Added support for variables in template filenames, enabling dynamic elements like date components and environment variables for enhanced customization in New+. Thanks [@cgaarden](https://github.com/cgaarden)!
|
||||
|
||||
- Fixed an issue where updating PowerToys on Windows 11 did not properly update context menu entries, impacting New+, PowerRename, Image Resizer, and File Locksmith.
|
||||
- Updated .NET Packages from 9.0.1 to 9.0.2. Thanks [@snickler](https://github.com/snickler) for this.
|
||||
- Enabled compatibility with VS17.3 and later, for C++23. Thanks [@LNKLEO](https://github.com/LNKLEO) for this.
|
||||
### Color Picker
|
||||
|
||||
### Advanced Paste
|
||||
- Replaced WPF UI with .NET WPF for the Color Picker, enhancing compatibility and improving theme support. Thanks [@mantaionut](https://github.com/mantaionut)! Thanks [@Jay-o-Way](https://github.com/Jay-o-Way) and [@niels9001](https://github.com/niels9001) for helping with the review!
|
||||
|
||||
- Added media transcoding support to convert different video and audio file formats, improved UI layouts, refined clipboard handling, and integrated Semantic Kernel for smarter pasting. Thanks [@snickler](https://github.com/snickler) for your help!
|
||||
### Command Palette
|
||||
|
||||
- Introduced the Windows Command Palette ("CmdPal"), the next iteration of PowerToys Run, designed with extensibility at its core. CmdPal includes features such as searching for installed apps, shell commands, files and WinGet package installation. This module aims to provide a more powerful and flexible launcher experience. Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@michael-hawker](https://github.com/michael-hawker), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), and the whole team!
|
||||
|
||||
### FancyZones
|
||||
|
||||
- Fixed accessibility by improving the text for monitors, ensuring clearer naming and help text for screen readers.
|
||||
- Fixed a bug where deleting a layout resulted in incorrect data being written to the JSON file.
|
||||
- Fixed a bug where layout hotkeys were displayed incorrectly, ensuring the hotkey list does not include invalid entries.
|
||||
- Fixed an issue where the "None" option was missing in the editor layout.
|
||||
|
||||
### Image Resizer
|
||||
- Fixed issues with Width and Height fields in Image Resizer's Custom preset, ensuring empty values no longer cause errors, settings save correctly, and auto-scaling behaves as expected. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed accessibility by ensuring screen readers announce selected image dimensions in the combo-box for better navigation.
|
||||
|
||||
### Monaco Preview
|
||||
|
||||
- Fixed open link in default browser rather than Microsoft Edge. Thanks [@OldUser101](https://github.com/OldUser101)!
|
||||
|
||||
### Mouse Highlighter
|
||||
|
||||
- Fixed a highlight released on an Administrator window will start fading, instead of staying on the screen indefinitely until the mouse button is pressed again on an unelevated window.
|
||||
- Fixed warnings in ImageResizer regarding the use of variables "shellItem" and "itemName" without being initialized.
|
||||
|
||||
### Mouse Without Borders
|
||||
- Fixed an issue in service mode where copy-paste and drag-drop file transfers didn’t work, ensuring seamless file operations.
|
||||
- Enabled GPO for enable/disable for Mouse Without Borders in Service Mode. Thanks [@htcfreek](https://github.com/htcfreek) for review and comments!
|
||||
- Fixed code maintainability by refactoring the oversized 'Common' class in Mouse Without Borders into smaller, focused classes for better structure and clarity. Thanks [@mikeclayton](https://github.com/mikeclayton) and thanks [@htcfreek](https://github.com/htcfreek) for review!
|
||||
|
||||
### PowerRename
|
||||
- Supported negative value as Start value in regular expression, e.g. ${start=-1314}
|
||||
- Enhanced RegEx help by adding $, ^, quantifiers, and common patterns for better usability. Thanks [@PesBandi](https://github.com/PesBandi) and thanks [@htcfreek](https://github.com/htcfreek) for review.
|
||||
- Enhanced the logger to properly track the file path for easier debugging.
|
||||
- Refactored the "Common" class into distinct individual classes to enhance maintainability, and updated all references and unit tests to reflect these changes. Thanks [@mikeclayton](https://github.com/mikeclayton) for this!
|
||||
|
||||
### New+
|
||||
|
||||
- Added support for variables in template filenames, including date/time components, parent folder name, and environment variables. Thanks [@cgaarden](https://github.com/cgaarden)!
|
||||
|
||||
### Peek
|
||||
|
||||
- Added the ability to delete the file currently being previewed in Peek, including navigation updates and handling for deleted items. Thanks [@daverayment](https://github.com/daverayment) and thanks [@htcfreek](https://github.com/htcfreek) for your help reviewing this!
|
||||
|
||||
### PowerToys Run
|
||||
- Fixed crashes when loading thumbnails after the .NET 9 update by disabling CETCompat.
|
||||
- Fixed PowerLauncher.exe blocking other MSI installers creating shortcuts. Thanks [@OneBlue](https://github.com/OneBlue)!
|
||||
- Fixed Run’s dark mode detection to work reliably, preventing issues with incorrect theme detection and ensuring a smoother user experience. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed list separator handling in Calculator, allowing functions with multiple arguments to work correctly across different locales. For example pow(2;3) would be replaced with pow(2,3). Thanks [@PesBandi](https://github.com/PesBandi) and thanks [@htcfreek](https://github.com/htcfreek) for review!
|
||||
- Fixed angle unit conversions in the PowerToys Run calculator, allowing quick conversions between radians, degrees, and gradians. Thanks [@OldUser101](https://github.com/OldUser101)!
|
||||
|
||||
### Quick Accent
|
||||
|
||||
- Added ǎ, ǒ and ǔ to the IPA character set. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
- Added ` (backtick) and ~ (tilde) to the VK_OEM_5 character set. Thanks [@xanatos](https://github.com/xanatos)!
|
||||
- Added ς (final sigma) to the Greek character set. Thanks [@IamSmeagol](https://github.com/IamSmeagol)!
|
||||
- Fixed an issue where duplicated applications were shown by ensuring the shell link helper opens .ink files non-exclusively and correctly retrieves the "FullPath". Thanks [@htcfreek](https://github.com/htcfreek) and [@davidegiacometti](https://github.com/davidegiacometti) for review!
|
||||
- Fixed an issue where applying round corners on Windows 11 build 22000 caused crashes.
|
||||
- Async the OnRename method to unblock the thread. Thanks [@davidegiacometti](https://github.com/davidegiacometti) for review!
|
||||
- Added support for using `sq` instead of `^2` in the Unit Converter. Thanks [@PesBandi](https://github.com/PesBandi)!
|
||||
|
||||
### Settings
|
||||
|
||||
- Enabled GPO for the "run at startup" setting. Thanks [@htcfreek](https://github.com/htcfreek) for review and comments!
|
||||
- Fixed accessibility issue by allowing screen readers to announce the group name for secondary links in Settings pages, instead of reading link descriptions without context.
|
||||
- Fixed an issue where the Color Picker shortcut was not displaying correctly in the Dashboard.
|
||||
- Disabled the spell check feature in the text boxes of plugin settings for PowerToys Run. Thanks [@htcfreek](https://github.com/htcfreek)!
|
||||
- Fixed an issue where InfoBars for release notes errors were not displayed properly, and added a retry button. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
|
||||
### Workspaces
|
||||
|
||||
- Fixed if a window was last placed on a disconnected monitor, it launches minimized and repositions within the main monitor's visible area when restored, instead of remaining off-screen and invisible.
|
||||
- Fixed on ARM64 to correctly display icons for packaged apps by resolving path mismatches.
|
||||
|
||||
### ZoomIt
|
||||
|
||||
- Fixed warning C4706 and related error C2220 during build. Thanks [@xanatos](https://github.com/xanatos)!
|
||||
- Fixed an issue where some minimized packaged apps (e.g., Microsoft ToDo, Settings) were not snapshotted.
|
||||
|
||||
### Documentation
|
||||
|
||||
- Fixed runner-ipc.md doc on the broken link. Thanks [@daverayment](https://github.com/daverayment)!
|
||||
- Fixed the new plugin checklist by updating the target framework, removing duplicates, and improving statement organization. Thanks [@hlaueriksson](https://github.com/hlaueriksson)!
|
||||
- Updated runner documentation to align with the latest code structure.
|
||||
- Added the FirefoxBookmark plugin to the list of Third-Party plugins for PowerToys Run. Thanks [@8LWXpg](https://github.com/8LWXpg)!
|
||||
- Added the SVGL third-party plugin to PowerToys Run, enabling users to search, browse, and copy SVG logos. Thanks [@SameerJS6](https://github.com/SameerJS6)!
|
||||
- Added Monaco usage for the Registry Preview.
|
||||
|
||||
### Development
|
||||
|
||||
- Stabilized pipeline on ARM64 and forked build.
|
||||
- Added fuzz testing for HostUILib, added as part of pipeline for OneFuzz.
|
||||
- Fixed and improved UI-Test automation framework, and added new test cases for the FancyZones and Hosts module.
|
||||
- Optimized Logger function as AOT compatible, improving performance by 18%.
|
||||
- Made Common.UI and Setting.UI to be AOT compatible.
|
||||
- Updated WinGet configuration file location and extension. Thanks [@mdanish-kh](https://github.com/mdanish-kh)!
|
||||
- Removed the Markdown file bypass to ensure CI runs for commits that only update Markdown files.
|
||||
- Fixed an issue where the default generated file path exceeded the length limit of 260 characters for EnvironmentVariablesUILib.csproj, causing build failures.
|
||||
- Upgraded WindowsAppSDK to 1.6.250205002 and CsWinRT to 2.2.0. Thanks [@htcfreek](https://github.com/htcfreek) for review!
|
||||
- Upgraded XamlStyler to 3.2501.8 and dotnet-consolidate to 4.2.0. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
|
||||
- Updated .NET Packages from 9.0.2 to 9.0.3.
|
||||
- Optimized the UI Test Automation Framework and added UI test cases for the Hosts File Editor module.
|
||||
- Added fuzz testing for RegistryPreview.
|
||||
- Added new UI tests for the FancyZones editor, including tests for creating, duplicating, editing, and deleting layouts.
|
||||
- Added telemetry code to measure the module editor open time and evaluate the benefits of applying AOT.
|
||||
|
||||
|
||||
|
||||
### What is being planned for version 0.90
|
||||
### What is being planned for version 0.91
|
||||
|
||||
For [v0.90][github-next-release-work], we'll work on the items below:
|
||||
For [v0.91][github-next-release-work], we'll work on the items below:
|
||||
|
||||
- New module: PowerToys Run v2
|
||||
- New module: File Actions Menu
|
||||
- New UI Automation tests
|
||||
- Working on installer upgrades
|
||||
- Upgrading keyboard manager's editor UI
|
||||
- Upgrading Keyboard Manager's editor UI
|
||||
- Stability / bug fixes
|
||||
|
||||
## PowerToys Community
|
||||
|
||||
@@ -88,4 +88,4 @@ namespace UITests_KeyboardManager
|
||||
## Extra tools and information
|
||||
|
||||
**Accessibility Tools**:
|
||||
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview)
|
||||
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview).
|
||||
|
||||
@@ -16,49 +16,70 @@ The 'Time and Date' plugin shows the date and time in different formats. For the
|
||||
### Available formats
|
||||
|
||||
**Remarks**
|
||||
- The following formats requires a prefix in the query:
|
||||
- The following formats requires a prefix in the query when using them as date input:
|
||||
- Unix Timestamp: `u`
|
||||
- Unix Timestamp in milliseconds: `ums`
|
||||
- Windows file time: `ft`
|
||||
- OLE Automation date: `oa`
|
||||
- Excel 1900 date value: `exc`
|
||||
- Excel 1904 date value: `exf`
|
||||
- On invalid number inputs we show a warning that tells the user which prefixes are allowed/required.
|
||||
|
||||
**List of available formats**
|
||||
|
||||
The following formats are currently available:
|
||||
|
||||
| Format | Example (Based on default settings) | As result | As input |
|
||||
| Format | Example (Based on default settings) | As result | As input | Result as custom format only
|
||||
|--------------|-----------|------------|------------|
|
||||
| Time | 5:10 PM | x | x |
|
||||
| Date | 3/5/2022 | x | x |
|
||||
| Now | 3/5/2022 5:10 PM | x | x |
|
||||
| Time UTC | 4:10 PM | x | x |
|
||||
| Now UTC | 3/5/2022 4:10 PM | x | x |
|
||||
| Unix Timestamp | 1646496622 | x | x |
|
||||
| Unix Timestamp in milliseconds | 1646496622500 | x | x |
|
||||
| Hour | 10 | x | |
|
||||
| Minute | 30 | x | |
|
||||
| Second | 45 | x | |
|
||||
| Millisecond | 678 | x | |
|
||||
| Day (Week day) | Saturday | x | |
|
||||
| Day of the week | 6 | x | |
|
||||
| Day of the month | 5 | x | |
|
||||
| Day of the year | 64 | x | |
|
||||
| Week of the month | 1 | x | |
|
||||
| Week of the year (Calendar week, Week number) | 10 | x | |
|
||||
| Month | March | x | |
|
||||
| Month of the year | 3 | x | |
|
||||
| Month and day | March 7 | x | x |
|
||||
| Year | 2022 | x | |
|
||||
| Era | AD | x | |
|
||||
| Era abbreviation | A | x | |
|
||||
| Month and year | March 2022 | x | x |
|
||||
| Windows file time (Int64 number) | 637820976123938199 | x | x |
|
||||
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x |
|
||||
| ISO 8601 | 2022-03-05T17:23:04 | x | x |
|
||||
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x |
|
||||
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x |
|
||||
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x |
|
||||
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x |
|
||||
| Time | 5:10 PM | x | x | |
|
||||
| Date | 3/5/2022 | x | x | |
|
||||
| Now | 3/5/2022 5:10 PM | x | x | |
|
||||
| Time UTC | 4:10 PM | x | x | |
|
||||
| Now UTC | 3/5/2022 4:10 PM | x | x | |
|
||||
| Unix Timestamp | 1646496622 | x | x | |
|
||||
| Unix Timestamp in milliseconds | 1646496622500 | x | x | |
|
||||
| Hour | 10 | x | | |
|
||||
| Minute | 30 | x | | |
|
||||
| Second | 45 | x | | |
|
||||
| Millisecond | 678 | x | | |
|
||||
| Day (Week day) | Saturday | x | | |
|
||||
| Day of the week | 6 | x | | |
|
||||
| Day of the month | 5 | x | | |
|
||||
| Day of the year | 64 | x | | |
|
||||
| Week of the month | 1 | x | | |
|
||||
| Week of the year (Calendar week, Week number) | 10 | x | | |
|
||||
| Month | March | x | | |
|
||||
| Month of the year | 3 | x | | |
|
||||
| Month and day | March 7 | x | x | |
|
||||
| Year | 2022 | x | | |
|
||||
| Era | AD | x | | |
|
||||
| Era abbreviation | A | x | | |
|
||||
| Month and year | March 2022 | x | x | |
|
||||
| Windows file time (Int64 number) | 637820976123938199 | x | x | |
|
||||
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x | |
|
||||
| ISO 8601 | 2022-03-05T17:23:04 | x | x | |
|
||||
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x | |
|
||||
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x | |
|
||||
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x | |
|
||||
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x | |
|
||||
| OLE Automation date | 45723.44143763889 | | x | x |
|
||||
| Excel's 1900 date value | 45723.44143763889 | | x | x |
|
||||
| Excel's 1904 date value | 44261.44143763889 | | x | x |
|
||||
|
||||
**Custom format definition**
|
||||
The user can create its own formats. One per line in the settings text box. The format of each line is `<name>=<syntax pattern>`.
|
||||
If the syntax pattern starting with `UTC:` then we use the UTC time instead of the local time.
|
||||
As syntax pattern the pattern from `DateTime.ToString()` and the following custom pattern are available:
|
||||
- DOW: Number of the day in the week.
|
||||
- WOM: Number of week in the month.
|
||||
- WOY: Number of the week in the year.
|
||||
- EAB: Era abbreviation.
|
||||
- WFT: Windows file time.
|
||||
- UXT: Unix time stamp.
|
||||
- UMS: Unix time stamp in milliseconds.
|
||||
- OAD: OLE Automation date.
|
||||
- EXC: Excel's 1900 based date value.
|
||||
- EXF: Excel's 1904 based date value.
|
||||
|
||||
### Add new formats
|
||||
- To add a new formats you have to add them to the method `GetList()` of the [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class.
|
||||
@@ -73,13 +94,13 @@ The following formats are currently available:
|
||||
|
||||
| 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 | |
|
||||
|
||||
| `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 | |
|
||||
| `CustomFormats` | Multiline text box | `string.Empty` | Custom formats | Use date and time string format syntax and DOW (Day of Week), WOM (Week of Month), WOY (Week of the year), EAB (Era abbreviation), WFT (Windows File Time), UXT (Unix Time), UMS (Unix Time in milliseconds), OAD (OLE Automation date), EXC (Excel's 1900 based date value), EXF (Excel's 1904 based date value). If the format starts with UTC:, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.) |
|
||||
|
||||
## Classes
|
||||
|
||||
|
||||
BIN
doc/images/overview/CmdPal_Hero.gif
Normal file
BIN
doc/images/overview/CmdPal_Hero.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 MiB |
@@ -43,6 +43,9 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS |
|
||||
| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes |
|
||||
| [SVGL](https://github.com/Sameerjs6/powertoys-svgl) | [SameerJS6](https://github.com/SameerJS6) | Search, Browse and copy SVG logos from SVGL. |
|
||||
| [QuickNotes](https://github.com/ruslanlap/CommunityPowerToysRunPlugin-QuickNotes) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and search notes directly from PowerToys Run. |
|
||||
| [Weather](https://github.com/ruslanlap/PowerToysRun-Weather) | [ruslanlap](https://github.com/ruslanlap) | Get real-time weather information directly from PowerToys Run. |
|
||||
| [Pomodoro](https://github.com/ruslanlap/PowerToysRun-Pomodoro) | [ruslanlap](https://github.com/ruslanlap) | Manage Pomodoro productivity sessions directly from PowerToys Run. |
|
||||
|
||||
## Extending software plugins
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\x64\Microsoft.WindowsAppRuntime.1.6.msix" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?else ?>
|
||||
@@ -50,7 +49,6 @@
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\arm64\Microsoft.WindowsAppRuntime.1.6.msix" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?endif ?>
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace AllExperiments
|
||||
}
|
||||
|
||||
// Using InvariantCulture since this is used for a log file name
|
||||
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
|
||||
var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
|
||||
|
||||
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace ManagedCommon
|
||||
Directory.CreateDirectory(applicationLogPath);
|
||||
}
|
||||
|
||||
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
|
||||
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log");
|
||||
|
||||
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
<PackageReference Include="Appium.WebDriver" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,49 +8,49 @@ struct LogSettings
|
||||
inline const static std::wstring logLevelOption = L"logLevel";
|
||||
inline const static std::string runnerLoggerName = "runner";
|
||||
inline const static std::wstring logPath = L"Logs\\";
|
||||
inline const static std::wstring runnerLogPath = L"RunnerLogs\\runner-log.txt";
|
||||
inline const static std::wstring runnerLogPath = L"RunnerLogs\\runner-log.log";
|
||||
inline const static std::string actionRunnerLoggerName = "action-runner";
|
||||
inline const static std::wstring actionRunnerLogPath = L"RunnerLogs\\action-runner-log.txt";
|
||||
inline const static std::wstring actionRunnerLogPath = L"RunnerLogs\\action-runner-log.log";
|
||||
inline const static std::string updateLoggerName = "update";
|
||||
inline const static std::wstring updateLogPath = L"UpdateLogs\\update-log.txt";
|
||||
inline const static std::wstring updateLogPath = L"UpdateLogs\\update-log.log";
|
||||
inline const static std::string fileExplorerLoggerName = "FileExplorer";
|
||||
inline const static std::wstring fileExplorerLogPath = L"Logs\\file-explorer-log.txt";
|
||||
inline const static std::wstring fileExplorerLogPath = L"Logs\\file-explorer-log.log";
|
||||
inline const static std::string gcodePrevLoggerName = "GcodePrevHandler";
|
||||
inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.txt";
|
||||
inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.log";
|
||||
inline const static std::string gcodeThumbLoggerName = "GcodeThumbnailProvider";
|
||||
inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.txt";
|
||||
inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.log";
|
||||
inline const static std::string mdPrevLoggerName = "MDPrevHandler";
|
||||
inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.txt";
|
||||
inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.log";
|
||||
inline const static std::string monacoPrevLoggerName = "MonacoPrevHandler";
|
||||
inline const static std::wstring monacoPrevLogPath = L"logs\\FileExplorer_localLow\\MonacoPrevHandler\\monaco-prev-handler-log.txt";
|
||||
inline const static std::wstring monacoPrevLogPath = L"logs\\FileExplorer_localLow\\MonacoPrevHandler\\monaco-prev-handler-log.log";
|
||||
inline const static std::string pdfPrevLoggerName = "PdfPrevHandler";
|
||||
inline const static std::wstring pdfPrevLogPath = L"logs\\FileExplorer_localLow\\PdfPrevHandler\\pdf-prev-handler-log.txt";
|
||||
inline const static std::wstring pdfPrevLogPath = L"logs\\FileExplorer_localLow\\PdfPrevHandler\\pdf-prev-handler-log.log";
|
||||
inline const static std::string pdfThumbLoggerName = "PdfThumbnailProvider";
|
||||
inline const static std::wstring pdfThumbLogPath = L"logs\\FileExplorer_localLow\\PdfThumbnailProvider\\pdf-thumbnail-provider-log.txt";
|
||||
inline const static std::wstring pdfThumbLogPath = L"logs\\FileExplorer_localLow\\PdfThumbnailProvider\\pdf-thumbnail-provider-log.log";
|
||||
inline const static std::string qoiPrevLoggerName = "QoiPrevHandler";
|
||||
inline const static std::wstring qoiPrevLogPath = L"logs\\FileExplorer_localLow\\QoiPreviewHandler\\qoi-prev-handler-log.txt";
|
||||
inline const static std::wstring qoiPrevLogPath = L"logs\\FileExplorer_localLow\\QoiPreviewHandler\\qoi-prev-handler-log.log";
|
||||
inline const static std::string qoiThumbLoggerName = "QoiThumbnailProvider";
|
||||
inline const static std::wstring qoiThumbLogPath = L"logs\\FileExplorer_localLow\\QoiThumbnailProvider\\qoi-thumbnail-provider-log.txt";
|
||||
inline const static std::wstring qoiThumbLogPath = L"logs\\FileExplorer_localLow\\QoiThumbnailProvider\\qoi-thumbnail-provider-log.log";
|
||||
inline const static std::string stlThumbLoggerName = "StlThumbnailProvider";
|
||||
inline const static std::wstring stlThumbLogPath = L"logs\\FileExplorer_localLow\\StlThumbnailProvider\\stl-thumbnail-provider-log.txt";
|
||||
inline const static std::wstring stlThumbLogPath = L"logs\\FileExplorer_localLow\\StlThumbnailProvider\\stl-thumbnail-provider-log.log";
|
||||
inline const static std::string svgPrevLoggerName = "SvgPrevHandler";
|
||||
inline const static std::wstring svgPrevLogPath = L"logs\\FileExplorer_localLow\\SvgPrevHandler\\svg-prev-handler-log.txt";
|
||||
inline const static std::wstring svgPrevLogPath = L"logs\\FileExplorer_localLow\\SvgPrevHandler\\svg-prev-handler-log.log";
|
||||
inline const static std::string svgThumbLoggerName = "SvgThumbnailProvider";
|
||||
inline const static std::wstring svgThumbLogPath = L"logs\\FileExplorer_localLow\\SvgThumbnailProvider\\svg-thumbnail-provider-log.txt";
|
||||
inline const static std::wstring svgThumbLogPath = L"logs\\FileExplorer_localLow\\SvgThumbnailProvider\\svg-thumbnail-provider-log.log";
|
||||
inline const static std::string launcherLoggerName = "launcher";
|
||||
inline const static std::wstring launcherLogPath = L"LogsModuleInterface\\launcher-log.txt";
|
||||
inline const static std::wstring launcherLogPath = L"LogsModuleInterface\\launcher-log.log";
|
||||
inline const static std::string mouseWithoutBordersLoggerName = "mouseWithoutBorders";
|
||||
inline const static std::wstring mouseWithoutBordersLogPath = L"LogsModuleInterface\\mouseWithoutBorders-log.txt";
|
||||
inline const static std::wstring awakeLogPath = L"Logs\\awake-log.txt";
|
||||
inline const static std::wstring powerAccentLogPath = L"quick-accent-log.txt";
|
||||
inline const static std::wstring mouseWithoutBordersLogPath = L"LogsModuleInterface\\mouseWithoutBorders-log.log";
|
||||
inline const static std::wstring awakeLogPath = L"Logs\\awake-log.log";
|
||||
inline const static std::wstring powerAccentLogPath = L"quick-accent-log.log";
|
||||
inline const static std::string fancyZonesLoggerName = "fancyzones";
|
||||
inline const static std::wstring fancyZonesLogPath = L"fancyzones-log.txt";
|
||||
inline const static std::wstring fancyZonesLogPath = L"fancyzones-log.log";
|
||||
inline const static std::wstring fancyZonesOldLogPath = L"FancyZonesLogs\\"; // needed to clean up old logs
|
||||
inline const static std::string shortcutGuideLoggerName = "shortcut-guide";
|
||||
inline const static std::wstring shortcutGuideLogPath = L"ShortcutGuideLogs\\shortcut-guide-log.txt";
|
||||
inline const static std::wstring powerOcrLogPath = L"Logs\\text-extractor-log.txt";
|
||||
inline const static std::wstring shortcutGuideLogPath = L"ShortcutGuideLogs\\shortcut-guide-log.log";
|
||||
inline const static std::wstring powerOcrLogPath = L"Logs\\text-extractor-log.log";
|
||||
inline const static std::string keyboardManagerLoggerName = "keyboard-manager";
|
||||
inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.txt";
|
||||
inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.log";
|
||||
inline const static std::string findMyMouseLoggerName = "find-my-mouse";
|
||||
inline const static std::string mouseHighlighterLoggerName = "mouse-highlighter";
|
||||
inline const static std::string mouseJumpLoggerName = "mouse-jump";
|
||||
@@ -60,22 +60,22 @@ struct LogSettings
|
||||
inline const static std::string alwaysOnTopLoggerName = "always-on-top";
|
||||
inline const static std::string powerOcrLoggerName = "TextExtractor";
|
||||
inline const static std::string fileLocksmithLoggerName = "FileLocksmith";
|
||||
inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt";
|
||||
inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.log";
|
||||
inline const static std::string hostsLoggerName = "hosts";
|
||||
inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt";
|
||||
inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.log";
|
||||
inline const static std::string registryPreviewLoggerName = "registrypreview";
|
||||
inline const static std::string cropAndLockLoggerName = "crop-and-lock";
|
||||
inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.txt";
|
||||
inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.log";
|
||||
inline const static std::string environmentVariablesLoggerName = "environment-variables";
|
||||
inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.txt";
|
||||
inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.log";
|
||||
inline const static std::string cmdNotFoundLoggerName = "cmd-not-found";
|
||||
inline const static std::string newLoggerName = "NewPlus";
|
||||
inline const static std::string workspacesLauncherLoggerName = "workspaces-launcher";
|
||||
inline const static std::wstring workspacesLauncherLogPath = L"workspaces-launcher-log.txt";
|
||||
inline const static std::wstring workspacesLauncherLogPath = L"workspaces-launcher-log.log";
|
||||
inline const static std::string workspacesWindowArrangerLoggerName = "workspaces-window-arranger";
|
||||
inline const static std::wstring workspacesWindowArrangerLogPath = L"workspaces-window-arranger-log.txt";
|
||||
inline const static std::wstring workspacesWindowArrangerLogPath = L"workspaces-window-arranger-log.log";
|
||||
inline const static std::string workspacesSnapshotToolLoggerName = "workspaces-snapshot-tool";
|
||||
inline const static std::wstring workspacesSnapshotToolLogPath = L"workspaces-snapshot-tool-log.txt";
|
||||
inline const static std::wstring workspacesSnapshotToolLogPath = L"workspaces-snapshot-tool-log.log";
|
||||
inline const static std::string zoomItLoggerName = "zoom-it";
|
||||
inline const static int retention = 30;
|
||||
std::wstring logLevel;
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace LoggerHelpers
|
||||
currentFolder.append(get_product_version());
|
||||
|
||||
auto logsPath = currentFolder;
|
||||
logsPath.append(L"log.txt");
|
||||
logsPath.append(L"log.log");
|
||||
Logger::init(loggerName, logsPath.wstring(), PTSettingsHelper::get_log_settings_file_location());
|
||||
|
||||
delete_other_versions_log_folders(rootFolder.wstring(), currentFolder);
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <appxpackaging.h>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <Shlwapi.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
@@ -15,11 +18,12 @@
|
||||
#include "../logger/logger.h"
|
||||
#include "../version/version.h"
|
||||
|
||||
namespace package {
|
||||
|
||||
namespace package
|
||||
{
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::Management::Deployment;
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
inline BOOL IsWin11OrGreater()
|
||||
{
|
||||
@@ -46,6 +50,118 @@ namespace package {
|
||||
dwlConditionMask);
|
||||
}
|
||||
|
||||
struct PACKAGE_VERSION
|
||||
{
|
||||
UINT16 Major;
|
||||
UINT16 Minor;
|
||||
UINT16 Build;
|
||||
UINT16 Revision;
|
||||
};
|
||||
|
||||
class ComInitializer
|
||||
{
|
||||
public:
|
||||
explicit ComInitializer(DWORD coInitFlags = COINIT_MULTITHREADED) :
|
||||
_initialized(false)
|
||||
{
|
||||
const HRESULT hr = CoInitializeEx(nullptr, coInitFlags);
|
||||
_initialized = SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
~ComInitializer()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool Succeeded() const { return _initialized; }
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
};
|
||||
|
||||
inline bool GetPackageNameAndVersionFromAppx(
|
||||
const std::wstring& appxPath,
|
||||
std::wstring& outName,
|
||||
PACKAGE_VERSION& outVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
ComInitializer comInit;
|
||||
if (!comInit.Succeeded())
|
||||
{
|
||||
Logger::error(L"COM initialization failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<IAppxFactory> factory;
|
||||
ComPtr<IStream> stream;
|
||||
ComPtr<IAppxPackageReader> reader;
|
||||
ComPtr<IAppxManifestReader> manifest;
|
||||
ComPtr<IAppxManifestPackageId> packageId;
|
||||
|
||||
HRESULT hr = CoCreateInstance(__uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = SHCreateStreamOnFileEx(appxPath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = factory->CreatePackageReader(stream.Get(), &reader);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = reader->GetManifest(&manifest);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = manifest->GetPackageId(&packageId);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
LPWSTR name = nullptr;
|
||||
hr = packageId->GetName(&name);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
UINT64 version = 0;
|
||||
hr = packageId->GetVersion(&version);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
outName = std::wstring(name);
|
||||
CoTaskMemFree(name);
|
||||
|
||||
outVersion.Major = static_cast<UINT16>((version >> 48) & 0xFFFF);
|
||||
outVersion.Minor = static_cast<UINT16>((version >> 32) & 0xFFFF);
|
||||
outVersion.Build = static_cast<UINT16>((version >> 16) & 0xFFFF);
|
||||
outVersion.Revision = static_cast<UINT16>(version & 0xFFFF);
|
||||
|
||||
Logger::info(L"Package name: {}, version: {}.{}.{}.{}, appxPath: {}",
|
||||
outName,
|
||||
outVersion.Major,
|
||||
outVersion.Minor,
|
||||
outVersion.Build,
|
||||
outVersion.Revision,
|
||||
appxPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(L"Standard exception: {}", winrt::to_hstring(ex.what()));
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Unknown or non-standard exception occurred.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<Package> GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
|
||||
{
|
||||
PackageManager packageManager;
|
||||
@@ -229,6 +345,59 @@ namespace package {
|
||||
return matchedFiles;
|
||||
}
|
||||
|
||||
inline bool IsPackageSatisfied(const std::wstring& appxPath)
|
||||
{
|
||||
std::wstring targetName;
|
||||
PACKAGE_VERSION targetVersion{};
|
||||
|
||||
if (!GetPackageNameAndVersionFromAppx(appxPath, targetName, targetVersion))
|
||||
{
|
||||
Logger::error(L"Failed to get package name and version from appx: " + appxPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageManager pm;
|
||||
|
||||
for (const auto& package : pm.FindPackagesForUser({}))
|
||||
{
|
||||
const auto& id = package.Id();
|
||||
if (std::wstring(id.Name()) == targetName)
|
||||
{
|
||||
const auto& version = id.Version();
|
||||
|
||||
if (version.Major > targetVersion.Major ||
|
||||
(version.Major == targetVersion.Major && version.Minor > targetVersion.Minor) ||
|
||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build > targetVersion.Build) ||
|
||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build == targetVersion.Build && version.Revision >= targetVersion.Revision))
|
||||
{
|
||||
Logger::info(
|
||||
L"Package {} is already satisfied with version {}.{}.{}.{}; target version {}.{}.{}.{}; appxPath: {}",
|
||||
id.Name(),
|
||||
version.Major,
|
||||
version.Minor,
|
||||
version.Build,
|
||||
version.Revision,
|
||||
targetVersion.Major,
|
||||
targetVersion.Minor,
|
||||
targetVersion.Build,
|
||||
targetVersion.Revision,
|
||||
appxPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info(
|
||||
L"Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}",
|
||||
targetName,
|
||||
targetVersion.Major,
|
||||
targetVersion.Minor,
|
||||
targetVersion.Build,
|
||||
targetVersion.Revision,
|
||||
appxPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool RegisterPackage(std::wstring pkgPath, std::vector<std::wstring> dependencies)
|
||||
{
|
||||
try
|
||||
@@ -238,7 +407,7 @@ namespace package {
|
||||
PackageManager packageManager;
|
||||
|
||||
// Declare use of an external location
|
||||
DeploymentOptions options = DeploymentOptions::ForceApplicationShutdown;
|
||||
DeploymentOptions options = DeploymentOptions::ForceTargetApplicationShutdown;
|
||||
|
||||
Collections::IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
|
||||
if (!dependencies.empty())
|
||||
@@ -247,7 +416,14 @@ namespace package {
|
||||
{
|
||||
try
|
||||
{
|
||||
uris.Append(Uri(dependency));
|
||||
if (IsPackageSatisfied(dependency))
|
||||
{
|
||||
Logger::info(L"Dependency already satisfied: {}", dependency);
|
||||
}
|
||||
else
|
||||
{
|
||||
uris.Append(Uri(dependency));
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& ex)
|
||||
{
|
||||
@@ -282,7 +458,6 @@ namespace package {
|
||||
{
|
||||
Logger::debug(L"Register {} package started.", pkgPath);
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
@@ -293,4 +468,4 @@ namespace package {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.CmdPal.Ext.TimeDate.Helpers;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -12,6 +14,12 @@ namespace Microsoft.CmdPal.Ext.TimeDate.Pages;
|
||||
|
||||
internal sealed partial class TimeDateExtensionPage : DynamicListPage
|
||||
{
|
||||
private readonly Lock _resultsLock = new();
|
||||
|
||||
private IList<ListItem> _results = new List<ListItem>();
|
||||
|
||||
private bool initialized;
|
||||
|
||||
private SettingsManager _settingsManager;
|
||||
|
||||
public TimeDateExtensionPage(SettingsManager settingsManager)
|
||||
@@ -24,20 +32,36 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => DoExecuteSearch(SearchText).ToArray();
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
DoExecuteSearch(string.Empty);
|
||||
}
|
||||
|
||||
lock (_resultsLock)
|
||||
{
|
||||
ListItem[] results = _results.ToArray();
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
if (newSearch == oldSearch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoExecuteSearch(newSearch);
|
||||
RaiseItemsChanged(0);
|
||||
}
|
||||
|
||||
private List<ListItem> DoExecuteSearch(string query)
|
||||
private void DoExecuteSearch(string query)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = TimeDateCalculator.ExecuteSearch(_settingsManager, query);
|
||||
return result;
|
||||
UpdateResult(result);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -51,7 +75,18 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
|
||||
ResultHelper.CreateInvalidInputErrorResult(),
|
||||
};
|
||||
|
||||
return items;
|
||||
UpdateResult(items);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateResult(IList<ListItem> result)
|
||||
{
|
||||
lock (_resultsLock)
|
||||
{
|
||||
initialized = true;
|
||||
this._results = result;
|
||||
}
|
||||
|
||||
RaiseItemsChanged(this._results.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch.Commands;
|
||||
|
||||
internal sealed partial class OpenURLCommand : InvokableCommand
|
||||
{
|
||||
private readonly SettingsManager _settingsManager;
|
||||
|
||||
public string Url { get; internal set; } = string.Empty;
|
||||
|
||||
internal OpenURLCommand(string url, SettingsManager settingsManager)
|
||||
{
|
||||
Url = url;
|
||||
BrowserInfo.UpdateIfTimePassed();
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
Name = string.Empty;
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
if (!ShellHelpers.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, $"{Url}"))
|
||||
{
|
||||
// TODO GH# 138 --> actually display feedback from the extension somewhere.
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Commands;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Helpers;
|
||||
using Microsoft.CmdPal.Ext.WebSearch.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WebSearch;
|
||||
|
||||
internal sealed partial class FallbackOpenURLItem : FallbackCommandItem
|
||||
{
|
||||
private readonly OpenURLCommand _executeItem;
|
||||
private static readonly CompositeFormat PluginOpenURL = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url);
|
||||
private static readonly CompositeFormat PluginOpenUrlInBrowser = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url_in_browser);
|
||||
|
||||
public FallbackOpenURLItem(SettingsManager settings)
|
||||
: base(new OpenURLCommand(string.Empty, settings), string.Empty)
|
||||
{
|
||||
_executeItem = (OpenURLCommand)this.Command!;
|
||||
Title = string.Empty;
|
||||
_executeItem.Name = string.Empty;
|
||||
Subtitle = string.Empty;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
|
||||
}
|
||||
|
||||
public override void UpdateQuery(string query)
|
||||
{
|
||||
if (!IsValidUrl(query))
|
||||
{
|
||||
Title = string.Empty;
|
||||
Subtitle = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
var success = Uri.TryCreate(query, UriKind.Absolute, out var uri);
|
||||
|
||||
// if url not contain schema, add http:// by default.
|
||||
if (!success)
|
||||
{
|
||||
query = "https://" + query;
|
||||
}
|
||||
|
||||
_executeItem.Url = query;
|
||||
_executeItem.Name = string.IsNullOrEmpty(query) ? string.Empty : Properties.Resources.open_in_default_browser;
|
||||
|
||||
Title = string.Format(CultureInfo.CurrentCulture, PluginOpenURL, query);
|
||||
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpenUrlInBrowser, BrowserInfo.Name ?? BrowserInfo.MSEdgeName);
|
||||
}
|
||||
|
||||
public static bool IsValidUrl(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!url.Contains('.', StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// eg: 'com', 'org'. We don't think it's a valid url.
|
||||
// This can simplify the logic of checking if the url is valid.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
|
||||
!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) &&
|
||||
!url.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase) &&
|
||||
!url.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Uri.IsWellFormedUriString("https://" + url, UriKind.Absolute))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -195,6 +195,24 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open "{0}".
|
||||
/// </summary>
|
||||
public static string plugin_open_url {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_open_url", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open url in {0}.
|
||||
/// </summary>
|
||||
public static string plugin_open_url_in_browser {
|
||||
get {
|
||||
return ResourceManager.GetString("plugin_open_url_in_browser", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to open {0}..
|
||||
/// </summary>
|
||||
|
||||
@@ -163,6 +163,12 @@
|
||||
<data name="plugin_open" xml:space="preserve">
|
||||
<value>Search the web in {0}</value>
|
||||
</data>
|
||||
<data name="plugin_open_url" xml:space="preserve">
|
||||
<value>Open "{0}"</value>
|
||||
</data>
|
||||
<data name="plugin_open_url_in_browser" xml:space="preserve">
|
||||
<value>Open url in {0}</value>
|
||||
</data>
|
||||
<data name="plugin_search_failed" xml:space="preserve">
|
||||
<value>Failed to open {0}.</value>
|
||||
</data>
|
||||
|
||||
@@ -14,6 +14,7 @@ public partial class WebSearchCommandsProvider : CommandProvider
|
||||
{
|
||||
private readonly SettingsManager _settingsManager = new();
|
||||
private readonly FallbackExecuteSearchItem _fallbackItem;
|
||||
private readonly FallbackOpenURLItem _openUrlFallbackItem;
|
||||
|
||||
public WebSearchCommandsProvider()
|
||||
{
|
||||
@@ -23,6 +24,7 @@ public partial class WebSearchCommandsProvider : CommandProvider
|
||||
Settings = _settingsManager.Settings;
|
||||
|
||||
_fallbackItem = new FallbackExecuteSearchItem(_settingsManager);
|
||||
_openUrlFallbackItem = new FallbackOpenURLItem(_settingsManager);
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands()
|
||||
@@ -36,5 +38,5 @@ public partial class WebSearchCommandsProvider : CommandProvider
|
||||
];
|
||||
}
|
||||
|
||||
public override IFallbackCommandItem[]? FallbackCommands() => [_fallbackItem];
|
||||
public override IFallbackCommandItem[]? FallbackCommands() => [_openUrlFallbackItem, _fallbackItem];
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@
|
||||
"Areas": [ "AreaEaseOfAccess" ],
|
||||
"Type": "AppSettingsApp",
|
||||
"AltNames": [ "TouchFeedback" ],
|
||||
"Command": "ms-settings:easeofaccess-MousePointer"
|
||||
"Command": "ms-settings:easeofaccess-mousepointer"
|
||||
},
|
||||
{
|
||||
"Name": "Display",
|
||||
|
||||
@@ -16,31 +16,21 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "List Page Sample Command",
|
||||
Subtitle = "Display a list of items",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new SampleListPageWithDetails())
|
||||
{
|
||||
Title = "List Page With Details",
|
||||
Subtitle = "A list of items, each with additional details to display",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new SampleUpdatingItemsPage())
|
||||
{
|
||||
Title = "List page with items that change",
|
||||
Subtitle = "The items on the list update themselves in real time",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new SampleDynamicListPage())
|
||||
{
|
||||
Title = "Dynamic List Page Command",
|
||||
Subtitle = "Changes the list of items in response to the typed query",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new FizzBuzzListPage())
|
||||
{
|
||||
Title = "Sections sample",
|
||||
Subtitle = "Changing list of items, with sections",
|
||||
Section = "Lists",
|
||||
},
|
||||
|
||||
// Content pages
|
||||
@@ -48,38 +38,32 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "Sample content page",
|
||||
Subtitle = "Display mixed forms, markdown, and other types of content",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleTreeContentPage())
|
||||
{
|
||||
Title = "Sample nested content",
|
||||
Subtitle = "Example of nesting a tree of content",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleCommentsPage())
|
||||
{
|
||||
Title = "Sample of nested comments",
|
||||
Subtitle = "Demo of using nested trees of content to create a comment thread-like experience",
|
||||
Icon = new IconInfo("\uE90A"), // Comment
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleMarkdownPage())
|
||||
{
|
||||
Title = "Markdown Page Sample Command",
|
||||
Subtitle = "Display a page of rendered markdown",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleMarkdownManyBodies())
|
||||
{
|
||||
Title = "Markdown with multiple blocks",
|
||||
Subtitle = "A page with multiple blocks of rendered markdown",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleMarkdownDetails())
|
||||
{
|
||||
Title = "Markdown with details",
|
||||
Subtitle = "A page with markdown and details",
|
||||
Section = "Content",
|
||||
},
|
||||
|
||||
// Settings helpers
|
||||
@@ -87,7 +71,6 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "Sample settings page",
|
||||
Subtitle = "A demo of the settings helpers",
|
||||
Section = "Settings",
|
||||
},
|
||||
|
||||
// Evil edge cases
|
||||
@@ -96,7 +79,6 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "Evil samples",
|
||||
Subtitle = "Samples designed to break the palette in many different evil ways",
|
||||
Section = "Debugging",
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -28,69 +29,69 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_page_title}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_title)}},
|
||||
"size": "large"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_page_text}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_text)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_name_header}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_header)}},
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_name_description}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "{{Properties.Resources.builtin_create_extension_name_label}}",
|
||||
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_label)}},
|
||||
"isRequired": true,
|
||||
"errorMessage": "{{Properties.Resources.builtin_create_extension_name_required}}",
|
||||
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_required)}},
|
||||
"id": "ExtensionName",
|
||||
"placeholder": "ExtensionName",
|
||||
"regex": "^[^\\s]+$"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_display_name_header}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_header)}},
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_display_name_description}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "{{Properties.Resources.builtin_create_extension_display_name_label}}",
|
||||
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_label)}},
|
||||
"isRequired": true,
|
||||
"errorMessage": "{{Properties.Resources.builtin_create_extension_display_name_required}}",
|
||||
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_required)}},
|
||||
"id": "DisplayName",
|
||||
"placeholder": "My new extension"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_directory_header}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_header)}},
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "{{Properties.Resources.builtin_create_extension_directory_description}}",
|
||||
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}},
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "{{Properties.Resources.builtin_create_extension_directory_label}}",
|
||||
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_label)}},
|
||||
"isRequired": true,
|
||||
"errorMessage": "{{Properties.Resources.builtin_create_extension_directory_required}}",
|
||||
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_required)}},
|
||||
"id": "OutputPath",
|
||||
"placeholder": "C:\\users\\me\\dev"
|
||||
}
|
||||
@@ -98,7 +99,7 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "{{Properties.Resources.builtin_create_extension_submit}}",
|
||||
"title": {{FormatJsonString(Properties.Resources.builtin_create_extension_submit)}},
|
||||
"associatedInputs": "auto"
|
||||
}
|
||||
]
|
||||
@@ -192,4 +193,10 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
// Delete the temp dir
|
||||
Directory.Delete(tempDir, true);
|
||||
}
|
||||
|
||||
private string FormatJsonString(string str)
|
||||
{
|
||||
// Escape the string for JSON
|
||||
return JsonSerializer.Serialize(str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// 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.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class ListGroup : ObservableObject
|
||||
{
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ListItemViewModel> Items { get; set; } = [];
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
@@ -27,12 +27,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
private ObservableCollection<ListItemViewModel> Items { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool HasGrouping { get; private set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ListGroup> Groups { get; set; } = [];
|
||||
|
||||
private readonly ExtensionObject<IListPage> _model;
|
||||
|
||||
private readonly Lock _listLock = new();
|
||||
@@ -254,53 +248,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateGroupsIfNeeded()
|
||||
{
|
||||
HasGrouping = FilteredItems.Any(i => !string.IsNullOrEmpty(i.Section));
|
||||
|
||||
if (HasGrouping)
|
||||
{
|
||||
lock (_listLock)
|
||||
{
|
||||
if (FilteredItems.Count == 0)
|
||||
{
|
||||
Groups.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// get current groups
|
||||
var groups = FilteredItems.GroupBy(item => item.Section).Select(group => group);
|
||||
|
||||
// Remove any groups that no longer exist
|
||||
foreach (var group in Groups)
|
||||
{
|
||||
if (!groups.Any(g => g.Key == group.Key))
|
||||
{
|
||||
Groups.Remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Update lists for each existing group
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var existingGroup = Groups.FirstOrDefault(groupItem => groupItem.Key == group.Key);
|
||||
if (existingGroup == null)
|
||||
{
|
||||
// Add a new group if it doesn't exist
|
||||
Groups.Add(new ListGroup { Key = group.Key, Items = new ObservableCollection<ListItemViewModel>(group) });
|
||||
existingGroup = Groups.FirstOrDefault(groupItem => groupItem.Key == group.Key);
|
||||
}
|
||||
|
||||
if (existingGroup != null)
|
||||
{
|
||||
// Update the existing group
|
||||
ListHelpers.InPlaceUpdateList(existingGroup.Items, FilteredItems.Where(item => item.Section == group.Key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply our current filter text to the list of items, and update
|
||||
/// FilteredItems to match the results.
|
||||
@@ -540,18 +487,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
|
||||
FilteredItems.Clear();
|
||||
|
||||
foreach (ListGroup group in Groups)
|
||||
{
|
||||
foreach (ListItemViewModel item in group.Items)
|
||||
{
|
||||
item.SafeCleanup();
|
||||
}
|
||||
|
||||
group.Items.Clear();
|
||||
}
|
||||
|
||||
Groups.Clear();
|
||||
}
|
||||
|
||||
IListPage? model = _model.Unsafe;
|
||||
|
||||
@@ -36,6 +36,8 @@ public partial class SettingsModel : ObservableObject
|
||||
|
||||
public bool HighlightSearchOnActivate { get; set; } = true;
|
||||
|
||||
public bool ShowSystemTrayIcon { get; set; } = true;
|
||||
|
||||
public Dictionary<string, ProviderSettings> ProviderSettings { get; set; } = [];
|
||||
|
||||
public Dictionary<string, CommandAlias> Aliases { get; set; } = [];
|
||||
|
||||
@@ -87,6 +87,16 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowSystemTrayIcon
|
||||
{
|
||||
get => _settings.ShowSystemTrayIcon;
|
||||
set
|
||||
{
|
||||
_settings.ShowSystemTrayIcon = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ProviderSettingsViewModel> CommandProviders { get; } = [];
|
||||
|
||||
public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler)
|
||||
|
||||
@@ -152,7 +152,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
{
|
||||
GenerateId();
|
||||
|
||||
UpdateAlias();
|
||||
FetchAliasFromAliasManager();
|
||||
UpdateHotkey();
|
||||
UpdateTags();
|
||||
}
|
||||
@@ -163,24 +163,31 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
|
||||
private void HandleChangeAlias()
|
||||
{
|
||||
SetAlias(Alias);
|
||||
SetAlias();
|
||||
Save();
|
||||
}
|
||||
|
||||
public void SetAlias(CommandAlias? newAlias)
|
||||
public void SetAlias()
|
||||
{
|
||||
_serviceProvider.GetService<AliasManager>()!.UpdateAlias(Id, newAlias);
|
||||
UpdateAlias();
|
||||
var commandAlias = Alias is null
|
||||
? null
|
||||
: new CommandAlias(Alias.Alias, Alias.CommandId, Alias.IsDirect);
|
||||
|
||||
_serviceProvider.GetService<AliasManager>()!.UpdateAlias(Id, commandAlias);
|
||||
UpdateTags();
|
||||
}
|
||||
|
||||
private void UpdateAlias()
|
||||
private void FetchAliasFromAliasManager()
|
||||
{
|
||||
// Add tags for the alias, if we have one.
|
||||
var aliases = _serviceProvider.GetService<AliasManager>();
|
||||
if (aliases != null)
|
||||
var am = _serviceProvider.GetService<AliasManager>();
|
||||
if (am != null)
|
||||
{
|
||||
Alias = aliases.AliasFromId(Id);
|
||||
var commandAlias = am.AliasFromId(Id);
|
||||
if (commandAlias is not null)
|
||||
{
|
||||
// Decouple from the alias manager alias object
|
||||
Alias = new CommandAlias(commandAlias.Alias, commandAlias.CommandId, commandAlias.IsDirect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<!-- Other merged dictionaries here -->
|
||||
<ResourceDictionary Source="Styles/Button.xaml" />
|
||||
<ResourceDictionary Source="Styles/Colors.xaml" />
|
||||
<ResourceDictionary Source="Styles/Generic.xaml" />
|
||||
<ResourceDictionary Source="Styles/TextBox.xaml" />
|
||||
<ResourceDictionary Source="Styles/Settings.xaml" />
|
||||
<ResourceDictionary Source="Controls/Tag.xaml" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Common.Helpers;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.Ext.Apps;
|
||||
@@ -78,7 +79,7 @@ public partial class App : Application
|
||||
|
||||
var cmdArgs = Environment.GetCommandLineArgs();
|
||||
|
||||
bool runFromPT = false;
|
||||
var runFromPT = false;
|
||||
foreach (var arg in cmdArgs)
|
||||
{
|
||||
if (arg == "RunFromPT")
|
||||
@@ -107,9 +108,6 @@ public partial class App : Application
|
||||
|
||||
// Built-in Commands. Order matters - this is the order they'll be presented by default.
|
||||
var allApps = new AllAppsCommandProvider();
|
||||
var winget = new WinGetExtensionCommandsProvider();
|
||||
var callback = allApps.LookupApp;
|
||||
winget.SetAllLookup(callback);
|
||||
services.AddSingleton<ICommandProvider>(allApps);
|
||||
services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
|
||||
@@ -120,7 +118,25 @@ public partial class App : Application
|
||||
// services.AddSingleton<ICommandProvider, ClipboardHistoryCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider>(winget);
|
||||
|
||||
// GH #38440: Users might not have WinGet installed! Or they might have
|
||||
// a ridiculously old version. Or might be running as admin.
|
||||
// We shouldn't explode in the App ctor if we fail to instantiate an
|
||||
// instance of PackageManager, which will happen in the static ctor
|
||||
// for WinGetStatics
|
||||
try
|
||||
{
|
||||
var winget = new WinGetExtensionCommandsProvider();
|
||||
var callback = allApps.LookupApp;
|
||||
winget.SetAllLookup(callback);
|
||||
services.AddSingleton<ICommandProvider>(winget);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Couldn't load winget");
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
|
||||
services.AddSingleton<ICommandProvider, WindowsTerminalCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowsSettingsCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, RegistryCommandsProvider>();
|
||||
|
||||
@@ -105,19 +105,8 @@ public sealed partial class SearchBar : UserControl,
|
||||
|
||||
var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
|
||||
var altPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down);
|
||||
if (e.Key == VirtualKey.Down)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigateNextCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Up)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigatePreviousCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (ctrlPressed && e.Key == VirtualKey.Enter)
|
||||
if (ctrlPressed && e.Key == VirtualKey.Enter)
|
||||
{
|
||||
// ctrl+enter
|
||||
WeakReferenceMessenger.Default.Send<ActivateSecondaryCommandMessage>();
|
||||
@@ -197,6 +186,18 @@ public sealed partial class SearchBar : UserControl,
|
||||
_isBackspaceHeld = true;
|
||||
}
|
||||
}
|
||||
else if (e.Key == VirtualKey.Up)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigatePreviousCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Down)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigateNextCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterBox_PreviewKeyUp(object sender, KeyRoutedEventArgs e)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Microsoft.CmdPal.UI.ListPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
@@ -17,16 +17,10 @@
|
||||
<Page.Resources>
|
||||
<!-- TODO: Figure out what we want to do here for filtering/grouping and where -->
|
||||
<!-- https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.data.collectionviewsource -->
|
||||
<CollectionViewSource
|
||||
x:Name="GroupedItemsCVS"
|
||||
<!--<CollectionViewSource
|
||||
x:Name="ItemsCVS"
|
||||
IsSourceGrouped="True"
|
||||
ItemsPath="Items"
|
||||
Source="{x:Bind ViewModel.Groups, Mode=OneWay}" />
|
||||
|
||||
<CollectionViewSource
|
||||
x:Name="UngroupedItemsCVS"
|
||||
IsSourceGrouped="False"
|
||||
Source="{x:Bind ViewModel.FilteredItems, Mode=OneWay}" />
|
||||
Source="{x:Bind ViewModel.Items, Mode=OneWay}" />-->
|
||||
|
||||
<converters:StringVisibilityConverter
|
||||
x:Key="StringVisibilityConverter"
|
||||
@@ -113,74 +107,32 @@
|
||||
TargetType="x:Boolean"
|
||||
Value="{x:Bind ViewModel.ShowEmptyContent, Mode=OneWay}">
|
||||
<controls:Case Value="False">
|
||||
<controls:SwitchPresenter
|
||||
HorizontalAlignment="Stretch"
|
||||
TargetType="x:Boolean"
|
||||
Value="{x:Bind ViewModel.HasGrouping, Mode=OneWay}">
|
||||
<controls:Case Value="True">
|
||||
<ListView
|
||||
x:Name="GroupedItemsList"
|
||||
Padding="0,2,0,0"
|
||||
DoubleTapped="GroupedItemsList_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ItemsList_ItemClick"
|
||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||
ItemsSource="{Binding ElementName=GroupedItemsCVS, Path=View}"
|
||||
SelectionChanged="GroupedItemsList_SelectionChanged">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</ListView.ItemContainerTransitions>
|
||||
<ListView.GroupStyle>
|
||||
<!--<GroupStyle HeaderContainerStyle="{StaticResource CustomHeaderContainerStyle}" HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Padding="20,12,0,8"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind Key}"
|
||||
Visibility="{x:Bind Key, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>-->
|
||||
|
||||
<GroupStyle HeaderContainerStyle="{StaticResource CustomHeaderContainerStyle}" HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Margin="0,16,0,0"
|
||||
Padding="20,8,0,4"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Key, Mode=OneWay}"
|
||||
Visibility="{Binding Key, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>
|
||||
</ListView>
|
||||
</controls:Case>
|
||||
<controls:Case Value="False">
|
||||
<ListView
|
||||
x:Name="ItemsList"
|
||||
Padding="0,2,0,0"
|
||||
DoubleTapped="ItemsList_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ItemsList_ItemClick"
|
||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||
ItemsSource="{Binding ElementName=UngroupedItemsCVS, Path=View}"
|
||||
SelectionChanged="ItemsList_SelectionChanged">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</ListView.ItemContainerTransitions>
|
||||
</ListView>
|
||||
</controls:Case>
|
||||
</controls:SwitchPresenter>
|
||||
<ListView
|
||||
x:Name="ItemsList"
|
||||
Padding="0,2,0,0"
|
||||
DoubleTapped="ItemsList_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ItemsList_ItemClick"
|
||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
||||
SelectionChanged="ItemsList_SelectionChanged">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</ListView.ItemContainerTransitions>
|
||||
<!--<ListView.GroupStyle>
|
||||
<GroupStyle HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Margin="0,16,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Key, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>-->
|
||||
</ListView>
|
||||
</controls:Case>
|
||||
<controls:Case Value="True">
|
||||
<StackPanel
|
||||
|
||||
@@ -37,7 +37,6 @@ public sealed partial class ListPage : Page,
|
||||
this.InitializeComponent();
|
||||
this.NavigationCacheMode = NavigationCacheMode.Disabled;
|
||||
this.ItemsList.Loaded += ItemsList_Loaded;
|
||||
this.GroupedItemsList.Loaded += GroupedItemsList_Loaded;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
@@ -122,18 +121,6 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
private void GroupedItemsList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
if (GroupedItemsList.SelectedItem is ListItemViewModel vm)
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
if (!settings.SingleClickActivates)
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||
private void ItemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
@@ -161,24 +148,6 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||
private void GroupedItemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (GroupedItemsList.SelectedItem is ListItemViewModel item)
|
||||
{
|
||||
var vm = ViewModel;
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
vm?.UpdateSelectedItemCommand.Execute(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedItem != null)
|
||||
{
|
||||
GroupedItemsList.ScrollIntoView(GroupedItemsList.SelectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemsList_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Find the ScrollViewer in the ListView
|
||||
@@ -190,17 +159,6 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
private void GroupedItemsList_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Find the ScrollViewer in the ListView
|
||||
var listViewScrollViewer = FindScrollViewer(this.GroupedItemsList);
|
||||
|
||||
if (listViewScrollViewer != null)
|
||||
{
|
||||
listViewScrollViewer.ViewChanged += ListViewScrollViewer_ViewChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void ListViewScrollViewer_ViewChanged(object? sender, ScrollViewerViewChangedEventArgs e)
|
||||
{
|
||||
var scrollView = sender as ScrollViewer;
|
||||
@@ -229,10 +187,9 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ItemsList.SelectedIndex++;
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedIndex < GroupedItemsList.Items.Count - 1)
|
||||
else
|
||||
{
|
||||
GroupedItemsList.SelectedIndex++;
|
||||
ItemsList.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +199,9 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ItemsList.SelectedIndex--;
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedIndex > 0)
|
||||
else
|
||||
{
|
||||
GroupedItemsList.SelectedIndex--;
|
||||
ItemsList.SelectedIndex = ItemsList.Items.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,10 +215,6 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(item);
|
||||
}
|
||||
else if (GroupedItemsList.SelectedItem is ListItemViewModel groupedItem)
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(groupedItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(ActivateSecondaryCommandMessage message)
|
||||
@@ -275,10 +227,6 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ViewModel?.InvokeSecondaryCommandCommand.Execute(item);
|
||||
}
|
||||
else if (GroupedItemsList.SelectedItem is ListItemViewModel groupedItem)
|
||||
{
|
||||
ViewModel?.InvokeSecondaryCommandCommand.Execute(groupedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
@@ -307,11 +255,6 @@ public sealed partial class ListPage : Page,
|
||||
// GetItems or a change in the filter.
|
||||
private void Page_ItemsUpdated(ListViewModel sender, object args)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel?.UpdateGroupsIfNeeded();
|
||||
}
|
||||
|
||||
// If for some reason, we don't have a selected item, fix that.
|
||||
//
|
||||
// It's important to do this here, because once there's no selection
|
||||
@@ -323,11 +266,6 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ItemsList.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedItem == null)
|
||||
{
|
||||
GroupedItemsList.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
|
||||
@@ -54,7 +54,6 @@ public sealed partial class MainWindow : Window,
|
||||
|
||||
// Notification Area ("Tray") icon data
|
||||
private NOTIFYICONDATAW? _trayIconData;
|
||||
private bool _createdIcon;
|
||||
private DestroyIconSafeHandle? _largeIcon;
|
||||
|
||||
private DesktopAcrylicController? _acrylicController;
|
||||
@@ -99,7 +98,6 @@ public sealed partial class MainWindow : Window,
|
||||
_hotkeyWndProc = HotKeyPrc;
|
||||
var hotKeyPrcPointer = Marshal.GetFunctionPointerForDelegate(_hotkeyWndProc);
|
||||
_originalWndProc = Marshal.GetDelegateForFunctionPointer<WNDPROC>(PInvoke.SetWindowLongPtr(_hwnd, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, hotKeyPrcPointer));
|
||||
AddNotificationIcon();
|
||||
|
||||
// Load our settings, and then also wire up a settings changed handler
|
||||
HotReloadSettings();
|
||||
@@ -149,6 +147,7 @@ public sealed partial class MainWindow : Window,
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
|
||||
SetupHotkey(settings);
|
||||
SetupTrayIcon(settings.ShowSystemTrayIcon);
|
||||
|
||||
// This will prevent our window from appearing in alt+tab or the taskbar.
|
||||
// You'll _need_ to use the hotkey to summon it.
|
||||
@@ -299,7 +298,7 @@ public sealed partial class MainWindow : Window,
|
||||
var extensionService = serviceProvider.GetService<IExtensionService>()!;
|
||||
extensionService.SignalStopExtensionsAsync();
|
||||
|
||||
RemoveNotificationIcon();
|
||||
RemoveTrayIcon();
|
||||
|
||||
// WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592).
|
||||
// Workaround by turning it off before shutdown.
|
||||
@@ -491,9 +490,9 @@ public sealed partial class MainWindow : Window,
|
||||
// WM_WINDOWPOSCHANGING which is always received on explorer startup sequence.
|
||||
case PInvoke.WM_WINDOWPOSCHANGING:
|
||||
{
|
||||
if (!_createdIcon)
|
||||
if (_trayIconData == null)
|
||||
{
|
||||
AddNotificationIcon();
|
||||
SetupTrayIcon();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,7 +504,7 @@ public sealed partial class MainWindow : Window,
|
||||
{
|
||||
// Handle the case where explorer.exe restarts.
|
||||
// Even if we created it before, do it again
|
||||
AddNotificationIcon();
|
||||
SetupTrayIcon();
|
||||
}
|
||||
else if (uMsg == WM_TRAY_ICON)
|
||||
{
|
||||
@@ -525,55 +524,60 @@ public sealed partial class MainWindow : Window,
|
||||
return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
private void AddNotificationIcon()
|
||||
private void SetupTrayIcon(bool? showSystemTrayIcon = null)
|
||||
{
|
||||
// We only need to build the tray data once.
|
||||
if (_trayIconData == null)
|
||||
if (showSystemTrayIcon ?? App.Current.Services.GetService<SettingsModel>()!.ShowSystemTrayIcon)
|
||||
{
|
||||
// We need to stash this handle, so it doesn't clean itself up. If
|
||||
// explorer restarts, we'll come back through here, and we don't
|
||||
// really need to re-load the icon in that case. We can just use
|
||||
// the handle from the first time.
|
||||
_largeIcon = GetAppIconHandle();
|
||||
_trayIconData = new NOTIFYICONDATAW()
|
||||
// We only need to build the tray data once.
|
||||
if (_trayIconData == null)
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)),
|
||||
hWnd = _hwnd,
|
||||
uID = MY_NOTIFY_ID,
|
||||
uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP,
|
||||
uCallbackMessage = WM_TRAY_ICON,
|
||||
hIcon = (HICON)_largeIcon.DangerousGetHandle(),
|
||||
szTip = RS_.GetString("AppStoreName"),
|
||||
};
|
||||
// We need to stash this handle, so it doesn't clean itself up. If
|
||||
// explorer restarts, we'll come back through here, and we don't
|
||||
// really need to re-load the icon in that case. We can just use
|
||||
// the handle from the first time.
|
||||
_largeIcon = GetAppIconHandle();
|
||||
_trayIconData = new NOTIFYICONDATAW()
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)),
|
||||
hWnd = _hwnd,
|
||||
uID = MY_NOTIFY_ID,
|
||||
uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP,
|
||||
uCallbackMessage = WM_TRAY_ICON,
|
||||
hIcon = (HICON)_largeIcon.DangerousGetHandle(),
|
||||
szTip = RS_.GetString("AppStoreName"),
|
||||
};
|
||||
}
|
||||
|
||||
var d = (NOTIFYICONDATAW)_trayIconData;
|
||||
|
||||
// Add the notification icon
|
||||
PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d);
|
||||
}
|
||||
|
||||
var d = (NOTIFYICONDATAW)_trayIconData;
|
||||
|
||||
// Add the notification icon
|
||||
if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d))
|
||||
else
|
||||
{
|
||||
_createdIcon = true;
|
||||
RemoveTrayIcon();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveNotificationIcon()
|
||||
private void RemoveTrayIcon()
|
||||
{
|
||||
if (_trayIconData != null && _createdIcon)
|
||||
if (_trayIconData != null)
|
||||
{
|
||||
var d = (NOTIFYICONDATAW)_trayIconData;
|
||||
if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, in d))
|
||||
{
|
||||
_createdIcon = false;
|
||||
_trayIconData = null;
|
||||
}
|
||||
}
|
||||
|
||||
_largeIcon?.Close();
|
||||
}
|
||||
|
||||
private DestroyIconSafeHandle GetAppIconHandle()
|
||||
{
|
||||
var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
DestroyIconSafeHandle largeIcon;
|
||||
DestroyIconSafeHandle smallIcon;
|
||||
PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out smallIcon, 1);
|
||||
PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out _, 1);
|
||||
return largeIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
|
||||
<Version>$(CmdPalVersion)</Version>
|
||||
|
||||
@@ -72,12 +73,17 @@
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
|
||||
<!-- LOAD BEARING: GeneratePathProperty=true on BOTH the AC dependencies. Don't forget the AdaptiveCardsWorkaround below -->
|
||||
<PackageReference Include="AdaptiveCards.ObjectModel.WinUI3" GeneratePathProperty="true" />
|
||||
<PackageReference Include="AdaptiveCards.Rendering.WinUI3" GeneratePathProperty="True" />
|
||||
<PackageReference Include="AdaptiveCards.Templating" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -19,6 +19,7 @@ SetFocus
|
||||
SetActiveWindow
|
||||
MonitorFromWindow
|
||||
GetMonitorInfo
|
||||
GetDpiForMonitor
|
||||
SHCreateStreamOnFileEx
|
||||
CoAllowSetForegroundWindow
|
||||
SHCreateStreamOnFileEx
|
||||
|
||||
@@ -68,6 +68,10 @@
|
||||
<ToggleSwitch IsOn="{x:Bind viewModel.SingleClickActivates, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="Settings_GeneralPage_ShowSystemTrayIcon_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind viewModel.ShowSystemTrayIcon, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- Example 'About' section -->
|
||||
<TextBlock x:Uid="AboutSettingsHeader" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
|
||||
@@ -385,4 +385,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="BehaviorSettingsHeader.Text" xml:space="preserve">
|
||||
<value>Behavior</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_ShowSystemTrayIcon_SettingsCard.Header" xml:space="preserve">
|
||||
<value>Show system tray icon</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_ShowSystemTrayIcon_SettingsCard.Description" xml:space="preserve">
|
||||
<value>Choose if Command Palette is visible in the system tray</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,29 +0,0 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- Using custom style to remove header divider from list view headers -->
|
||||
<Style x:Key="CustomHeaderContainerStyle" TargetType="ListViewHeaderItem">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ListViewHeaderItemThemeFontSize}" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Margin" Value="0,0,0,0" />
|
||||
<Setter Property="Padding" Value="12,8,12,0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="MinHeight" Value="0" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewHeaderItem">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
@@ -13,6 +14,8 @@ using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Gdi;
|
||||
using Windows.Win32.UI.HiDpi;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
|
||||
|
||||
@@ -44,6 +47,21 @@ public sealed partial class ToastWindow : Window,
|
||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
||||
}
|
||||
|
||||
private static double GetScaleFactor(HWND hwnd)
|
||||
{
|
||||
try
|
||||
{
|
||||
var monitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
_ = PInvoke.GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
|
||||
return dpiX / 96.0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to get scale factor, error: {ex.Message}");
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
private void PositionCentered()
|
||||
{
|
||||
var intSize = new SizeInt32
|
||||
@@ -51,7 +69,14 @@ public sealed partial class ToastWindow : Window,
|
||||
Width = Convert.ToInt32(ToastText.ActualWidth),
|
||||
Height = Convert.ToInt32(ToastText.ActualHeight),
|
||||
};
|
||||
AppWindow.Resize(intSize);
|
||||
|
||||
var scaleAdjustment = GetScaleFactor(_hwnd);
|
||||
var scaled = new SizeInt32
|
||||
{
|
||||
Width = (int)Math.Round(intSize.Width * scaleAdjustment),
|
||||
Height = (int)Math.Round(intSize.Height * scaleAdjustment),
|
||||
};
|
||||
AppWindow.Resize(scaled);
|
||||
|
||||
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest);
|
||||
if (displayArea is not null)
|
||||
|
||||
@@ -40,7 +40,7 @@ Projects of interest are:
|
||||
|
||||
[Initial SDK Spec]: ./doc/initial-sdk-spec/initial-sdk-spec.md
|
||||
[generic samples]: ./Exts/SamplePagesExtension
|
||||
[real samples]: .Exts/ProcessMonitorExtension
|
||||
[real samples]: ./Exts/ProcessMonitorExtension
|
||||
[real extensions that we've "shipped" already]: https://github.com/zadjii/CmdPalExtensions/blob/main/src/extensions
|
||||
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ public partial class InstallPackageListItem : ListItem
|
||||
|
||||
private void InstallStateChangedHandler(object? sender, InstallPackageCommand e)
|
||||
{
|
||||
if (!ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment", 12))
|
||||
if (!ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment.WindowsPackageManagerContract", 12))
|
||||
{
|
||||
Logger.LogError($"RefreshPackageCatalogAsync isn't available");
|
||||
e.FakeChangeStatus();
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Management.Deployment;
|
||||
using Windows.Foundation.Metadata;
|
||||
using WindowsPackageManager.Interop;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WinGet;
|
||||
@@ -51,9 +52,12 @@ internal static class WinGetStatics
|
||||
_storeCatalog,
|
||||
];
|
||||
|
||||
foreach (var catalogReference in AvailableCatalogs)
|
||||
if (ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment.WindowsPackageManagerContract", 8))
|
||||
{
|
||||
catalogReference.PackageCatalogBackgroundUpdateInterval = new(0);
|
||||
foreach (var catalogReference in AvailableCatalogs)
|
||||
{
|
||||
catalogReference.PackageCatalogBackgroundUpdateInterval = new(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Immediately start the lazy-init of the all packages catalog, but
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
// 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.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace SamplePagesExtension;
|
||||
|
||||
internal sealed partial class FizzBuzzListPage : ListPage
|
||||
{
|
||||
public override string Title => "FizzBuzz Page";
|
||||
|
||||
public override IconInfo Icon => new("\uE94C"); // % symbol
|
||||
|
||||
public override string Name => "Open";
|
||||
|
||||
private readonly List<ListItem> _items;
|
||||
|
||||
internal FizzBuzzListPage()
|
||||
{
|
||||
var addNewItem = new ListItem(new AnonymousCommand(() =>
|
||||
{
|
||||
var c = _items.Count;
|
||||
var f = c % 3 == 0;
|
||||
var b = c % 5 == 0;
|
||||
var s = string.Empty;
|
||||
if (f)
|
||||
{
|
||||
s += "Fizz";
|
||||
}
|
||||
|
||||
if (b)
|
||||
{
|
||||
s += "Buzz";
|
||||
}
|
||||
|
||||
_items.Add(new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = $"{c}",
|
||||
Icon = IconFromIndex(_items.Count),
|
||||
Section = s,
|
||||
});
|
||||
RaiseItemsChanged();
|
||||
})
|
||||
{ Result = CommandResult.KeepOpen() })
|
||||
{
|
||||
Title = "Add item",
|
||||
Subtitle = "Each item will be sorted into sections. Add at least three",
|
||||
Icon = new IconInfo("\uED0E"),
|
||||
};
|
||||
|
||||
_items = [addNewItem];
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
return _items.ToArray();
|
||||
}
|
||||
|
||||
private IconInfo IconFromIndex(int index)
|
||||
{
|
||||
return _icons[index % _icons.Length];
|
||||
}
|
||||
|
||||
private readonly IconInfo[] _icons =
|
||||
[
|
||||
new IconInfo("\ue700"),
|
||||
new IconInfo("\ue701"),
|
||||
new IconInfo("\ue702"),
|
||||
new IconInfo("\ue703"),
|
||||
new IconInfo("\ue704"),
|
||||
new IconInfo("\ue705"),
|
||||
new IconInfo("\ue706"),
|
||||
new IconInfo("\ue707"),
|
||||
new IconInfo("\ue708"),
|
||||
new IconInfo("\ue709"),
|
||||
new IconInfo("\ue70a"),
|
||||
new IconInfo("\ue70b"),
|
||||
new IconInfo("\ue70c"),
|
||||
new IconInfo("\ue70d"),
|
||||
new IconInfo("\ue70e"),
|
||||
new IconInfo("\ue70f"),
|
||||
new IconInfo("\ue710"),
|
||||
new IconInfo("\ue711"),
|
||||
new IconInfo("\ue712"),
|
||||
new IconInfo("\ue713"),
|
||||
];
|
||||
}
|
||||
@@ -222,7 +222,7 @@ namespace ColorPicker.Helpers
|
||||
|
||||
public bool HandleEnterPressed()
|
||||
{
|
||||
if (!IsColorPickerVisible())
|
||||
if (!_colorPickerShown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -233,14 +233,13 @@ namespace ColorPicker.Helpers
|
||||
|
||||
public bool HandleEscPressed()
|
||||
{
|
||||
if (!BlockEscapeKeyClosingColorPickerEditor)
|
||||
if (!BlockEscapeKeyClosingColorPickerEditor
|
||||
&& (_colorPickerShown || (_colorEditorWindow != null && _colorEditorWindow.IsActive)))
|
||||
{
|
||||
return EndUserSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void MoveCursor(int xOffset, int yOffset)
|
||||
|
||||
@@ -70,17 +70,10 @@ namespace ColorPicker.Keyboard
|
||||
var virtualCode = e.KeyboardData.VirtualCode;
|
||||
|
||||
// ESC pressed
|
||||
if (virtualCode == KeyInterop.VirtualKeyFromKey(Key.Escape)
|
||||
&& e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown
|
||||
)
|
||||
if (virtualCode == KeyInterop.VirtualKeyFromKey(Key.Escape) && e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown)
|
||||
{
|
||||
if (_appStateHandler.IsColorPickerVisible()
|
||||
|| !AppStateHandler.BlockEscapeKeyClosingColorPickerEditor
|
||||
)
|
||||
{
|
||||
e.Handled = _appStateHandler.EndUserSession();
|
||||
return;
|
||||
}
|
||||
e.Handled = _appStateHandler.HandleEscPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((virtualCode == KeyInterop.VirtualKeyFromKey(Key.Space) || virtualCode == KeyInterop.VirtualKeyFromKey(Key.Enter)) && (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyDown))
|
||||
|
||||
@@ -104,8 +104,10 @@ namespace ColorPicker.Mouse
|
||||
var rect = new Rectangle((int)mousePosition.X, (int)mousePosition.Y, 1, 1);
|
||||
using (var bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb))
|
||||
{
|
||||
var g = Graphics.FromImage(bmp);
|
||||
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
||||
using (var g = Graphics.FromImage(bmp)) // Ensure Graphics object is disposed
|
||||
{
|
||||
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
|
||||
}
|
||||
|
||||
return bmp.GetPixel(0, 0);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Appium.WebDriver" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<PackageReference Include="Appium.WebDriver" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -117,15 +117,15 @@ namespace EditorHelpers
|
||||
}
|
||||
if (shortcut.ctrlKey != ModifierKey::Disabled)
|
||||
{
|
||||
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetCtrlKey()).c_str()));
|
||||
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetCtrlKey(ModifierKey::Both)).c_str()));
|
||||
}
|
||||
if (shortcut.altKey != ModifierKey::Disabled)
|
||||
{
|
||||
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetAltKey()).c_str()));
|
||||
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetAltKey(ModifierKey::Both)).c_str()));
|
||||
}
|
||||
if (shortcut.shiftKey != ModifierKey::Disabled)
|
||||
{
|
||||
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetShiftKey()).c_str()));
|
||||
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetShiftKey(ModifierKey::Both)).c_str()));
|
||||
}
|
||||
if (shortcut.actionKey != NULL)
|
||||
{
|
||||
|
||||
@@ -158,13 +158,13 @@ namespace KeyboardEventHandlers
|
||||
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
Helpers::SetModifierKeyEvents(targetShortcut, Modifiers(), keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
// Dummy key is not required here since SetModifierKeyEvents will only add key-up events for the modifiers here, and the action key key-up is already sent before it
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dummy key is not required here since SetModifierKeyEvents will only add key-down events for the modifiers here, and the action key key-down is already sent after it
|
||||
Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
Helpers::SetModifierKeyEvents(targetShortcut, Modifiers(), keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(targetShortcut.GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
|
||||
}
|
||||
}
|
||||
@@ -371,11 +371,35 @@ namespace KeyboardEventHandlers
|
||||
// Remember which win key was pressed initially
|
||||
if (ii.GetVirtualKeyState(VK_RWIN))
|
||||
{
|
||||
it->second.winKeyInvoked = ModifierKey::Right;
|
||||
it->second.modifierKeysInvoked.winKey = ModifierKey::Right;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(VK_LWIN))
|
||||
{
|
||||
it->second.winKeyInvoked = ModifierKey::Left;
|
||||
it->second.modifierKeysInvoked.winKey = ModifierKey::Left;
|
||||
}
|
||||
if (ii.GetVirtualKeyState(VK_RCONTROL))
|
||||
{
|
||||
it->second.modifierKeysInvoked.ctrlKey = ModifierKey::Right;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(VK_LCONTROL))
|
||||
{
|
||||
it->second.modifierKeysInvoked.ctrlKey = ModifierKey::Left;
|
||||
}
|
||||
if (ii.GetVirtualKeyState(VK_RSHIFT))
|
||||
{
|
||||
it->second.modifierKeysInvoked.shiftKey = ModifierKey::Right;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(VK_LSHIFT))
|
||||
{
|
||||
it->second.modifierKeysInvoked.shiftKey = ModifierKey::Left;
|
||||
}
|
||||
if (ii.GetVirtualKeyState(VK_RMENU))
|
||||
{
|
||||
it->second.modifierKeysInvoked.altKey = ModifierKey::Right;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(VK_LMENU))
|
||||
{
|
||||
it->second.modifierKeysInvoked.altKey = ModifierKey::Left;
|
||||
}
|
||||
|
||||
if (isRunProgram)
|
||||
@@ -450,7 +474,7 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// key down for all new shortcut keys except the common modifiers
|
||||
keyEventList = std::vector<INPUT>{};
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
else
|
||||
@@ -460,15 +484,15 @@ namespace KeyboardEventHandlers
|
||||
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release original shortcut state (release in reverse order of shortcut to be accurate)
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// Set new shortcut key down state
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifiers - ex: Win+Caps -> Ctrl+A
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
if (it->first.GetCtrlKey(it->second.modifierKeysInvoked.ctrlKey) == NULL && it->first.GetAltKey(it->second.modifierKeysInvoked.altKey) == NULL && it->first.GetShiftKey(it->second.modifierKeysInvoked.shiftKey) == NULL)
|
||||
{
|
||||
Shortcut temp = std::get<Shortcut>(it->second.targetShortcut);
|
||||
for (auto keys : temp.GetKeyCodes())
|
||||
@@ -490,7 +514,7 @@ namespace KeyboardEventHandlers
|
||||
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release original shortcut state (release in reverse order of shortcut to be accurate)
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Set target key down state
|
||||
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
|
||||
@@ -499,7 +523,7 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
|
||||
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl
|
||||
if (it->first.GetCtrlKey() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
|
||||
if (it->first.GetCtrlKey(it->second.modifierKeysInvoked.ctrlKey) == NULL && it->first.GetAltKey(it->second.modifierKeysInvoked.altKey) == NULL && it->first.GetShiftKey(it->second.modifierKeysInvoked.shiftKey) == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, static_cast<WORD>(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))), data->lParam->vkCode);
|
||||
}
|
||||
@@ -512,7 +536,7 @@ namespace KeyboardEventHandlers
|
||||
Helpers::SetDummyKeyEvent(keyEventList, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release original shortcut state (release in reverse order of shortcut to be accurate)
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
Helpers::SetTextKeyEvents(keyEventList, remapping);
|
||||
}
|
||||
@@ -614,12 +638,12 @@ namespace KeyboardEventHandlers
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode);
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode);
|
||||
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate its own key message
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode);
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -643,7 +667,7 @@ namespace KeyboardEventHandlers
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
// Set original shortcut key down state except the action key and the released modifier since the original action key may or may not be held down. If it is held down it will generate its own key message
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -656,7 +680,7 @@ namespace KeyboardEventHandlers
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.modifierKeysInvoked.Reset();
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
@@ -719,14 +743,14 @@ namespace KeyboardEventHandlers
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(std::get<Shortcut>(it->second.targetShortcut).GetActionKey()), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
|
||||
// Release new shortcut state (release in reverse order of shortcut to be accurate)
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
|
||||
// Set old shortcut key down state
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.modifierKeysInvoked.Reset();
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
|
||||
// If app specific shortcut has finished invoking, reset the target application
|
||||
@@ -763,7 +787,7 @@ namespace KeyboardEventHandlers
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
// Set original shortcut key down state except the action key
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Shift+Win+A and release A, since Win will be pressed here we need to send a dummy event after it
|
||||
@@ -773,7 +797,7 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.modifierKeysInvoked.Reset();
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
}
|
||||
|
||||
@@ -795,7 +819,7 @@ namespace KeyboardEventHandlers
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey(it->second.modifierKeysInvoked.ctrlKey) == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey(it->second.modifierKeysInvoked.altKey) == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey(it->second.modifierKeysInvoked.shiftKey) == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
}
|
||||
@@ -817,7 +841,7 @@ namespace KeyboardEventHandlers
|
||||
if (remapToShortcut)
|
||||
{
|
||||
// Modifier state reset might be required for this key depending on the target shortcut action key - ex: Ctrl+A -> Win+Caps, Shift is pressed. System should not see Shift and Caps pressed together
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey() == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey() == NULL)
|
||||
if (std::get<Shortcut>(it->second.targetShortcut).GetCtrlKey(it->second.modifierKeysInvoked.ctrlKey) == NULL && std::get<Shortcut>(it->second.targetShortcut).GetAltKey(it->second.modifierKeysInvoked.altKey) == NULL && std::get<Shortcut>(it->second.targetShortcut).GetShiftKey(it->second.modifierKeysInvoked.shiftKey) == NULL)
|
||||
{
|
||||
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, std::get<Shortcut>(it->second.targetShortcut).GetActionKey());
|
||||
}
|
||||
@@ -837,7 +861,7 @@ namespace KeyboardEventHandlers
|
||||
DWORD to = std::get<0>(newRemapping.targetShortcut);
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
Helpers::SetModifierKeyEvents(from, it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
|
||||
{
|
||||
@@ -851,7 +875,7 @@ namespace KeyboardEventHandlers
|
||||
Shortcut to = std::get<Shortcut>(newRemapping.targetShortcut);
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
Helpers::SetModifierKeyEvents(from, it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to);
|
||||
Helpers::SetModifierKeyEvents(from, it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, to);
|
||||
}
|
||||
if (ii.GetVirtualKeyState(static_cast<WORD>(from.actionKey)))
|
||||
{
|
||||
@@ -860,21 +884,11 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
Helpers::SetModifierKeyEvents(to, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from);
|
||||
Helpers::SetModifierKeyEvents(to, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, from);
|
||||
}
|
||||
Helpers::SetKeyEvent(keyEventList, INPUT_KEYBOARD, static_cast<WORD>(to.actionKey), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
newRemapping.isShortcutInvoked = true;
|
||||
}
|
||||
|
||||
// Remember which win key was pressed initially
|
||||
if (ii.GetVirtualKeyState(VK_RWIN))
|
||||
{
|
||||
newRemapping.winKeyInvoked = ModifierKey::Right;
|
||||
}
|
||||
else if (ii.GetVirtualKeyState(VK_LWIN))
|
||||
{
|
||||
newRemapping.winKeyInvoked = ModifierKey::Left;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -888,10 +902,10 @@ namespace KeyboardEventHandlers
|
||||
}
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.modifierKeysInvoked, keyEventList, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
|
||||
|
||||
// Set old shortcut key down state
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
|
||||
}
|
||||
|
||||
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
|
||||
@@ -910,7 +924,7 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.modifierKeysInvoked.Reset();
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
}
|
||||
|
||||
@@ -960,7 +974,7 @@ namespace KeyboardEventHandlers
|
||||
if (!isAltRightKeyInvoked)
|
||||
{
|
||||
// Set original shortcut key down state
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
Helpers::SetModifierKeyEvents(it->first, it->second.modifierKeysInvoked, keyEventList, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
|
||||
}
|
||||
|
||||
// Send the original action key only if it is physically pressed. For remappings to keys other than disabled we already check earlier that it is not pressed in this scenario. For remap to disable
|
||||
@@ -979,7 +993,7 @@ namespace KeyboardEventHandlers
|
||||
{
|
||||
// Reset the remap state
|
||||
it->second.isShortcutInvoked = false;
|
||||
it->second.winKeyInvoked = ModifierKey::Disabled;
|
||||
it->second.modifierKeysInvoked.Reset();
|
||||
it->second.isOriginalActionKeyPressed = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,15 +145,15 @@ std::wstring GetShortcutHumanReadableString(Shortcut const& shortcut, LayoutMap&
|
||||
}
|
||||
if (shortcut.ctrlKey != ModifierKey::Disabled)
|
||||
{
|
||||
humanReadableShortcut += keyboardMap.GetKeyName(shortcut.GetCtrlKey()) + L" + ";
|
||||
humanReadableShortcut += keyboardMap.GetKeyName(shortcut.GetCtrlKey(ModifierKey::Both)) + L" + ";
|
||||
}
|
||||
if (shortcut.altKey != ModifierKey::Disabled)
|
||||
{
|
||||
humanReadableShortcut += keyboardMap.GetKeyName(shortcut.GetAltKey()) + L" + ";
|
||||
humanReadableShortcut += keyboardMap.GetKeyName(shortcut.GetAltKey(ModifierKey::Both)) + L" + ";
|
||||
}
|
||||
if (shortcut.shiftKey != ModifierKey::Disabled)
|
||||
{
|
||||
humanReadableShortcut += keyboardMap.GetKeyName(shortcut.GetShiftKey()) + L" + ";
|
||||
humanReadableShortcut += keyboardMap.GetKeyName(shortcut.GetShiftKey(ModifierKey::Both)) + L" + ";
|
||||
}
|
||||
if (shortcut.actionKey != NULL)
|
||||
{
|
||||
|
||||
@@ -262,27 +262,27 @@ namespace Helpers
|
||||
}
|
||||
|
||||
// Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
|
||||
void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const ModifierKey& winKeyInvoked, std::vector<INPUT>& keyEventArray, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare, const DWORD& keyToBeReleased)
|
||||
void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const Modifiers& modifiersKeys, std::vector<INPUT>& keyEventArray, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare, const DWORD& keyToBeReleased)
|
||||
{
|
||||
// If key down is to be sent, send in the order Win, Ctrl, Alt, Shift
|
||||
if (isKeyDown)
|
||||
{
|
||||
// If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
|
||||
if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetWinKey(modifiersKeys.winKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(modifiersKeys.winKey) != shortcutToCompare.GetWinKey(modifiersKeys.winKey)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), 0, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(modifiersKeys.winKey)), 0, extraInfoFlag);
|
||||
}
|
||||
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetCtrlKey(modifiersKeys.ctrlKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey(modifiersKeys.ctrlKey) != shortcutToCompare.GetCtrlKey(modifiersKeys.ctrlKey)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), 0, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey(modifiersKeys.ctrlKey)), 0, extraInfoFlag);
|
||||
}
|
||||
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetAltKey(modifiersKeys.altKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey(modifiersKeys.altKey) != shortcutToCompare.GetAltKey(modifiersKeys.altKey)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), 0, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey(modifiersKeys.altKey)), 0, extraInfoFlag);
|
||||
}
|
||||
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetShiftKey(modifiersKeys.shiftKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey(modifiersKeys.shiftKey) != shortcutToCompare.GetShiftKey(modifiersKeys.shiftKey)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), 0, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey(modifiersKeys.shiftKey)), 0, extraInfoFlag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,21 +290,21 @@ namespace Helpers
|
||||
else
|
||||
{
|
||||
// If shortcutToCompare is non-empty, then the key event is sent only if both shortcut's don't have the same modifier key. If keyToBeReleased is non-NULL, then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
|
||||
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetShiftKey(modifiersKeys.shiftKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey(modifiersKeys.shiftKey) != shortcutToCompare.GetShiftKey(modifiersKeys.shiftKey) || shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey()), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetShiftKey(modifiersKeys.shiftKey)), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
}
|
||||
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetAltKey(modifiersKeys.altKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey(modifiersKeys.altKey) != shortcutToCompare.GetAltKey(modifiersKeys.altKey) || shortcutToBeSent.CheckAltKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey()), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetAltKey(modifiersKeys.altKey)), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
}
|
||||
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetCtrlKey(modifiersKeys.ctrlKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey(modifiersKeys.ctrlKey) != shortcutToCompare.GetCtrlKey(modifiersKeys.ctrlKey) || shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey()), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetCtrlKey(modifiersKeys.ctrlKey)), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
}
|
||||
if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased)))
|
||||
if (shortcutToBeSent.GetWinKey(modifiersKeys.winKey) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(modifiersKeys.winKey) != shortcutToCompare.GetWinKey(modifiersKeys.winKey) || shortcutToBeSent.CheckWinKey(keyToBeReleased)))
|
||||
{
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(winKeyInvoked)), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
Helpers::SetKeyEvent(keyEventArray, INPUT_KEYBOARD, static_cast<WORD>(shortcutToBeSent.GetWinKey(modifiersKeys.winKey)), KEYEVENTF_KEYUP, extraInfoFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "Shortcut.h"
|
||||
#include "RemapShortcut.h"
|
||||
|
||||
class LayoutMap;
|
||||
|
||||
@@ -47,7 +48,8 @@ namespace Helpers
|
||||
std::wstring GetCurrentApplication(bool keepPath);
|
||||
|
||||
// Function to set key events for modifier keys: When shortcutToCompare is passed (non-empty shortcut), then the key event is sent only if both shortcut's don't have the same modifier key. When keyToBeReleased is passed (non-NULL), then the key event is sent if either the shortcuts don't have the same modifier or if the shortcutToBeSent's modifier matches the keyToBeReleased
|
||||
void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const ModifierKey& winKeyInvoked, std::vector<INPUT>& keyEventArray, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare = Shortcut(), const DWORD& keyToBeReleased = NULL);
|
||||
void SetModifierKeyEvents(const Shortcut& shortcutToBeSent, const Modifiers& modifiersKeys, std::vector<INPUT>& keyEventArray, bool isKeyDown, ULONG_PTR extraInfoFlag, const Shortcut& shortcutToCompare = Shortcut(), const DWORD& keyToBeReleased = NULL);
|
||||
|
||||
|
||||
// Function to filter the key codes for artificial key codes
|
||||
int32_t FilterArtificialKeys(const int32_t& key);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
<ClInclude Include="InputInterface.h" />
|
||||
<ClInclude Include="Helpers.h" />
|
||||
<ClInclude Include="KeyboardManagerConstants.h" />
|
||||
<ClInclude Include="Modifiers.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="RemapShortcut.h" />
|
||||
<ClInclude Include="Shortcut.h" />
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
<ClInclude Include="MappingConfiguration.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Modifiers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
26
src/modules/keyboardmanager/common/Modifiers.h
Normal file
26
src/modules/keyboardmanager/common/Modifiers.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "ModifierKey.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Modifiers
|
||||
{
|
||||
public:
|
||||
ModifierKey winKey = ModifierKey::Disabled;
|
||||
ModifierKey ctrlKey = ModifierKey::Disabled;
|
||||
ModifierKey altKey = ModifierKey::Disabled;
|
||||
ModifierKey shiftKey = ModifierKey::Disabled;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
winKey = ModifierKey::Disabled;
|
||||
ctrlKey = ModifierKey::Disabled;
|
||||
altKey = ModifierKey::Disabled;
|
||||
shiftKey = ModifierKey::Disabled;
|
||||
}
|
||||
|
||||
inline bool operator==(const Modifiers& other) const
|
||||
{
|
||||
return winKey == other.winKey && ctrlKey == other.ctrlKey && altKey == other.altKey && shiftKey == other.shiftKey;
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "Shortcut.h"
|
||||
#include "Modifiers.h"
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
// This class stores all the variables associated with each shortcut remapping
|
||||
class RemapShortcut
|
||||
@@ -8,23 +10,24 @@ class RemapShortcut
|
||||
public:
|
||||
KeyShortcutTextUnion targetShortcut;
|
||||
bool isShortcutInvoked;
|
||||
ModifierKey winKeyInvoked;
|
||||
|
||||
Modifiers modifierKeysInvoked;
|
||||
// This bool value is only required for remapping shortcuts to Disable
|
||||
bool isOriginalActionKeyPressed;
|
||||
|
||||
RemapShortcut(const KeyShortcutTextUnion& sc) :
|
||||
targetShortcut(sc), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled), isOriginalActionKeyPressed(false)
|
||||
targetShortcut(sc), isShortcutInvoked(false), isOriginalActionKeyPressed(false)
|
||||
{
|
||||
}
|
||||
|
||||
RemapShortcut() :
|
||||
targetShortcut(Shortcut()), isShortcutInvoked(false), winKeyInvoked(ModifierKey::Disabled), isOriginalActionKeyPressed(false)
|
||||
targetShortcut(Shortcut()), isShortcutInvoked(false), isOriginalActionKeyPressed(false)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator==(const RemapShortcut& sc) const
|
||||
{
|
||||
return targetShortcut == sc.targetShortcut && isShortcutInvoked == sc.isShortcutInvoked && winKeyInvoked == sc.winKeyInvoked;
|
||||
return targetShortcut == sc.targetShortcut && isShortcutInvoked == sc.isShortcutInvoked && modifierKeysInvoked == sc.modifierKeysInvoked;
|
||||
}
|
||||
|
||||
bool RemapToKey()
|
||||
|
||||
@@ -185,7 +185,7 @@ DWORD Shortcut::GetWinKey(const ModifierKey& input) const
|
||||
}
|
||||
|
||||
// Function to return the virtual key code of the ctrl key state expected in the shortcut. Return NULL if it is not a part of the shortcut
|
||||
DWORD Shortcut::GetCtrlKey() const
|
||||
DWORD Shortcut::GetCtrlKey(const ModifierKey& input) const
|
||||
{
|
||||
if (ctrlKey == ModifierKey::Disabled)
|
||||
{
|
||||
@@ -201,12 +201,20 @@ DWORD Shortcut::GetCtrlKey() const
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input == ModifierKey::Right)
|
||||
{
|
||||
return VK_RCONTROL;
|
||||
}
|
||||
if (input == ModifierKey::Left)
|
||||
{
|
||||
return VK_LCONTROL;
|
||||
}
|
||||
return VK_CONTROL;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to return the virtual key code of the alt key state expected in the shortcut. Return NULL if it is not a part of the shortcut
|
||||
DWORD Shortcut::GetAltKey() const
|
||||
DWORD Shortcut::GetAltKey(const ModifierKey& input) const
|
||||
{
|
||||
if (altKey == ModifierKey::Disabled)
|
||||
{
|
||||
@@ -220,6 +228,14 @@ DWORD Shortcut::GetAltKey() const
|
||||
{
|
||||
return VK_RMENU;
|
||||
}
|
||||
if (input == ModifierKey::Right)
|
||||
{
|
||||
return VK_RMENU;
|
||||
}
|
||||
else if (input == ModifierKey::Left || input == ModifierKey::Disabled)
|
||||
{
|
||||
return VK_LMENU;
|
||||
}
|
||||
else
|
||||
{
|
||||
return VK_MENU;
|
||||
@@ -227,7 +243,7 @@ DWORD Shortcut::GetAltKey() const
|
||||
}
|
||||
|
||||
// Function to return the virtual key code of the shift key state expected in the shortcut. Return NULL if it is not a part of the shortcut
|
||||
DWORD Shortcut::GetShiftKey() const
|
||||
DWORD Shortcut::GetShiftKey(const ModifierKey& input) const
|
||||
{
|
||||
if (shiftKey == ModifierKey::Disabled)
|
||||
{
|
||||
@@ -243,6 +259,14 @@ DWORD Shortcut::GetShiftKey() const
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input == ModifierKey::Right)
|
||||
{
|
||||
return VK_RSHIFT;
|
||||
}
|
||||
if (input == ModifierKey::Left)
|
||||
{
|
||||
return VK_LSHIFT;
|
||||
}
|
||||
return VK_SHIFT;
|
||||
}
|
||||
}
|
||||
@@ -493,15 +517,15 @@ winrt::hstring Shortcut::ToHstringVK() const
|
||||
}
|
||||
if (ctrlKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + winrt::to_hstring(static_cast<unsigned int>(GetCtrlKey())) + winrt::to_hstring(L";");
|
||||
output = output + winrt::to_hstring(static_cast<unsigned int>(GetCtrlKey(ModifierKey::Both))) + winrt::to_hstring(L";");
|
||||
}
|
||||
if (altKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + winrt::to_hstring(static_cast<unsigned int>(GetAltKey())) + winrt::to_hstring(L";");
|
||||
output = output + winrt::to_hstring(static_cast<unsigned int>(GetAltKey(ModifierKey::Both))) + winrt::to_hstring(L";");
|
||||
}
|
||||
if (shiftKey != ModifierKey::Disabled)
|
||||
{
|
||||
output = output + winrt::to_hstring(static_cast<unsigned int>(GetShiftKey())) + winrt::to_hstring(L";");
|
||||
output = output + winrt::to_hstring(static_cast<unsigned int>(GetShiftKey(ModifierKey::Both))) + winrt::to_hstring(L";");
|
||||
}
|
||||
if (actionKey != NULL)
|
||||
{
|
||||
@@ -531,15 +555,15 @@ std::vector<DWORD> Shortcut::GetKeyCodes()
|
||||
}
|
||||
if (ctrlKey != ModifierKey::Disabled)
|
||||
{
|
||||
keys.push_back(GetCtrlKey());
|
||||
keys.push_back(GetCtrlKey(ModifierKey::Both));
|
||||
}
|
||||
if (altKey != ModifierKey::Disabled)
|
||||
{
|
||||
keys.push_back(GetAltKey());
|
||||
keys.push_back(GetAltKey(ModifierKey::Both));
|
||||
}
|
||||
if (shiftKey != ModifierKey::Disabled)
|
||||
{
|
||||
keys.push_back(GetShiftKey());
|
||||
keys.push_back(GetShiftKey(ModifierKey::Both));
|
||||
}
|
||||
if (actionKey != NULL)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <compare>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
namespace KeyboardManagerInput
|
||||
{
|
||||
class InputInterface;
|
||||
@@ -142,13 +141,13 @@ public:
|
||||
DWORD GetWinKey(const ModifierKey& input) const;
|
||||
|
||||
// Function to return the virtual key code of the ctrl key state expected in the shortcut. Return NULL if it is not a part of the shortcut
|
||||
DWORD GetCtrlKey() const;
|
||||
DWORD GetCtrlKey(const ModifierKey& input) const;
|
||||
|
||||
// Function to return the virtual key code of the alt key state expected in the shortcut. Return NULL if it is not a part of the shortcut
|
||||
DWORD GetAltKey() const;
|
||||
DWORD GetAltKey(const ModifierKey& input) const;
|
||||
|
||||
// Function to return the virtual key code of the shift key state expected in the shortcut. Return NULL if it is not a part of the shortcut
|
||||
DWORD GetShiftKey() const;
|
||||
DWORD GetShiftKey(const ModifierKey& input) const;
|
||||
|
||||
// Function to check if the input key matches the win key expected in the shortcut
|
||||
bool CheckWinKey(const DWORD input) const;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Microsoft.Plugin.Program.Programs
|
||||
public ApplicationType AppType { get; set; }
|
||||
|
||||
// Wrappers for File Operations
|
||||
public static IFileVersionInfoWrapper FileVersionInfoWrapper { get; set; } = new FileVersionInfoWrapper();
|
||||
public static IFileVersionInfoWrapper FileVersionInfoWrapper { get; set; } = new Wox.Infrastructure.FileSystemHelper.FileVersionInfoWrapper();
|
||||
|
||||
public static IFile FileWrapper { get; set; } = new FileSystem().File;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Components;
|
||||
@@ -23,7 +24,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
var result = settings?.Length;
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(6, result);
|
||||
Assert.AreEqual(7, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
@@ -33,6 +34,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[DataRow("TimeWithSeconds")]
|
||||
[DataRow("DateWithWeekday")]
|
||||
[DataRow("HideNumberMessageOnGlobalQuery")]
|
||||
[DataRow("CustomFormats")]
|
||||
public void DoesSettingExist(string name)
|
||||
{
|
||||
// Setup
|
||||
@@ -78,5 +80,20 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
// Assert
|
||||
Assert.AreEqual(valueExpected, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("CustomFormats")]
|
||||
public void DefaultEmptyMultilineTextValues(string name)
|
||||
{
|
||||
// Setup
|
||||
TimeDateSettings setting = TimeDateSettings.Instance;
|
||||
|
||||
// Act
|
||||
PropertyInfo propertyInfo = setting?.GetType()?.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
List<string> result = (List<string>)propertyInfo?.GetValue(setting);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, result.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("(time", 18)]
|
||||
[DataRow("(date", 26)]
|
||||
[DataRow("(year", 7)]
|
||||
[DataRow("(now", 32)]
|
||||
[DataRow("(current", 32)]
|
||||
[DataRow("(", 32)]
|
||||
[DataRow("(date", 28)]
|
||||
[DataRow("(year", 8)]
|
||||
[DataRow("(now", 34)]
|
||||
[DataRow("(current", 34)]
|
||||
[DataRow("(", 34)]
|
||||
[DataRow("(now::10:10:10", 1)] // Windows file time
|
||||
[DataRow("(current::10:10:10", 0)]
|
||||
public void CountWithPluginKeyword(string typedString, int expectedResultCount)
|
||||
@@ -140,6 +140,8 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[DataRow("(week day", "Day of the week (Week day) -")]
|
||||
[DataRow("(cal week", "Week of the year (Calendar week, Week number) -")]
|
||||
[DataRow("(week num", "Week of the year (Calendar week, Week number) -")]
|
||||
[DataRow("(days in mo", "Days in month -")]
|
||||
[DataRow("(Leap y", "Leap year -")]
|
||||
public void CanFindFormatResult(string typedString, string expectedResult)
|
||||
{
|
||||
// Setup
|
||||
|
||||
@@ -41,10 +41,29 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[DataRow("ums-10456", true, "", "")] // Value is UTC and can be different based on system
|
||||
[DataRow("ums+10456", true, "", "")] // Value is UTC and can be different based on system
|
||||
[DataRow("ft10456", true, "", "")] // Value is UTC and can be different based on system
|
||||
[DataRow("oa-657434.99999999", true, "G", "1/1/0100 11:59:59 PM")]
|
||||
[DataRow("oa2958465.99999999", true, "G", "12/31/9999 11:59:59 PM")]
|
||||
[DataRow("oa-657435", false, "", "")] // Value to low
|
||||
[DataRow("oa2958466", false, "", "")] // Value to large
|
||||
[DataRow("exc1.99998843", true, "G", "1/1/1900 11:59:59 PM")]
|
||||
[DataRow("exc59.99998843", true, "G", "2/28/1900 11:59:59 PM")]
|
||||
[DataRow("exc61", true, "G", "3/1/1900 12:00:00 AM")]
|
||||
[DataRow("exc62.99998843", true, "G", "3/2/1900 11:59:59 PM")]
|
||||
[DataRow("exc2958465.99998843", true, "G", "12/31/9999 11:59:59 PM")]
|
||||
[DataRow("exc0", false, "", "")] // Day 0 means in Excel 0/1/1900 and this is a fake date.
|
||||
[DataRow("exc0.99998843", false, "", "")] // Day 0 means in Excel 0/1/1900 and this is a fake date.
|
||||
[DataRow("exc60.99998843", false, "", "")] // Day 60 means in Excel 2/29/1900 and this is a fake date in Excel which we cannot support.
|
||||
[DataRow("exc60", false, "", "")] // Day 60 means in Excel 2/29/1900 and this is a fake date in Excel which we cannot support.
|
||||
[DataRow("exc-1", false, "", "")] // Value to low
|
||||
[DataRow("exc2958466", false, "", "")] // Value to large
|
||||
[DataRow("exf0.99998843", true, "G", "1/1/1904 11:59:59 PM")]
|
||||
[DataRow("exf2957003.99998843", true, "G", "12/31/9999 11:59:59 PM")]
|
||||
[DataRow("exf-0.5", false, "", "")] // Value to low
|
||||
[DataRow("exf2957004", false, "", "")] // Value to large
|
||||
public void ConvertStringToDateTime(string typedString, bool expectedBool, string stringType, string expectedString)
|
||||
{
|
||||
// Act
|
||||
bool boolResult = TimeAndDateHelper.ParseStringAsDateTime(in typedString, out DateTime result);
|
||||
bool boolResult = TimeAndDateHelper.ParseStringAsDateTime(in typedString, out DateTime result, out string _);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(expectedBool, boolResult);
|
||||
|
||||
@@ -13,6 +13,19 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[TestClass]
|
||||
public class TimeAndDateHelperTests
|
||||
{
|
||||
private CultureInfo originalCulture;
|
||||
private CultureInfo originalUiCulture;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
// Set culture to 'en-us'
|
||||
originalCulture = CultureInfo.CurrentCulture;
|
||||
CultureInfo.CurrentCulture = new CultureInfo("en-us", false);
|
||||
originalUiCulture = CultureInfo.CurrentUICulture;
|
||||
CultureInfo.CurrentUICulture = new CultureInfo("en-us", false);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(-1, null)] // default setting
|
||||
[DataRow(0, CalendarWeekRule.FirstDay)]
|
||||
@@ -62,5 +75,103 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
Assert.AreEqual(valueExpected, result);
|
||||
}
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0, "12/30/1899 12:00 PM", 0.5)] // OLE Automation date
|
||||
[DataRow(1, "12/31/1898 12:00 PM", null)] // Excel based 1900: Date to low
|
||||
[DataRow(1, "1/1/1900, 00:00 AM", 1.0)] // Excel based 1900
|
||||
[DataRow(2, "12/31/1898 12:00 PM", null)] // Excel based 1904: Date to low
|
||||
[DataRow(2, "1/1/1904, 00:00 AM", 0.0)] // Excel based 1904
|
||||
public void ConvertToOADateFormat(int type, string date, double? valueExpected)
|
||||
{
|
||||
// Act
|
||||
DateTime dt = DateTime.Parse(date, DateTimeFormatInfo.CurrentInfo);
|
||||
|
||||
// Assert
|
||||
if (valueExpected == null)
|
||||
{
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => TimeAndDateHelper.ConvertToOleAutomationFormat(dt, (OADateFormats)type));
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = TimeAndDateHelper.ConvertToOleAutomationFormat(dt, (OADateFormats)type);
|
||||
Assert.AreEqual(valueExpected, result);
|
||||
}
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("dow")]
|
||||
[DataRow("\\DOW")]
|
||||
[DataRow("wom")]
|
||||
[DataRow("\\WOM")]
|
||||
[DataRow("woy")]
|
||||
[DataRow("\\WOY")]
|
||||
[DataRow("eab")]
|
||||
[DataRow("\\EAB")]
|
||||
[DataRow("wft")]
|
||||
[DataRow("\\WFT")]
|
||||
[DataRow("uxt")]
|
||||
[DataRow("\\UXT")]
|
||||
[DataRow("ums")]
|
||||
[DataRow("\\UMS")]
|
||||
[DataRow("oad")]
|
||||
[DataRow("\\OAD")]
|
||||
[DataRow("exc")]
|
||||
[DataRow("\\EXC")]
|
||||
[DataRow("exf")]
|
||||
[DataRow("\\EXF")]
|
||||
[DataRow("My super Test String with \\EXC pattern.")]
|
||||
public void CustomFormatIgnoreInvalidPattern(string format)
|
||||
{
|
||||
// Act
|
||||
string result = TimeAndDateHelper.ConvertToCustomFormat(DateTime.Now, 0, 0, 1, "AD", format, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(format, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("DOW")]
|
||||
[DataRow("DIM")]
|
||||
[DataRow("WOM")]
|
||||
[DataRow("WOY")]
|
||||
[DataRow("EAB")]
|
||||
[DataRow("WFT")]
|
||||
[DataRow("UXT")]
|
||||
[DataRow("UMS")]
|
||||
[DataRow("OAD")]
|
||||
[DataRow("EXC")]
|
||||
[DataRow("EXF")]
|
||||
public void CustomFormatReplacesValidPattern(string format)
|
||||
{
|
||||
// Act
|
||||
string result = TimeAndDateHelper.ConvertToCustomFormat(DateTime.Now, 0, 0, 1, "AD", format, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Contains(format, StringComparison.CurrentCulture));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("01/01/0001", 1)] // First possible date
|
||||
[DataRow("12/31/9999", 5)] // Last possible date
|
||||
[DataRow("03/20/2025", 4)]
|
||||
[DataRow("09/01/2025", 1)] // First day in month is first day of week
|
||||
[DataRow("03/03/2025", 2)] // First monday is in second week
|
||||
public void GetWeekOfMonth(string date, int week)
|
||||
{
|
||||
// Act
|
||||
int result = TimeAndDateHelper.GetWeekOfMonth(DateTime.Parse(date, CultureInfo.GetCultureInfo("en-us")), DayOfWeek.Monday);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(result, week);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void CleanUp()
|
||||
{
|
||||
// Set culture to original value
|
||||
CultureInfo.CurrentCulture = originalCulture;
|
||||
CultureInfo.CurrentUICulture = originalUiCulture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal class AvailableResult
|
||||
internal sealed class AvailableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the time/date value
|
||||
@@ -41,6 +41,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
ResultIconType.Time => $"Images\\time.{theme}.png",
|
||||
ResultIconType.Date => $"Images\\calendar.{theme}.png",
|
||||
ResultIconType.DateTime => $"Images\\timeDate.{theme}.png",
|
||||
ResultIconType.Error => $"Images\\Warning.{theme}.png",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -51,5 +52,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
Time,
|
||||
Date,
|
||||
DateTime,
|
||||
Error,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
@@ -72,6 +72,86 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
string era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow));
|
||||
string eraShort = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(calendar.GetEra(dateTimeNow));
|
||||
|
||||
// Custom formats
|
||||
foreach (string f in TimeDateSettings.Instance.CustomFormats)
|
||||
{
|
||||
string[] formatParts = f.Split("=", 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
string formatSyntax = formatParts.Length == 2 ? formatParts[1] : string.Empty;
|
||||
string searchTags = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagCustom");
|
||||
DateTime dtObject = dateTimeNow;
|
||||
|
||||
// If Length = 0 then empty string.
|
||||
if (formatParts.Length >= 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Verify and check input and update search tags
|
||||
if (formatParts.Length == 1)
|
||||
{
|
||||
throw new FormatException("Format syntax part after equal sign is missing.");
|
||||
}
|
||||
|
||||
bool containsCustomSyntax = TimeAndDateHelper.StringContainsCustomFormatSyntax(formatSyntax);
|
||||
if (formatSyntax.StartsWith("UTC:", StringComparison.InvariantCulture))
|
||||
{
|
||||
searchTags = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagCustomUtc");
|
||||
dtObject = dateTimeNowUtc;
|
||||
}
|
||||
|
||||
// Get formated date
|
||||
var value = TimeAndDateHelper.ConvertToCustomFormat(dtObject, unixTimestamp, unixTimestampMilliseconds, weekOfYear, eraShort, Regex.Replace(formatSyntax, "^UTC:", string.Empty), firstWeekRule, firstDayOfTheWeek);
|
||||
try
|
||||
{
|
||||
value = dtObject.ToString(value, CultureInfo.CurrentCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (!containsCustomSyntax)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not fail as we have custom format syntax. Instead fix backslashes.
|
||||
value = Regex.Replace(value, @"(?<!\\)\\", string.Empty).Replace("\\\\", "\\");
|
||||
}
|
||||
}
|
||||
|
||||
// Add result
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = value,
|
||||
Label = formatParts[0],
|
||||
AlternativeSearchTag = searchTags,
|
||||
IconType = ResultIconType.DateTime,
|
||||
});
|
||||
}
|
||||
catch (ArgumentOutOfRangeException e)
|
||||
{
|
||||
Wox.Plugin.Logger.Log.Exception($"Failed to convert into custom format {formatParts[0]}: {formatSyntax}", e, typeof(AvailableResultsList));
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = Resources.Microsoft_plugin_timedate_ErrorConvertCustomFormat + " " + e.Message,
|
||||
Label = formatParts[0],
|
||||
AlternativeSearchTag = searchTags,
|
||||
IconType = ResultIconType.Error,
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Wox.Plugin.Logger.Log.Exception($"Failed to convert into custom format {formatParts[0]}: {formatSyntax}", e, typeof(AvailableResultsList));
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = Resources.Microsoft_plugin_timedate_InvalidCustomFormat + " " + formatSyntax,
|
||||
Label = formatParts[0],
|
||||
AlternativeSearchTag = searchTags,
|
||||
IconType = ResultIconType.Error,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined formats
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new AvailableResult()
|
||||
@@ -152,6 +232,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = DateTime.DaysInMonth(dateTimeNow.Year, dateTimeNow.Month).ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DaysInMonth,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.DayOfYear.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DayOfYear,
|
||||
@@ -201,6 +288,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = DateTime.IsLeapYear(dateTimeNow.Year) ? Resources.Microsoft_plugin_timedate_LeapYear : Resources.Microsoft_plugin_timedate_NoLeapYear,
|
||||
Label = Resources.Microsoft_plugin_timedate_LeapYear,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = era,
|
||||
Label = Resources.Microsoft_plugin_timedate_Era,
|
||||
@@ -221,13 +315,31 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToFileTime().ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_WindowsFileTime,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = Resources.Microsoft_plugin_timedate_ErrorConvertWft,
|
||||
Label = Resources.Microsoft_plugin_timedate_WindowsFileTime,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.Error,
|
||||
});
|
||||
}
|
||||
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString("u"),
|
||||
|
||||
@@ -83,10 +83,10 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
/// Gets a result with an error message that only numbers can't be parsed
|
||||
/// </summary>
|
||||
/// <returns>Element of type <see cref="Result"/>.</returns>
|
||||
internal static Result CreateNumberErrorResult(string theme) => new Result()
|
||||
internal static Result CreateNumberErrorResult(string theme, string title, string subtitle) => new Result()
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_timedate_ErrorResultTitle,
|
||||
SubTitle = Resources.Microsoft_plugin_timedate_ErrorResultSubTitle,
|
||||
Title = title,
|
||||
SubTitle = subtitle,
|
||||
ToolTipData = new ToolTipData(Resources.Microsoft_plugin_timedate_ErrorResultTitle, Resources.Microsoft_plugin_timedate_ErrorResultSubTitle),
|
||||
IcoPath = $"Images\\Warning.{theme}.png",
|
||||
};
|
||||
|
||||
@@ -40,9 +40,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
List<AvailableResult> availableFormats = new List<AvailableResult>();
|
||||
List<Result> results = new List<Result>();
|
||||
bool isKeywordSearch = !string.IsNullOrEmpty(query.ActionKeyword);
|
||||
bool isEmptySearchInput = string.IsNullOrEmpty(query.Search);
|
||||
bool isEmptySearchInput = string.IsNullOrWhiteSpace(query.Search);
|
||||
string searchTerm = query.Search;
|
||||
|
||||
// Last input parsing error
|
||||
string lastInputParsingErrorReason = string.Empty;
|
||||
|
||||
// Conjunction search without keyword => return no results
|
||||
// (This improves the results on global queries.)
|
||||
if (!isKeywordSearch && _conjunctionList.Any(x => x.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)))
|
||||
@@ -61,13 +64,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
// Search for specified format with specified time/date value
|
||||
var userInput = searchTerm.Split(InputDelimiter);
|
||||
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp))
|
||||
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp, out lastInputParsingErrorReason))
|
||||
{
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
||||
searchTerm = userInput[0];
|
||||
}
|
||||
}
|
||||
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp))
|
||||
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp, out lastInputParsingErrorReason))
|
||||
{
|
||||
// Return all formats for specified time/date value
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
||||
@@ -122,12 +125,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
}
|
||||
|
||||
// If search term is only a number that can't be parsed return an error message
|
||||
if (!isEmptySearchInput && results.Count == 0 && Regex.IsMatch(searchTerm, @"\w+\d+.*$") && !searchTerm.Any(char.IsWhiteSpace) && (TimeAndDateHelper.IsSpecialInputParsing(searchTerm) || !Regex.IsMatch(searchTerm, @"\d+[\.:/]\d+")))
|
||||
if (!isEmptySearchInput && results.Count == 0 && Regex.IsMatch(searchTerm, @"\w+[+-]?\d+.*$") && !searchTerm.Any(char.IsWhiteSpace) && (TimeAndDateHelper.IsSpecialInputParsing(searchTerm) || !Regex.IsMatch(searchTerm, @"\d+[\.:/]\d+")))
|
||||
{
|
||||
// Without plugin key word show only if message is not hidden by setting
|
||||
string title = !string.IsNullOrEmpty(lastInputParsingErrorReason) ? Resources.Microsoft_plugin_timedate_ErrorResultValue : Resources.Microsoft_plugin_timedate_ErrorResultTitle;
|
||||
string message = !string.IsNullOrEmpty(lastInputParsingErrorReason) ? lastInputParsingErrorReason : Resources.Microsoft_plugin_timedate_ErrorResultSubTitle;
|
||||
|
||||
// Without plugin key word show only if not hidden by setting
|
||||
if (isKeywordSearch || !TimeDateSettings.Instance.HideNumberMessageOnGlobalQuery)
|
||||
{
|
||||
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme));
|
||||
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme, title, message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
|
||||
|
||||
@@ -13,6 +15,33 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal static class TimeAndDateHelper
|
||||
{
|
||||
private static readonly Regex _regexSpecialInputFormats = new Regex(@"^.*(::)?(u|ums|ft|oa|exc|exf)[+-]\d");
|
||||
private static readonly Regex _regexCustomDateTimeFormats = new Regex(@"(?<!\\)(DOW|DIM|WOM|WOY|EAB|WFT|UXT|UMS|OAD|EXC|EXF)");
|
||||
private static readonly Regex _regexCustomDateTimeDow = new Regex(@"(?<!\\)DOW");
|
||||
private static readonly Regex _regexCustomDateTimeDim = new Regex(@"(?<!\\)DIM");
|
||||
private static readonly Regex _regexCustomDateTimeWom = new Regex(@"(?<!\\)WOM");
|
||||
private static readonly Regex _regexCustomDateTimeWoy = new Regex(@"(?<!\\)WOY");
|
||||
private static readonly Regex _regexCustomDateTimeEab = new Regex(@"(?<!\\)EAB");
|
||||
private static readonly Regex _regexCustomDateTimeWft = new Regex(@"(?<!\\)WFT");
|
||||
private static readonly Regex _regexCustomDateTimeUxt = new Regex(@"(?<!\\)UXT");
|
||||
private static readonly Regex _regexCustomDateTimeUms = new Regex(@"(?<!\\)UMS");
|
||||
private static readonly Regex _regexCustomDateTimeOad = new Regex(@"(?<!\\)OAD");
|
||||
private static readonly Regex _regexCustomDateTimeExc = new Regex(@"(?<!\\)EXC");
|
||||
private static readonly Regex _regexCustomDateTimeExf = new Regex(@"(?<!\\)EXF");
|
||||
|
||||
private const long UnixTimeSecondsMin = -62135596800;
|
||||
private const long UnixTimeSecondsMax = 253402300799;
|
||||
private const long UnixTimeMillisecondsMin = -62135596800000;
|
||||
private const long UnixTimeMillisecondsMax = 253402300799999;
|
||||
private const long WindowsFileTimeMin = 0;
|
||||
private const long WindowsFileTimeMax = 2650467707991000000;
|
||||
private const double OADateMin = -657434.99999999;
|
||||
private const double OADateMax = 2958465.99999999;
|
||||
private const double Excel1900DateMin = 1;
|
||||
private const double Excel1900DateMax = 2958465.99998843;
|
||||
private const double Excel1904DateMin = 0;
|
||||
private const double Excel1904DateMax = 2957003.99998843;
|
||||
|
||||
/// <summary>
|
||||
/// Get the format for the time string
|
||||
/// </summary>
|
||||
@@ -56,18 +85,25 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
/// Returns the number week in the month (Used code from 'David Morton' from <see href="https://social.msdn.microsoft.com/Forums/vstudio/bf504bba-85cb-492d-a8f7-4ccabdf882cb/get-week-number-for-month"/>)
|
||||
/// </summary>
|
||||
/// <param name="date">date</param>
|
||||
/// <param name="formatSettingFirstDayOfWeek">Setting for the first day in the week.</param>
|
||||
/// <returns>Number of week in the month</returns>
|
||||
internal static int GetWeekOfMonth(DateTime date, DayOfWeek formatSettingFirstDayOfWeek)
|
||||
{
|
||||
DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1);
|
||||
int adjustment = 1; // We count from 1 to 7 and not from 0 to 6
|
||||
int weekCount = 1;
|
||||
|
||||
while (date.Date.AddDays(1).DayOfWeek != formatSettingFirstDayOfWeek)
|
||||
for (int i = 1; i <= date.Day; i++)
|
||||
{
|
||||
date = date.AddDays(1);
|
||||
DateTime d = new(date.Year, date.Month, i);
|
||||
|
||||
// Count week number +1 if day is the first day of a week and not day 1 of the month.
|
||||
// (If we count on day one of a month we would start the month with week number 2.)
|
||||
if (i > 1 && d.DayOfWeek == formatSettingFirstDayOfWeek)
|
||||
{
|
||||
weekCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + adjustment;
|
||||
return weekCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,40 +119,170 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
return ((date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment;
|
||||
}
|
||||
|
||||
internal static double ConvertToOleAutomationFormat(DateTime date, OADateFormats type)
|
||||
{
|
||||
double v = date.ToOADate();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case OADateFormats.Excel1904:
|
||||
// Excel with base 1904: Adjust by -1462
|
||||
v -= 1462;
|
||||
|
||||
// Date starts at 1/1/1904 = 0
|
||||
if (Math.Truncate(v) < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Not a valid Excel date.", innerException: null);
|
||||
}
|
||||
|
||||
return v;
|
||||
case OADateFormats.Excel1900:
|
||||
// Excel with base 1900: Adjust by -1 if v < 61
|
||||
v = v < 61 ? v - 1 : v;
|
||||
|
||||
// Date starts at 1/1/1900 = 1
|
||||
if (Math.Truncate(v) < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Not a valid Excel date.", innerException: null);
|
||||
}
|
||||
|
||||
return v;
|
||||
default:
|
||||
// OLE Automation date: Return as is.
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert input string to a <see cref="DateTime"/> object in local time
|
||||
/// </summary>
|
||||
/// <param name="input">String with date/time</param>
|
||||
/// <param name="timestamp">The new <see cref="DateTime"/> object</param>
|
||||
/// <param name="inputParsingErrorMsg">Error message shown to the user</param>
|
||||
/// <returns>True on success, otherwise false</returns>
|
||||
internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp)
|
||||
internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp, out string inputParsingErrorMsg)
|
||||
{
|
||||
inputParsingErrorMsg = string.Empty;
|
||||
CompositeFormat errorMessage = CompositeFormat.Parse(Resources.Microsoft_plugin_timedate_InvalidInput_SupportedRange);
|
||||
|
||||
if (DateTime.TryParse(input, out timestamp))
|
||||
{
|
||||
// Known date/time format
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^u[\+-]?\d{1,10}$") && long.TryParse(input.TrimStart('u'), out long secondsU))
|
||||
else if (Regex.IsMatch(input, @"^u[\+-]?\d+$"))
|
||||
{
|
||||
// Unix time stamp
|
||||
// We use long instead of int, because int is too small after 03:14:07 UTC 2038-01-19
|
||||
var canParse = long.TryParse(input.TrimStart('u'), out var secondsU);
|
||||
|
||||
// Value has to be in the range from -62135596800 to 253402300799
|
||||
if (!canParse || secondsU < UnixTimeSecondsMin || secondsU > UnixTimeSecondsMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Unix, UnixTimeSecondsMin, UnixTimeSecondsMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTimeOffset.FromUnixTimeSeconds(secondsU).LocalDateTime;
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^ums[\+-]?\d{1,13}$") && long.TryParse(input.TrimStart("ums".ToCharArray()), out long millisecondsUms))
|
||||
else if (Regex.IsMatch(input, @"^ums[\+-]?\d+$"))
|
||||
{
|
||||
// Unix time stamp in milliseconds
|
||||
// We use long instead of int because int is too small after 03:14:07 UTC 2038-01-19
|
||||
var canParse = long.TryParse(input.TrimStart("ums".ToCharArray()), out var millisecondsUms);
|
||||
|
||||
// Value has to be in the range from -62135596800000 to 253402300799999
|
||||
if (!canParse || millisecondsUms < UnixTimeMillisecondsMin || millisecondsUms > UnixTimeMillisecondsMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Unix_Milliseconds, UnixTimeMillisecondsMin, UnixTimeMillisecondsMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTimeOffset.FromUnixTimeMilliseconds(millisecondsUms).LocalDateTime;
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^ft\d+$") && long.TryParse(input.TrimStart("ft".ToCharArray()), out long secondsFt))
|
||||
else if (Regex.IsMatch(input, @"^ft\d+$"))
|
||||
{
|
||||
var canParse = long.TryParse(input.TrimStart("ft".ToCharArray()), out var secondsFt);
|
||||
|
||||
// Windows file time
|
||||
// Value has to be in the range from 0 to 2650467707991000000
|
||||
if (!canParse || secondsFt < WindowsFileTimeMin || secondsFt > WindowsFileTimeMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_WindowsFileTime, WindowsFileTimeMin, WindowsFileTimeMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DateTime.FromFileTime returns as local time.
|
||||
timestamp = DateTime.FromFileTime(secondsFt);
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^oa[+-]?\d+[,.0-9]*$"))
|
||||
{
|
||||
var canParse = double.TryParse(input.TrimStart("oa".ToCharArray()), out var oADate);
|
||||
|
||||
// OLE Automation date
|
||||
// Input has to be in the range from -657434.99999999 to 2958465.99999999
|
||||
// DateTime.FromOADate returns as local time.
|
||||
if (!canParse || oADate < OADateMin || oADate > OADateMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_OADate, OADateMin, OADateMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTime.FromOADate(oADate);
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^exc[+-]?\d+[,.0-9]*$"))
|
||||
{
|
||||
var canParse = double.TryParse(input.TrimStart("exc".ToCharArray()), out var excDate);
|
||||
|
||||
// Excel's 1900 date value
|
||||
// Input has to be in the range from 1 (0 = Fake date) to 2958465.99998843 and not 60 whole number
|
||||
// Because of a bug in Excel and the way it behaves before 3/1/1900 we have to adjust all inputs lower than 61 for +1
|
||||
// DateTime.FromOADate returns as local time.
|
||||
if (!canParse || excDate < 0 || excDate > Excel1900DateMax)
|
||||
{
|
||||
// For the if itself we use 0 as min value that we can show a special message if input is 0.
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Excel1900, Excel1900DateMin, Excel1900DateMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Math.Truncate(excDate) == 0 || Math.Truncate(excDate) == 60)
|
||||
{
|
||||
inputParsingErrorMsg = Resources.Microsoft_plugin_timedate_InvalidInput_FakeExcel1900;
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
excDate = excDate <= 60 ? excDate + 1 : excDate;
|
||||
timestamp = DateTime.FromOADate(excDate);
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^exf[+-]?\d+[,.0-9]*$"))
|
||||
{
|
||||
var canParse = double.TryParse(input.TrimStart("exf".ToCharArray()), out var exfDate);
|
||||
|
||||
// Excel's 1904 date value
|
||||
// Input has to be in the range from 0 to 2957003.99998843
|
||||
// Because Excel uses 01/01/1904 as base we need to adjust for +1462
|
||||
// DateTime.FromOADate returns as local time.
|
||||
if (!canParse || exfDate < Excel1904DateMin || exfDate > Excel1904DateMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Excel1904, Excel1904DateMin, Excel1904DateMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTime.FromOADate(exfDate + 1462);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
@@ -125,13 +291,85 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if input is special parsing for Unix time, Unix time in milliseconds or File time.
|
||||
/// Test if input is special parsing for Unix time, Unix time in milliseconds, file time, ...
|
||||
/// </summary>
|
||||
/// <param name="input">String with date/time</param>
|
||||
/// <returns>True if yes, otherwise false</returns>
|
||||
internal static bool IsSpecialInputParsing(string input)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^.*(u|ums|ft)\d");
|
||||
return _regexSpecialInputFormats.IsMatch(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a DateTime object based on the format string
|
||||
/// </summary>
|
||||
/// <param name="date">Date/time object.</param>
|
||||
/// <param name="unix">Value for replacing "Unix Time Stamp".</param>
|
||||
/// <param name="unixMilliseconds">Value for replacing "Unix Time Stamp in milliseconds".</param>
|
||||
/// <param name="calWeek">Value for relacing calendar week.</param>
|
||||
/// <param name="eraShortFormat">Era abbreviation.</param>
|
||||
/// <param name="format">Format definition.</param>
|
||||
/// <returns>Formated date/time string.</returns>
|
||||
internal static string ConvertToCustomFormat(DateTime date, long unix, long unixMilliseconds, int calWeek, string eraShortFormat, string format, CalendarWeekRule firstWeekRule, DayOfWeek firstDayOfTheWeek)
|
||||
{
|
||||
string result = format;
|
||||
|
||||
// DOW: Number of day in week
|
||||
result = _regexCustomDateTimeDow.Replace(result, GetNumberOfDayInWeek(date, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// DIM: Days in Month
|
||||
result = _regexCustomDateTimeDim.Replace(result, DateTime.DaysInMonth(date.Year, date.Month).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// WOM: Week of Month
|
||||
result = _regexCustomDateTimeWom.Replace(result, GetWeekOfMonth(date, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// WOY: Week of Year
|
||||
result = _regexCustomDateTimeWoy.Replace(result, calWeek.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// EAB: Era abbreviation
|
||||
result = _regexCustomDateTimeEab.Replace(result, eraShortFormat);
|
||||
|
||||
// WFT: Week of Month
|
||||
if (_regexCustomDateTimeWft.IsMatch(result))
|
||||
{
|
||||
// Special handling as very early dates can't convert.
|
||||
result = _regexCustomDateTimeWft.Replace(result, date.ToFileTime().ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
// UXT: Unix time stamp
|
||||
result = _regexCustomDateTimeUxt.Replace(result, unix.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// UMS: Unix time stamp milli seconds
|
||||
result = _regexCustomDateTimeUms.Replace(result, unixMilliseconds.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// OAD: OLE Automation date
|
||||
result = _regexCustomDateTimeOad.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.OLEAutomation).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// EXC: Excel date value with base 1900
|
||||
if (_regexCustomDateTimeExc.IsMatch(result))
|
||||
{
|
||||
// Special handling as very early dates can't convert.
|
||||
result = _regexCustomDateTimeExc.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.Excel1900).ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
// EXF: Excel date value with base 1904
|
||||
if (_regexCustomDateTimeExf.IsMatch(result))
|
||||
{
|
||||
// Special handling as very early dates can't convert.
|
||||
result = _regexCustomDateTimeExf.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.Excel1904).ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test a string for our custom date and time format syntax
|
||||
/// </summary>
|
||||
/// <param name="str">String to test.</param>
|
||||
/// <returns>True if yes and otherwise false</returns>
|
||||
internal static bool StringContainsCustomFormatSyntax(string str)
|
||||
{
|
||||
return _regexCustomDateTimeFormats.IsMatch(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -190,4 +428,14 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
Date,
|
||||
DateTime,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Different versions of Date formats based on OLE Automation date
|
||||
/// </summary>
|
||||
internal enum OADateFormats
|
||||
{
|
||||
OLEAutomation,
|
||||
Excel1900,
|
||||
Excel1904,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
/// </summary>
|
||||
internal bool HideNumberMessageOnGlobalQuery { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value containing the custom format definitions
|
||||
/// </summary>
|
||||
internal List<string> CustomFormats { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimeDateSettings"/> class.
|
||||
/// Private constructor to make sure there is never more than one instance of this class
|
||||
@@ -100,29 +105,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
var optionList = new List<PluginAdditionalOption>
|
||||
{
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(CalendarFirstWeekRule),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule,
|
||||
DisplayDescription = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_Description,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay, "0"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek, "1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek, "2"),
|
||||
},
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(FirstDayOfWeek),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = GetSortedListForWeekDaySetting(),
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(OnlyDateTimeNowGlobal),
|
||||
@@ -150,6 +132,38 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery,
|
||||
Value = false,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(CalendarFirstWeekRule),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule,
|
||||
DisplayDescription = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_Description,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay, "0"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek, "1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek, "2"),
|
||||
},
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(FirstDayOfWeek),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = GetSortedListForWeekDaySetting(),
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(CustomFormats),
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.MultilineTextbox,
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_Setting_CustomFormats,
|
||||
DisplayDescription = string.Format(CultureInfo.CurrentCulture, Resources.Microsoft_plugin_timedate_Setting_CustomFormatsDescription.ToString(), "DOW", "DIM", "WOM", "WOY", "EAB", "WFT", "UXT", "UMS", "OAD", "EXC", "EXF", "UTC:"),
|
||||
PlaceholderText = "MyFormat=dd-MMM-yyyy\rMySecondFormat=dddd (Da\\y nu\\mber: DOW)\rMyUtcFormat=UTC:hh:mm:ss",
|
||||
TextValue = string.Empty,
|
||||
},
|
||||
};
|
||||
|
||||
return optionList;
|
||||
@@ -172,6 +186,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
TimeWithSeconds = GetSettingOrDefault(settings, nameof(TimeWithSeconds));
|
||||
DateWithWeekday = GetSettingOrDefault(settings, nameof(DateWithWeekday));
|
||||
HideNumberMessageOnGlobalQuery = GetSettingOrDefault(settings, nameof(HideNumberMessageOnGlobalQuery));
|
||||
CustomFormats = GetMultilineTextSettingOrDefault(settings, nameof(CustomFormats));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -204,6 +219,21 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
return option?.ComboBoxValue ?? GetAdditionalOptions().First(x => x.Key == name).ComboBoxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the combobox value of the given settings list with the given name.
|
||||
/// </summary>
|
||||
/// <param name="settings">The object that contain all settings.</param>
|
||||
/// <param name="name">The name of the setting.</param>
|
||||
/// <returns>A settings value.</returns>
|
||||
private static List<string> GetMultilineTextSettingOrDefault(PowerLauncherPluginSettings settings, string name)
|
||||
{
|
||||
var option = settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == name);
|
||||
|
||||
// If a setting isn't available, we use the value defined in the method GetAdditionalOptions() as fallback.
|
||||
// We can use First() instead of FirstOrDefault() because the values must exist. Otherwise, we made a mistake when defining the settings.
|
||||
return option?.TextValueAsMultilineList ?? GetAdditionalOptions().First(x => x.Key == name).TextValueAsMultilineList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a sorted list of values for the combo box of 'first day of week' setting.
|
||||
/// The list is sorted based on the current system culture setting.
|
||||
|
||||
@@ -150,6 +150,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Days in month.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_DaysInMonth {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_DaysInMonth", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Era.
|
||||
/// </summary>
|
||||
@@ -169,7 +178,25 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time.
|
||||
/// Looks up a localized string similar to Failed to convert into custom format:.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorConvertCustomFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorConvertCustomFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not a valid Windows file time.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorConvertWft {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorConvertWft", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time, 'oa' for OLE Automation date, 'exc' for Excel's 1900 date value, 'exf' for Excel's 1904 date value.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorResultSubTitle {
|
||||
get {
|
||||
@@ -178,7 +205,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error: Invalid number input.
|
||||
/// Looks up a localized string similar to Error: Invalid input.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorResultTitle {
|
||||
get {
|
||||
@@ -186,6 +213,33 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error: Invalid number.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorResultValue {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorResultValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Excel's 1900 date value.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Excel1900 {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Excel1900", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Excel's 1904 date value.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Excel1904 {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Excel1904", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date and time in filename-compatible format.
|
||||
/// </summary>
|
||||
@@ -204,6 +258,33 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid custom format:.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_InvalidCustomFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidCustomFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot parse the input as Excel's 1900 date value because it is a fake date. (In Excel 0 stands for 0/1/1900 and this date doesn't exist. And 60 stands for 2/29/1900 and this date only exists in Excel for compatibility with Lotus 123.).
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_InvalidInput_FakeExcel1900 {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_FakeExcel1900", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your input for {0} is outside the range from {1} to {2}..
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_InvalidInput_SupportedRange {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_SupportedRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ISO 8601.
|
||||
/// </summary>
|
||||
@@ -240,6 +321,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Leap year.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_LeapYear {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_LeapYear", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Millisecond.
|
||||
/// </summary>
|
||||
@@ -285,6 +375,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not a leap year.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_NoLeapYear {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_NoLeapYear", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Now.
|
||||
/// </summary>
|
||||
@@ -303,6 +402,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OLE Automation Date.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_OADate {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_OADate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Provides time and date values for the system time or a custom time stamp (e.g.'{0}', '{1}', '{2}', '{3}').
|
||||
/// </summary>
|
||||
@@ -366,6 +474,42 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date and time; Time and Date; Custom format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustom {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustom", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Current date and time; Current time and date; Now; Custom format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustomNow {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomNow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date and time UTC; Time UTC and Date; Custom UTC format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustomUtc {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomUtc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Current date and time UTC; Current time UTC and date; Now UTC; Custom UTC format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustomUtcNow {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomUtcNow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date.
|
||||
/// </summary>
|
||||
@@ -447,6 +591,24 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Custom formats.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Setting_CustomFormats {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_CustomFormats", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use date and time string format syntax and {0} (Day of Week), {1} (Days in Month), {2} (Week of Month), {3} (Week of the year), {4} (Era abbreviation), {5} (Windows File Time), {6} (Unix Time), {7} (Unix Time in milliseconds), {8} (OLE Automation date), {9} (Excel's 1900 based date value), {10} (Excel's 1904 based date value). If the format starts with {11}, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.).
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Setting_CustomFormatsDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_CustomFormatsDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use system setting.
|
||||
/// </summary>
|
||||
|
||||
@@ -156,10 +156,13 @@
|
||||
<value>Era abbreviation</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorResultSubTitle" xml:space="preserve">
|
||||
<value>Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time</value>
|
||||
<value>Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time, 'oa' for OLE Automation date, 'exc' for Excel's 1900 date value, 'exf' for Excel's 1904 date value</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorResultTitle" xml:space="preserve">
|
||||
<value>Error: Invalid number input</value>
|
||||
<value>Error: Invalid input</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorResultValue" xml:space="preserve">
|
||||
<value>Error: Invalid number</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Hour" xml:space="preserve">
|
||||
<value>Hour</value>
|
||||
@@ -243,10 +246,26 @@
|
||||
<value>Date and time; Time and Date</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustom" xml:space="preserve">
|
||||
<value>Date and time; Time and Date; Custom format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustomUtc" xml:space="preserve">
|
||||
<value>Date and time UTC; Time UTC and Date; Custom UTC format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagFormatNow" xml:space="preserve">
|
||||
<value>Current date and time; Current time and date; Now</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustomNow" xml:space="preserve">
|
||||
<value>Current date and time; Current time and date; Now; Custom format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustomUtcNow" xml:space="preserve">
|
||||
<value>Current date and time UTC; Current time UTC and date; Now UTC; Custom UTC format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagTime" xml:space="preserve">
|
||||
<value>Time</value>
|
||||
<comment>Don't change order</comment>
|
||||
@@ -262,6 +281,9 @@
|
||||
<data name="Microsoft_plugin_timedate_Second" xml:space="preserve">
|
||||
<value>Second</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidCustomFormat" xml:space="preserve">
|
||||
<value>Invalid custom format:</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SettingDateWithWeekday" xml:space="preserve">
|
||||
<value>Show date with weekday and name of month</value>
|
||||
</data>
|
||||
@@ -360,4 +382,42 @@
|
||||
<data name="Microsoft_plugin_timedate_Setting_UseSystemSetting" xml:space="preserve">
|
||||
<value>Use system setting</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Setting_CustomFormats" xml:space="preserve">
|
||||
<value>Custom formats</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Setting_CustomFormatsDescription" xml:space="preserve">
|
||||
<value>Use date and time string format syntax and {0} (Day of Week), {1} (Days in Month), {2} (Week of Month), {3} (Week of the year), {4} (Era abbreviation), {5} (Windows File Time), {6} (Unix Time), {7} (Unix Time in milliseconds), {8} (OLE Automation date), {9} (Excel's 1900 based date value), {10} (Excel's 1904 based date value). If the format starts with {11}, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.)</value>
|
||||
<comment>The {n} parts are place holders and get replaced in the code.</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorConvertCustomFormat" xml:space="preserve">
|
||||
<value>Failed to convert into custom format:</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorConvertWft" xml:space="preserve">
|
||||
<value>Not a valid Windows file time</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidInput_SupportedRange" xml:space="preserve">
|
||||
<value>Your input for {0} is outside the range from {1} to {2}.</value>
|
||||
<comment>The placeholder will be replace in code.</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_OADate" xml:space="preserve">
|
||||
<value>OLE Automation Date</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Excel1900" xml:space="preserve">
|
||||
<value>Excel's 1900 date value</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidInput_FakeExcel1900" xml:space="preserve">
|
||||
<value>Cannot parse the input as Excel's 1900 date value because it is a fake date. (In Excel 0 stands for 0/1/1900 and this date doesn't exist. And 60 stands for 2/29/1900 and this date only exists in Excel for compatibility with Lotus 123.)</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Excel1904" xml:space="preserve">
|
||||
<value>Excel's 1904 date value</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_LeapYear" xml:space="preserve">
|
||||
<value>Leap year</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_NoLeapYear" xml:space="preserve">
|
||||
<value>Not a leap year</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_DaysInMonth" xml:space="preserve">
|
||||
<value>Days in month</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -295,7 +295,7 @@
|
||||
"Areas": [ "AreaEaseOfAccess" ],
|
||||
"Type": "AppSettingsApp",
|
||||
"AltNames": [ "TouchFeedback" ],
|
||||
"Command": "ms-settings:easeofaccess-MousePointer"
|
||||
"Command": "ms-settings:easeofaccess-mousepointer"
|
||||
},
|
||||
{
|
||||
"Name": "Display",
|
||||
|
||||
@@ -325,7 +325,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_H => new[] { "ĥ" },
|
||||
LetterKey.VK_J => new[] { "ĵ" },
|
||||
LetterKey.VK_S => new[] { "ŝ" },
|
||||
LetterKey.VK_U => new[] { "ǔ" },
|
||||
LetterKey.VK_U => new[] { "ŭ" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions.TestingHelpers" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -60,6 +60,9 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Markdown" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
|
||||
<PackageReference Include="MessagePack" />
|
||||
@@ -75,6 +78,8 @@
|
||||
<PackageReference Include="Microsoft.Web.WebView2" />
|
||||
<!-- HACK: CmdPal uses CommunityToolkit.Common directly. Align the version. -->
|
||||
<PackageReference Include="CommunityToolkit.Common" />
|
||||
<!-- HACK: MWB and Advanced Paste. Align the version. got flagged when https://github.com/microsoft/PowerToys/pull/38779 was done -->
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
|
||||
@@ -28,18 +28,15 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
var bootTime = new System.Diagnostics.Stopwatch();
|
||||
bootTime.Start();
|
||||
|
||||
this.Activated += Window_Activated_SetIcon;
|
||||
|
||||
App.ThemeService.ThemeChanged += OnThemeChanged;
|
||||
App.ThemeService.ApplyTheme();
|
||||
|
||||
ShellPage.SetElevationStatus(App.IsElevated);
|
||||
ShellPage.SetIsUserAnAdmin(App.IsUserAnAdmin);
|
||||
|
||||
// Set window icon
|
||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
|
||||
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
|
||||
var placement = WindowHelper.DeserializePlacementOrDefault(hWnd);
|
||||
if (createHidden)
|
||||
{
|
||||
@@ -221,6 +218,15 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
App.ThemeService.ThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
|
||||
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// Set window icon
|
||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
|
||||
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
}
|
||||
|
||||
private void Window_Activated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
if (args.WindowActivationState != WindowActivationState.Deactivated)
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
private WindowId _windowId;
|
||||
private IntPtr _hWnd;
|
||||
private AppWindow _appWindow;
|
||||
private WindowMessageMonitor _msgMonitor;
|
||||
private bool disposedValue;
|
||||
|
||||
public OobeWindow(PowerToysModules initialModule)
|
||||
@@ -41,15 +40,10 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set window icon
|
||||
_hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
_windowId = Win32Interop.GetWindowIdFromWindow(_hWnd);
|
||||
_appWindow = AppWindow.GetFromWindowId(_windowId);
|
||||
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
|
||||
OverlappedPresenter presenter = _appWindow.Presenter as OverlappedPresenter;
|
||||
presenter.IsMinimizable = false;
|
||||
presenter.IsMaximizable = false;
|
||||
this.Activated += Window_Activated_SetIcon;
|
||||
|
||||
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
||||
_currentDPI = dpi;
|
||||
@@ -64,18 +58,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
this.initialModule = initialModule;
|
||||
|
||||
_msgMonitor = new WindowMessageMonitor(this);
|
||||
_msgMonitor.WindowMessageReceived += (_, e) =>
|
||||
{
|
||||
const int WM_NCLBUTTONDBLCLK = 0x00A3;
|
||||
if (e.Message.MessageId == WM_NCLBUTTONDBLCLK)
|
||||
{
|
||||
// Disable double click on title bar to maximize window
|
||||
e.Result = 0;
|
||||
e.Handled = true;
|
||||
}
|
||||
};
|
||||
|
||||
this.SizeChanged += OobeWindow_SizeChanged;
|
||||
|
||||
var loader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
@@ -110,6 +92,12 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// Set window icon
|
||||
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
}
|
||||
|
||||
private void OobeWindow_SizeChanged(object sender, WindowSizeChangedEventArgs args)
|
||||
{
|
||||
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
||||
@@ -149,8 +137,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
_msgMonitor?.Dispose();
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,7 +458,7 @@
|
||||
<controls:SettingsPageControl.PrimaryLinks>
|
||||
<controls:PageLink x:Uid="GeneralPage_Documentation" Link="https://aka.ms/PowerToysOverview" />
|
||||
<controls:PageLink x:Uid="General_Repository" Link="https://aka.ms/powertoys" />
|
||||
<controls:PageLink x:Uid="GeneralPage_ReportAbug" Link="https://aka.ms/powerToysReportBug" />
|
||||
<controls:PageLink x:Uid="GeneralPage_ReportAbug" Link="{x:Bind ViewModel.ReportBugLink, Mode=OneWay}" />
|
||||
<controls:PageLink x:Uid="GeneralPage_RequestAFeature_URL" Link="https://aka.ms/powerToysRequestFeature" />
|
||||
</controls:SettingsPageControl.PrimaryLinks>
|
||||
<controls:SettingsPageControl.SecondaryLinks>
|
||||
|
||||
@@ -84,6 +84,8 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
|
||||
DataContext = ViewModel;
|
||||
|
||||
ViewModel.InitializeReportBugLink();
|
||||
|
||||
doRefreshBackupRestoreStatus(100);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
@@ -25,11 +26,19 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.Win32;
|
||||
using Windows.System.Profile;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
{
|
||||
public partial class GeneralViewModel : Observable
|
||||
{
|
||||
public enum InstallScope
|
||||
{
|
||||
PerMachine = 0,
|
||||
PerUser,
|
||||
}
|
||||
|
||||
private GeneralSettings GeneralSettingsConfig { get; set; }
|
||||
|
||||
private UpdatingSettings UpdatingSettingsConfig { get; set; }
|
||||
@@ -72,6 +81,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private SettingsBackupAndRestoreUtils settingsBackupAndRestoreUtils = SettingsBackupAndRestoreUtils.Instance;
|
||||
|
||||
private const string InstallScopeRegKey = @"Software\Classes\powertoys\";
|
||||
|
||||
public GeneralViewModel(ISettingsRepository<GeneralSettings> settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func<string, int> ipcMSGCallBackFunc, Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc, Func<string, int> ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "", Action dispatcherAction = null, Action hideBackupAndRestoreMessageAreaAction = null, Action<int> doBackupAndRestoreDryRun = null, Func<Task<string>> pickSingleFolderDialog = null, Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = null)
|
||||
{
|
||||
CheckForUpdatesEventHandler = new ButtonClickCommand(CheckForUpdatesClick);
|
||||
@@ -256,6 +267,73 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private int _initLanguagesIndex;
|
||||
private bool _languageChanged;
|
||||
|
||||
private string reportBugLink;
|
||||
|
||||
// Gets or sets a value indicating whether run powertoys on start-up.
|
||||
public string ReportBugLink
|
||||
{
|
||||
get => reportBugLink;
|
||||
set
|
||||
{
|
||||
reportBugLink = value;
|
||||
OnPropertyChanged(nameof(ReportBugLink));
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeReportBugLink()
|
||||
{
|
||||
var version = GetPowerToysVersion();
|
||||
|
||||
string isElevatedString = "PowerToys is running " + (IsElevated ? "as admin (elevated)" : "as user (non-elevated)");
|
||||
|
||||
string installScope = GetCurrentInstallScope() == InstallScope.PerMachine ? "per machine (system)" : "per user";
|
||||
|
||||
var info = $"OS Version: {GetOSVersion()} \n.NET Version: {GetDotNetVersion()}\n{isElevatedString}\nInstall scope: {installScope}\nOperating System Language: {CultureInfo.InstalledUICulture.DisplayName}\nSystem locale: {CultureInfo.InstalledUICulture.Name}";
|
||||
|
||||
var gitHubURL = "https://github.com/microsoft/PowerToys/issues/new?template=bug_report.yml&labels=Issue-Bug%2CTriage-Needed" +
|
||||
"&version=" + version + "&additionalInfo=" + System.Web.HttpUtility.UrlEncode(info);
|
||||
|
||||
ReportBugLink = gitHubURL;
|
||||
}
|
||||
|
||||
private string GetPowerToysVersion()
|
||||
{
|
||||
return Helper.GetProductVersion().TrimStart('v');
|
||||
}
|
||||
|
||||
private string GetOSVersion()
|
||||
{
|
||||
return Environment.OSVersion.VersionString;
|
||||
}
|
||||
|
||||
public static string GetDotNetVersion()
|
||||
{
|
||||
return $".NET {Environment.Version}";
|
||||
}
|
||||
|
||||
public static InstallScope GetCurrentInstallScope()
|
||||
{
|
||||
// Check HKLM first
|
||||
if (Registry.LocalMachine.OpenSubKey(InstallScopeRegKey) != null)
|
||||
{
|
||||
return InstallScope.PerMachine;
|
||||
}
|
||||
|
||||
// If not found, check HKCU
|
||||
var userKey = Registry.CurrentUser.OpenSubKey(InstallScopeRegKey);
|
||||
if (userKey != null)
|
||||
{
|
||||
var installScope = userKey.GetValue("InstallScope") as string;
|
||||
userKey.Close();
|
||||
if (!string.IsNullOrEmpty(installScope) && installScope.Contains("perUser"))
|
||||
{
|
||||
return InstallScope.PerUser;
|
||||
}
|
||||
}
|
||||
|
||||
return InstallScope.PerMachine; // Default if no specific registry key found
|
||||
}
|
||||
|
||||
// Gets or sets a value indicating whether run powertoys on start-up.
|
||||
public bool Startup
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user