mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-06 04:17:04 +01:00
Compare commits
19 Commits
dev/haoliu
...
yuleng/cmd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a19fc93346 | ||
|
|
85bdeeaacf | ||
|
|
f1bda8d71f | ||
|
|
21aa49cefb | ||
|
|
293fa262bb | ||
|
|
578d99f3b3 | ||
|
|
badb029bcf | ||
|
|
bec6754aa3 | ||
|
|
d986592737 | ||
|
|
662f04ed34 | ||
|
|
03bc72c436 | ||
|
|
5c8aa67781 | ||
|
|
b32c04fca1 | ||
|
|
4cb72ee126 | ||
|
|
55f8f3a53e | ||
|
|
a7994402fe | ||
|
|
41472a483c | ||
|
|
94e8559796 | ||
|
|
fe53a9c89a |
@@ -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
|
||||
|
||||
31
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
31
.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,7 +7,8 @@ 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: X.XX.X
|
||||
@@ -16,7 +16,8 @@ body:
|
||||
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
|
||||
|
||||
|
||||
1
.github/actions/spell-check/allow/names.txt
vendored
1
.github/actions/spell-check/allow/names.txt
vendored
@@ -188,6 +188,7 @@ zhaoqpcn
|
||||
Zoltan
|
||||
Zykova
|
||||
Sameerjs
|
||||
ruslanlap
|
||||
|
||||
# OTHERS
|
||||
|
||||
|
||||
50
.github/actions/spell-check/expect.txt
vendored
50
.github/actions/spell-check/expect.txt
vendored
@@ -56,12 +56,10 @@ APIIs
|
||||
Apm
|
||||
APPBARDATA
|
||||
APPEXECLINK
|
||||
APPICONREFERENCE
|
||||
APPLICATIONFRAMEHOST
|
||||
appmanifest
|
||||
APPMODEL
|
||||
APPNAME
|
||||
APPPUBLISHER
|
||||
appref
|
||||
appsettings
|
||||
appwindow
|
||||
@@ -173,7 +171,6 @@ CCHFORMNAME
|
||||
CCom
|
||||
CContext
|
||||
CDeclaration
|
||||
cdn
|
||||
CElems
|
||||
CENTERALIGN
|
||||
certlm
|
||||
@@ -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
|
||||
@@ -510,12 +497,10 @@ Fira
|
||||
FIXEDFILEINFO
|
||||
FIXEDSYS
|
||||
flac
|
||||
flaticon
|
||||
flyouts
|
||||
FMask
|
||||
fmtid
|
||||
FOF
|
||||
WANTNUKEWARNING
|
||||
FOFX
|
||||
FOLDERID
|
||||
folderpath
|
||||
@@ -525,7 +510,6 @@ FORCEMINIMIZE
|
||||
FORMATDLGORD
|
||||
formatetc
|
||||
FORPARSING
|
||||
fpvm
|
||||
Fqc
|
||||
FRAMECHANGED
|
||||
frm
|
||||
@@ -559,7 +543,6 @@ GETSECKEY
|
||||
GETSTICKYKEYS
|
||||
GETTEXTLENGTH
|
||||
GHND
|
||||
gifv
|
||||
GMEM
|
||||
GNumber
|
||||
gpedit
|
||||
@@ -653,7 +636,6 @@ HROW
|
||||
hsb
|
||||
HSCROLL
|
||||
hsi
|
||||
HSSH
|
||||
HTCLIENT
|
||||
hthumbnail
|
||||
HTOUCHINPUT
|
||||
@@ -691,7 +673,6 @@ iextn
|
||||
IFACEMETHOD
|
||||
IFACEMETHODIMP
|
||||
IFile
|
||||
IGNOREBASECLASS
|
||||
IGNOREUNKNOWN
|
||||
IGo
|
||||
iid
|
||||
@@ -740,8 +721,6 @@ Inste
|
||||
Interlop
|
||||
INTRESOURCE
|
||||
INVALIDARG
|
||||
INVALIDCALL
|
||||
INVALIDINDEX
|
||||
invalidoperatioexception
|
||||
ipcmanager
|
||||
IPREVIEW
|
||||
@@ -803,7 +782,6 @@ LEVELID
|
||||
LExit
|
||||
lhwnd
|
||||
LIBID
|
||||
libraryincludes
|
||||
LIMITSIZE
|
||||
LIMITTEXT
|
||||
lindex
|
||||
@@ -905,7 +883,6 @@ MBM
|
||||
MBR
|
||||
MDICHILD
|
||||
MDL
|
||||
mdpvm
|
||||
mdtext
|
||||
mdtxt
|
||||
mdwn
|
||||
@@ -1053,7 +1030,6 @@ NOCRLF
|
||||
nodeca
|
||||
NODRAWCAPTION
|
||||
NODRAWICON
|
||||
NOFIXUPS
|
||||
NOINHERITLAYOUT
|
||||
NOINTERFACE
|
||||
NOINVERT
|
||||
@@ -1069,13 +1045,11 @@ NONELEVATED
|
||||
NONINFRINGEMENT
|
||||
nonspace
|
||||
nonstd
|
||||
NOOPEN
|
||||
NOOWNERZORDER
|
||||
NOPARENTNOTIFY
|
||||
NOPREFIX
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREDRAW
|
||||
NOREMAPCLSID
|
||||
NOREMOVE
|
||||
norename
|
||||
NOREPEAT
|
||||
@@ -1094,11 +1068,9 @@ NOTIFYICONDATAW
|
||||
NOTIMPL
|
||||
NOTOPMOST
|
||||
NOTRACK
|
||||
NOTRUNCATE
|
||||
NOTSRCCOPY
|
||||
NOTSRCERASE
|
||||
NOTXORPEN
|
||||
NOUSERSETTINGS
|
||||
NOZORDER
|
||||
NPH
|
||||
npmjs
|
||||
@@ -1224,6 +1196,7 @@ Podcasts
|
||||
POINTERID
|
||||
POINTERUPDATE
|
||||
Pokedex
|
||||
Pomodoro
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
POSITIONITEM
|
||||
@@ -1318,7 +1291,6 @@ Quarternary
|
||||
QUERYENDSESSION
|
||||
QUERYOPEN
|
||||
QUEUESYNC
|
||||
QUICKTIP
|
||||
QUNS
|
||||
QXZ
|
||||
RAII
|
||||
@@ -1347,7 +1319,6 @@ REFCLSID
|
||||
REFIID
|
||||
REGCLS
|
||||
regfile
|
||||
REGISTERCLASSEX
|
||||
REGISTERCLASSFAILED
|
||||
REGISTRYHEADER
|
||||
registrypath
|
||||
@@ -1359,7 +1330,6 @@ REINSTALLMODE
|
||||
reloadable
|
||||
Relogger
|
||||
remappings
|
||||
REMAPRUNDLL
|
||||
REMAPSUCCESSFUL
|
||||
REMAPUNSUCCESSFUL
|
||||
Remotable
|
||||
@@ -1436,7 +1406,6 @@ SDDL
|
||||
SDKDDK
|
||||
sdns
|
||||
searchterm
|
||||
searchtext
|
||||
SEARCHUI
|
||||
SECONDARYDISPLAY
|
||||
secpol
|
||||
@@ -1484,8 +1453,6 @@ SHELLDLL
|
||||
shellex
|
||||
SHELLEXECUTEINFO
|
||||
SHELLEXECUTEINFOW
|
||||
SHELLEXTENSION
|
||||
SHELLNEWVALUE
|
||||
SHFILEINFO
|
||||
SHFILEOPSTRUCT
|
||||
SHGDN
|
||||
@@ -1495,7 +1462,6 @@ SHGFIICON
|
||||
SHGFILARGEICON
|
||||
shinfo
|
||||
shlwapi
|
||||
SHNAMEMAPPING
|
||||
shobjidl
|
||||
SHORTCUTATLEAST
|
||||
SHORTCUTMAXONEACTIONKEY
|
||||
@@ -1546,7 +1512,6 @@ SNAPPROCESS
|
||||
snwprintf
|
||||
softline
|
||||
SOURCECLIENTAREAONLY
|
||||
sourced
|
||||
sourcedoc
|
||||
SOURCEHEADER
|
||||
sourcesdirectory
|
||||
@@ -1685,7 +1650,6 @@ THotkey
|
||||
throughs
|
||||
TIcon
|
||||
TILEDWINDOW
|
||||
TILEINFO
|
||||
TILLSON
|
||||
timedate
|
||||
timediff
|
||||
@@ -1699,9 +1663,6 @@ TLayout
|
||||
tlb
|
||||
tlbimp
|
||||
tlc
|
||||
TPMLEFTALIGN
|
||||
TPMRETURNCMD
|
||||
TMPVAR
|
||||
TNP
|
||||
Toolhelp
|
||||
toolkitconverters
|
||||
@@ -1709,6 +1670,8 @@ toolwindow
|
||||
TOPDOWNDIB
|
||||
TOUCHEVENTF
|
||||
TOUCHINPUT
|
||||
TPMLEFTALIGN
|
||||
TPMRETURNCMD
|
||||
TRACEHANDLE
|
||||
tracelogging
|
||||
tracerpt
|
||||
@@ -1782,7 +1745,6 @@ USRDLL
|
||||
UType
|
||||
uuidv
|
||||
uwp
|
||||
ums
|
||||
uxt
|
||||
uxtheme
|
||||
vabdq
|
||||
@@ -1809,7 +1771,6 @@ VERTSIZE
|
||||
VFT
|
||||
vget
|
||||
vgetq
|
||||
videourl
|
||||
viewmodel
|
||||
VIRTKEY
|
||||
VIRTUALDESK
|
||||
@@ -1843,7 +1804,7 @@ vstprintf
|
||||
VSTT
|
||||
vswhere
|
||||
Vtbl
|
||||
WANTMAPPINGHANDLE
|
||||
WANTNUKEWARNING
|
||||
WANTPALM
|
||||
wasdk
|
||||
wbem
|
||||
@@ -1958,7 +1919,6 @@ WUX
|
||||
Wwanpp
|
||||
XAxis
|
||||
xclip
|
||||
xdoc
|
||||
XDocument
|
||||
XElement
|
||||
xfd
|
||||
@@ -1981,13 +1941,13 @@ Yeet
|
||||
YIncrement
|
||||
yinle
|
||||
yinyue
|
||||
youtube
|
||||
YPels
|
||||
YResolution
|
||||
YStr
|
||||
YTM
|
||||
YVIRTUALSCREEN
|
||||
ZEROINIT
|
||||
Zhiwei
|
||||
zonable
|
||||
zoneset
|
||||
Zoneszonabletester
|
||||
|
||||
@@ -127,7 +127,6 @@
|
||||
|
||||
"PowerToys.KeyboardManager.dll",
|
||||
"KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe",
|
||||
"KeyboardManagerEditorUI\\PowerToys.KeyboardManagerEditorUI.exe",
|
||||
"KeyboardManagerEngine\\PowerToys.KeyboardManagerEngine.exe",
|
||||
"PowerToys.KeyboardManagerEditorLibraryWrapper.dll",
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
|
||||
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
|
||||
- [@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
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<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.4" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
@@ -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,7 +69,7 @@
|
||||
<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.4" />
|
||||
@@ -84,14 +84,17 @@
|
||||
<!-- 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.4" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.4" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
|
||||
<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.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" />
|
||||
|
||||
15
NOTICE.md
15
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,7 +1418,7 @@ SOFTWARE.
|
||||
- LazyCache 2.4.0
|
||||
- Mages 3.0.0
|
||||
- Markdig.Signed 0.34.0
|
||||
- MessagePack 2.5.187
|
||||
- MessagePack 3.1.3
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.4
|
||||
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
|
||||
- Microsoft.Data.Sqlite 9.0.4
|
||||
@@ -1445,14 +1444,14 @@ 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.4
|
||||
- System.CommandLine 2.0.0-beta4.22272.1
|
||||
@@ -1463,17 +1462,21 @@ SOFTWARE.
|
||||
- System.Diagnostics.EventLog 9.0.4
|
||||
- System.Diagnostics.PerformanceCounter 9.0.4
|
||||
- System.Drawing.Common 9.0.4
|
||||
- System.IO.Abstractions 21.0.29
|
||||
- System.IO.Abstractions.TestingHelpers 21.0.29
|
||||
- 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.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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,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>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,665 +1,11 @@
|
||||
#include "pch.h"
|
||||
#include "KeyboardManagerEditorLibraryWrapper.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.h>
|
||||
#include <keyboardmanager/KeyboardManagerEditorLibrary/EditorHelpers.h>
|
||||
#include <common/interop/keyboard_layout.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void* CreateMappingConfiguration()
|
||||
{
|
||||
return new MappingConfiguration();
|
||||
}
|
||||
|
||||
void DestroyMappingConfiguration(void* config)
|
||||
{
|
||||
delete static_cast<MappingConfiguration*>(config);
|
||||
}
|
||||
|
||||
bool LoadMappingSettings(void* config)
|
||||
{
|
||||
return static_cast<MappingConfiguration*>(config)->LoadSettings();
|
||||
}
|
||||
|
||||
bool SaveMappingSettings(void* config)
|
||||
{
|
||||
return static_cast<MappingConfiguration*>(config)->SaveSettingsToFile();
|
||||
}
|
||||
|
||||
wchar_t* AllocateAndCopyString(const std::wstring& str)
|
||||
{
|
||||
size_t len = str.length();
|
||||
wchar_t* buffer = new wchar_t[len + 1];
|
||||
wcscpy_s(buffer, len + 1, str.c_str());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int GetSingleKeyRemapCount(void* config)
|
||||
{
|
||||
auto mapping = static_cast<MappingConfiguration*>(config);
|
||||
return static_cast<int>(mapping->singleKeyReMap.size());
|
||||
}
|
||||
|
||||
bool GetSingleKeyRemap(void* config, int index, SingleKeyMapping* mapping)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
std::vector<std::pair<DWORD, KeyShortcutTextUnion>> allMappings;
|
||||
|
||||
for (const auto& kv : mappingConfig->singleKeyReMap)
|
||||
{
|
||||
allMappings.push_back(kv);
|
||||
}
|
||||
|
||||
if (index < 0 || index >= allMappings.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& kv = allMappings[index];
|
||||
mapping->originalKey = static_cast<int>(kv.first);
|
||||
|
||||
// Remap to single key
|
||||
if (kv.second.index() == 0)
|
||||
{
|
||||
mapping->targetKey = AllocateAndCopyString(std::to_wstring(std::get<DWORD>(kv.second)));
|
||||
mapping->isShortcut = false;
|
||||
}
|
||||
// Remap to shortcut
|
||||
else if (kv.second.index() == 1)
|
||||
{
|
||||
mapping->targetKey = AllocateAndCopyString(std::get<Shortcut>(kv.second).ToHstringVK().c_str());
|
||||
mapping->isShortcut = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapping->targetKey = AllocateAndCopyString(L"");
|
||||
mapping->isShortcut = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetSingleKeyToTextRemapCount(void* config)
|
||||
{
|
||||
auto mapping = static_cast<MappingConfiguration*>(config);
|
||||
return static_cast<int>(mapping->singleKeyToTextReMap.size());
|
||||
}
|
||||
|
||||
bool GetSingleKeyToTextRemap(void* config, int index, KeyboardTextMapping* mapping)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
if (index < 0 || index >= mappingConfig->singleKeyToTextReMap.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = mappingConfig->singleKeyToTextReMap.begin();
|
||||
std::advance(it, index);
|
||||
|
||||
mapping->originalKey = static_cast<int>(it->first);
|
||||
std::wstring text = std::get<std::wstring>(it->second);
|
||||
mapping->targetText = AllocateAndCopyString(text);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetShortcutRemapCountByType(void* config, int operationType)
|
||||
{
|
||||
auto mapping = static_cast<MappingConfiguration*>(config);
|
||||
int count = 0;
|
||||
|
||||
for (const auto& kv : mapping->osLevelShortcutReMap)
|
||||
{
|
||||
bool shouldCount = false;
|
||||
|
||||
|
||||
if (operationType == 0)
|
||||
{
|
||||
if ((kv.second.targetShortcut.index() == 0) ||
|
||||
(kv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(kv.second.targetShortcut).operationType == Shortcut::OperationType::RemapShortcut))
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 1)
|
||||
{
|
||||
|
||||
if (kv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(kv.second.targetShortcut).operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 2)
|
||||
{
|
||||
if (kv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(kv.second.targetShortcut).operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 3)
|
||||
{
|
||||
if (kv.second.targetShortcut.index() == 2)
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCount)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& appMap : mapping->appSpecificShortcutReMap)
|
||||
{
|
||||
for (const auto& shortcutKv : appMap.second)
|
||||
{
|
||||
bool shouldCount = false;
|
||||
|
||||
if (operationType == 0)
|
||||
{
|
||||
if ((shortcutKv.second.targetShortcut.index() == 0) ||
|
||||
(shortcutKv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(shortcutKv.second.targetShortcut).operationType == Shortcut::OperationType::RemapShortcut))
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 1)
|
||||
{
|
||||
if (shortcutKv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(shortcutKv.second.targetShortcut).operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 2)
|
||||
{
|
||||
if (shortcutKv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(shortcutKv.second.targetShortcut).operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 3)
|
||||
{
|
||||
if (shortcutKv.second.targetShortcut.index() == 2)
|
||||
{
|
||||
shouldCount = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldCount)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool GetShortcutRemapByType(void* config, int operationType, int index, ShortcutMapping* mapping)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
std::vector<std::tuple<Shortcut, KeyShortcutTextUnion, std::wstring>> filteredMappings;
|
||||
|
||||
for (const auto& kv : mappingConfig->osLevelShortcutReMap)
|
||||
{
|
||||
bool shouldAdd = false;
|
||||
|
||||
if (operationType == 0) // RemapShortcut
|
||||
{
|
||||
if ((kv.second.targetShortcut.index() == 0) ||
|
||||
(kv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(kv.second.targetShortcut).operationType == Shortcut::OperationType::RemapShortcut))
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 1) // RunProgram
|
||||
{
|
||||
if (kv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(kv.second.targetShortcut).operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 2) // OpenURI
|
||||
{
|
||||
if (kv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(kv.second.targetShortcut).operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 3)
|
||||
{
|
||||
if (kv.second.targetShortcut.index() == 2)
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldAdd)
|
||||
{
|
||||
filteredMappings.push_back(std::make_tuple(kv.first, kv.second.targetShortcut, L""));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& appKv : mappingConfig->appSpecificShortcutReMap)
|
||||
{
|
||||
for (const auto& shortcutKv : appKv.second)
|
||||
{
|
||||
bool shouldAdd = false;
|
||||
|
||||
if (operationType == 0) // RemapShortcut
|
||||
{
|
||||
if ((shortcutKv.second.targetShortcut.index() == 0) ||
|
||||
(shortcutKv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(shortcutKv.second.targetShortcut).operationType == Shortcut::OperationType::RemapShortcut))
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 1) // RunProgram
|
||||
{
|
||||
if (shortcutKv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(shortcutKv.second.targetShortcut).operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 2) // OpenURI
|
||||
{
|
||||
if (shortcutKv.second.targetShortcut.index() == 1 &&
|
||||
std::get<Shortcut>(shortcutKv.second.targetShortcut).operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
else if (operationType == 3)
|
||||
{
|
||||
if (shortcutKv.second.targetShortcut.index() == 2)
|
||||
{
|
||||
shouldAdd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldAdd)
|
||||
{
|
||||
filteredMappings.push_back(std::make_tuple(
|
||||
shortcutKv.first, shortcutKv.second.targetShortcut, appKv.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 0 || index >= filteredMappings.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& [origShortcut, targetShortcutUnion, app] = filteredMappings[index];
|
||||
|
||||
std::wstring origKeysStr = origShortcut.ToHstringVK().c_str();
|
||||
mapping->originalKeys = AllocateAndCopyString(origKeysStr);
|
||||
mapping->targetApp = AllocateAndCopyString(app);
|
||||
|
||||
if (targetShortcutUnion.index() == 0)
|
||||
{
|
||||
DWORD targetKey = std::get<DWORD>(targetShortcutUnion);
|
||||
mapping->targetKeys = AllocateAndCopyString(std::to_wstring(targetKey));
|
||||
mapping->operationType = 0;
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
else if (targetShortcutUnion.index() == 1)
|
||||
{
|
||||
Shortcut targetShortcut = std::get<Shortcut>(targetShortcutUnion);
|
||||
std::wstring targetKeysStr = targetShortcut.ToHstringVK().c_str();
|
||||
|
||||
mapping->operationType = static_cast<int>(targetShortcut.operationType);
|
||||
|
||||
if (targetShortcut.operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
mapping->targetKeys = AllocateAndCopyString(targetKeysStr);
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(targetShortcut.runProgramFilePath);
|
||||
mapping->programArgs = AllocateAndCopyString(targetShortcut.runProgramArgs);
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
else if (targetShortcut.operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
mapping->targetKeys = AllocateAndCopyString(targetKeysStr);
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(targetShortcut.uriToOpen);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapping->targetKeys = AllocateAndCopyString(targetKeysStr);
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
}
|
||||
else if (targetShortcutUnion.index() == 2)
|
||||
{
|
||||
std::wstring text = std::get<std::wstring>(targetShortcutUnion);
|
||||
mapping->targetKeys = AllocateAndCopyString(L"");
|
||||
mapping->operationType = 0;
|
||||
mapping->targetText = AllocateAndCopyString(text);
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetShortcutRemapCount(void* config)
|
||||
{
|
||||
auto mapping = static_cast<MappingConfiguration*>(config);
|
||||
int count = static_cast<int>(mapping->osLevelShortcutReMap.size());
|
||||
|
||||
for (const auto& appMap : mapping->appSpecificShortcutReMap)
|
||||
{
|
||||
count += static_cast<int>(appMap.second.size());
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool GetShortcutRemap(void* config, int index, ShortcutMapping* mapping)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
std::vector<std::tuple<Shortcut, KeyShortcutTextUnion, std::wstring>> allMappings;
|
||||
|
||||
for (const auto& kv : mappingConfig->osLevelShortcutReMap)
|
||||
{
|
||||
allMappings.push_back(std::make_tuple(kv.first, kv.second.targetShortcut, L""));
|
||||
}
|
||||
|
||||
for (const auto& appKv : mappingConfig->appSpecificShortcutReMap)
|
||||
{
|
||||
for (const auto& shortcutKv : appKv.second)
|
||||
{
|
||||
allMappings.push_back(std::make_tuple(
|
||||
shortcutKv.first, shortcutKv.second.targetShortcut, appKv.first));
|
||||
}
|
||||
}
|
||||
|
||||
if (index < 0 || index >= allMappings.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& [origShortcut, targetShortcutUnion, app] = allMappings[index];
|
||||
|
||||
std::wstring origKeysStr = origShortcut.ToHstringVK().c_str();
|
||||
mapping->originalKeys = AllocateAndCopyString(origKeysStr);
|
||||
|
||||
mapping->targetApp = AllocateAndCopyString(app);
|
||||
|
||||
if (targetShortcutUnion.index() == 0)
|
||||
{
|
||||
DWORD targetKey = std::get<DWORD>(targetShortcutUnion);
|
||||
mapping->targetKeys = AllocateAndCopyString(std::to_wstring(targetKey));
|
||||
mapping->operationType = 0;
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
else if (targetShortcutUnion.index() == 1)
|
||||
{
|
||||
Shortcut targetShortcut = std::get<Shortcut>(targetShortcutUnion);
|
||||
std::wstring targetKeysStr = targetShortcut.ToHstringVK().c_str();
|
||||
|
||||
mapping->operationType = static_cast<int>(targetShortcut.operationType);
|
||||
|
||||
if (targetShortcut.operationType == Shortcut::OperationType::RunProgram)
|
||||
{
|
||||
mapping->targetKeys = AllocateAndCopyString(targetKeysStr);
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(targetShortcut.runProgramFilePath);
|
||||
mapping->programArgs = AllocateAndCopyString(targetShortcut.runProgramArgs);
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
else if (targetShortcut.operationType == Shortcut::OperationType::OpenURI)
|
||||
{
|
||||
mapping->targetKeys = AllocateAndCopyString(targetKeysStr);
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(targetShortcut.uriToOpen);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapping->targetKeys = AllocateAndCopyString(targetKeysStr);
|
||||
mapping->targetText = AllocateAndCopyString(L"");
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
}
|
||||
else if (targetShortcutUnion.index() == 2)
|
||||
{
|
||||
std::wstring text = std::get<std::wstring>(targetShortcutUnion);
|
||||
mapping->targetKeys = AllocateAndCopyString(L"");
|
||||
mapping->operationType = 0;
|
||||
mapping->targetText = AllocateAndCopyString(text);
|
||||
mapping->programPath = AllocateAndCopyString(L"");
|
||||
mapping->programArgs = AllocateAndCopyString(L"");
|
||||
mapping->uriToOpen = AllocateAndCopyString(L"");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreeString(wchar_t* str)
|
||||
{
|
||||
delete[] str;
|
||||
}
|
||||
|
||||
bool AddSingleKeyRemap(void* config, int originalKey, int targetKey)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
return mappingConfig->AddSingleKeyRemap(static_cast<DWORD>(originalKey), static_cast<DWORD>(targetKey));
|
||||
}
|
||||
|
||||
bool AddSingleKeyToTextRemap(void* config, int originalKey, const wchar_t* text)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
if (text == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return mappingConfig->AddSingleKeyToTextRemap(static_cast<DWORD>(originalKey), text);
|
||||
}
|
||||
|
||||
bool AddSingleKeyToShortcutRemap(void* config, int originalKey, const wchar_t* targetKeys)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
if (!targetKeys)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Shortcut targetShortcut(targetKeys);
|
||||
|
||||
return mappingConfig->AddSingleKeyRemap(static_cast<DWORD>(originalKey), targetShortcut);
|
||||
}
|
||||
|
||||
bool AddShortcutRemap(void* config,
|
||||
const wchar_t* originalKeys,
|
||||
const wchar_t* targetKeys,
|
||||
const wchar_t* targetApp)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
Shortcut originalShortcut(originalKeys);
|
||||
Shortcut targetShortcut(targetKeys);
|
||||
|
||||
std::wstring app(targetApp ? targetApp : L"");
|
||||
|
||||
if (app.empty())
|
||||
{
|
||||
return mappingConfig->AddOSLevelShortcut(originalShortcut, targetShortcut);
|
||||
}
|
||||
else
|
||||
{
|
||||
return mappingConfig->AddAppSpecificShortcut(app, originalShortcut, targetShortcut);
|
||||
}
|
||||
}
|
||||
|
||||
void GetKeyDisplayName(int keyCode, wchar_t* keyName, int maxCount)
|
||||
{
|
||||
if (keyName == nullptr || maxCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
LayoutMap layoutMap;
|
||||
std::wstring name = layoutMap.GetKeyName(static_cast<DWORD>(keyCode));
|
||||
wcsncpy_s(keyName, maxCount, name.c_str(), _TRUNCATE);
|
||||
}
|
||||
|
||||
int GetKeyCodeFromName(const wchar_t* keyName)
|
||||
{
|
||||
if (keyName == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LayoutMap layoutMap;
|
||||
std::wstring name(keyName);
|
||||
return static_cast<int>(layoutMap.GetKeyFromName(name));
|
||||
}
|
||||
|
||||
// Function to get the type of a key (Win, Ctrl, Alt, Shift, or Action)
|
||||
int GetKeyType(int key)
|
||||
{
|
||||
return static_cast<int>(Helpers::GetKeyType(static_cast<DWORD>(key)));
|
||||
}
|
||||
|
||||
// Function to check if a shortcut is illegal
|
||||
bool IsShortcutIllegal(const wchar_t* shortcutKeys)
|
||||
{
|
||||
if (!shortcutKeys)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Shortcut shortcut(shortcutKeys);
|
||||
|
||||
ShortcutErrorType result = EditorHelpers::IsShortcutIllegal(shortcut);
|
||||
|
||||
// Return true if an error was detected (anything other than NoError)
|
||||
return result != ShortcutErrorType::NoError;
|
||||
}
|
||||
|
||||
// Function to check if two shortcuts are equal
|
||||
bool AreShortcutsEqual(const wchar_t* lShort, const wchar_t* rShort)
|
||||
{
|
||||
if (!lShort || !rShort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Shortcut lhs(lShort);
|
||||
Shortcut rhs(rShort);
|
||||
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
// Function to delete a single key remapping
|
||||
bool DeleteSingleKeyRemap(void* config, int originalKey)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
// Find and delete the single key remapping
|
||||
auto it = mappingConfig->singleKeyReMap.find(static_cast<DWORD>(originalKey));
|
||||
if (it != mappingConfig->singleKeyReMap.end())
|
||||
{
|
||||
mappingConfig->singleKeyReMap.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to delete a shortcut remapping
|
||||
bool DeleteShortcutRemap(void* config, const wchar_t* originalKeys, const wchar_t* targetApp)
|
||||
{
|
||||
auto mappingConfig = static_cast<MappingConfiguration*>(config);
|
||||
|
||||
if (originalKeys == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring appName = targetApp ? targetApp : L"";
|
||||
Shortcut shortcut(originalKeys);
|
||||
|
||||
// Determine the type of remapping to delete based on the app name
|
||||
if (appName.empty())
|
||||
{
|
||||
// Delete OS level shortcut mapping
|
||||
auto it = mappingConfig->osLevelShortcutReMap.find(shortcut);
|
||||
if (it != mappingConfig->osLevelShortcutReMap.end())
|
||||
{
|
||||
mappingConfig->osLevelShortcutReMap.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delete app-specific shortcut mapping
|
||||
auto appIt = mappingConfig->appSpecificShortcutReMap.find(appName);
|
||||
if (appIt != mappingConfig->appSpecificShortcutReMap.end())
|
||||
{
|
||||
auto shortcutIt = appIt->second.find(shortcut);
|
||||
if (shortcutIt != appIt->second.end())
|
||||
{
|
||||
appIt->second.erase(shortcutIt);
|
||||
|
||||
// If the app-specific mapping is empty, remove the app entry
|
||||
if (appIt->second.empty())
|
||||
{
|
||||
mappingConfig->appSpecificShortcutReMap.erase(appIt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Test function to call the remapping helper function
|
||||
|
||||
bool CheckIfRemappingsAreValid()
|
||||
{
|
||||
RemapBuffer remapBuffer;
|
||||
@@ -672,26 +18,3 @@ bool CheckIfRemappingsAreValid()
|
||||
|
||||
return result == ShortcutErrorType::NoError;
|
||||
}
|
||||
|
||||
// Get the list of keyboard keys in Editor
|
||||
int GetKeyboardKeysList(bool isShortcut, KeyNamePair* keyList, int maxCount)
|
||||
{
|
||||
if (keyList == nullptr || maxCount <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
LayoutMap layoutMap;
|
||||
auto keyNameList = layoutMap.GetKeyNameList(isShortcut);
|
||||
|
||||
int count = (std::min)(static_cast<int>(keyNameList.size()), maxCount);
|
||||
|
||||
// Transfer the key list to the output struct format
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
keyList[i].keyCode = static_cast<int>(keyNameList[i].first);
|
||||
wcsncpy_s(keyList[i].keyName, keyNameList[i].second.c_str(), _countof(keyList[i].keyName) - 1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -4,76 +4,4 @@
|
||||
#include <keyboardmanager/common/Input.h>
|
||||
#include <keyboardmanager/common/MappingConfiguration.h>
|
||||
|
||||
struct KeyNamePair
|
||||
{
|
||||
int keyCode;
|
||||
wchar_t keyName[64];
|
||||
};
|
||||
|
||||
struct SingleKeyMapping
|
||||
{
|
||||
int originalKey;
|
||||
wchar_t* targetKey;
|
||||
bool isShortcut;
|
||||
};
|
||||
|
||||
struct KeyboardTextMapping
|
||||
{
|
||||
int originalKey;
|
||||
wchar_t* targetText;
|
||||
};
|
||||
|
||||
struct ShortcutMapping
|
||||
{
|
||||
wchar_t* originalKeys;
|
||||
wchar_t* targetKeys;
|
||||
wchar_t* targetApp;
|
||||
int operationType;
|
||||
wchar_t* targetText;
|
||||
wchar_t* programPath;
|
||||
wchar_t* programArgs;
|
||||
wchar_t* uriToOpen;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllexport) void* CreateMappingConfiguration();
|
||||
__declspec(dllexport) void DestroyMappingConfiguration(void* config);
|
||||
__declspec(dllexport) bool LoadMappingSettings(void* config);
|
||||
__declspec(dllexport) bool SaveMappingSettings(void* config);
|
||||
|
||||
__declspec(dllexport) int GetSingleKeyRemapCount(void* config);
|
||||
__declspec(dllexport) bool GetSingleKeyRemap(void* config, int index, SingleKeyMapping* mapping);
|
||||
|
||||
__declspec(dllexport) int GetSingleKeyToTextRemapCount(void* config);
|
||||
__declspec(dllexport) bool GetSingleKeyToTextRemap(void* config, int index, KeyboardTextMapping* mapping);
|
||||
|
||||
__declspec(dllexport) int GetShortcutRemapCountByType(void* config, int operationType);
|
||||
__declspec(dllexport) bool GetShortcutRemapByType(void* config, int operationType, int index, ShortcutMapping* mapping);
|
||||
|
||||
__declspec(dllexport) int GetShortcutRemapCount(void* config);
|
||||
__declspec(dllexport) bool GetShortcutRemap(void* config, int index, ShortcutMapping* mapping);
|
||||
|
||||
__declspec(dllexport) bool AddSingleKeyRemap(void* config, int originalKey, int targetKey);
|
||||
__declspec(dllexport) bool AddSingleKeyToTextRemap(void* config, int originalKey, const wchar_t* text);
|
||||
__declspec(dllexport) bool AddSingleKeyToShortcutRemap(void* config,
|
||||
int originalKey,
|
||||
const wchar_t* targetKeys);
|
||||
__declspec(dllexport) bool AddShortcutRemap(void* config,
|
||||
const wchar_t* originalKeys,
|
||||
const wchar_t* targetKeys,
|
||||
const wchar_t* targetApp);
|
||||
|
||||
__declspec(dllexport) void GetKeyDisplayName(int keyCode, wchar_t* keyName, int maxCount);
|
||||
__declspec(dllexport) int GetKeyCodeFromName(const wchar_t* keyName);
|
||||
__declspec(dllexport) void FreeString(wchar_t* str);
|
||||
__declspec(dllexport) int GetKeyType(int keyCode);
|
||||
|
||||
__declspec(dllexport) bool IsShortcutIllegal(const wchar_t* shortcutKeys);
|
||||
__declspec(dllexport) bool AreShortcutsEqual(const wchar_t* lShort, const wchar_t* rShort);
|
||||
|
||||
__declspec(dllexport) bool DeleteSingleKeyRemap(void* config, int originalKey);
|
||||
__declspec(dllexport) bool DeleteShortcutRemap(void* config, const wchar_t* originalKeys, const wchar_t* targetApp);
|
||||
}
|
||||
extern "C" __declspec(dllexport) bool CheckIfRemappingsAreValid();
|
||||
extern "C" __declspec(dllexport) int GetKeyboardKeysList(bool isShortcut, KeyNamePair* keyList, int maxCount);
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="/Styles/KeyVisual.xaml" />
|
||||
<ResourceDictionary Source="/Styles/CommonStyle.xaml" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Other app resources here -->
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -7,11 +7,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
@@ -39,13 +35,8 @@ namespace KeyboardManagerEditorUI
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logger.InitializeLogger("\\Keyboard Manager\\WinUI3Editor\\Logs");
|
||||
});
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
Logger.InitializeLogger("\\Keyboard Manager\\WinUI3Editor\\Logs");
|
||||
Logger.LogInfo("keyboard-manager WinUI3 editor logger is initialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,37 +46,10 @@ namespace KeyboardManagerEditorUI
|
||||
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
|
||||
{
|
||||
window = new MainWindow();
|
||||
|
||||
var appWindow = window.AppWindow;
|
||||
|
||||
var windowSize = new Windows.Graphics.SizeInt32(EditorConstants.DefaultEditorWindowWidth, EditorConstants.DefaultEditorWindowHeight);
|
||||
appWindow.Resize(windowSize);
|
||||
|
||||
window.DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
window.Activate();
|
||||
window.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
(window.Content as FrameworkElement)?.UpdateLayout();
|
||||
});
|
||||
});
|
||||
|
||||
window.Activate();
|
||||
Logger.LogInfo("keyboard-manager WinUI3 editor window is launched");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log the unhandled exception for the editor.
|
||||
/// </summary>
|
||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.LogError("Unhandled exception", e.Exception);
|
||||
}
|
||||
|
||||
public Window? GetWindow()
|
||||
{
|
||||
return window;
|
||||
}
|
||||
|
||||
private Window? window;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,22 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public static class EditorConstants
|
||||
{
|
||||
// Default window size
|
||||
public const int DefaultEditorWindowWidth = 960;
|
||||
public const int DefaultEditorWindowHeight = 600;
|
||||
|
||||
// Default notification timeout
|
||||
public const int DefaultNotificationTimeout = 1500;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +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.
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public enum KeyInputMode
|
||||
{
|
||||
OriginalKeys,
|
||||
RemappedKeys,
|
||||
}
|
||||
}
|
||||
@@ -1,241 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Windows.System;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public class KeyboardHookHelper : IDisposable
|
||||
{
|
||||
private static KeyboardHookHelper? _instance;
|
||||
|
||||
public static KeyboardHookHelper Instance => _instance ??= new KeyboardHookHelper();
|
||||
|
||||
private KeyboardMappingService _mappingService;
|
||||
|
||||
private HotkeySettingsControlHook? _keyboardHook;
|
||||
|
||||
// The active page using this keyboard hook
|
||||
private IKeyboardHookTarget? _activeTarget;
|
||||
|
||||
private HashSet<VirtualKey> _currentlyPressedKeys = new();
|
||||
private List<VirtualKey> _keyPressOrder = new();
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
// Singleton to make sure only one instance of the hook is active
|
||||
private KeyboardHookHelper()
|
||||
{
|
||||
_mappingService = new KeyboardMappingService();
|
||||
}
|
||||
|
||||
public void ActivateHook(IKeyboardHookTarget target)
|
||||
{
|
||||
CleanupHook();
|
||||
|
||||
_activeTarget = target;
|
||||
|
||||
_currentlyPressedKeys.Clear();
|
||||
_keyPressOrder.Clear();
|
||||
|
||||
_keyboardHook = new HotkeySettingsControlHook(
|
||||
KeyDown,
|
||||
KeyUp,
|
||||
() => true,
|
||||
(key, extraInfo) => true);
|
||||
}
|
||||
|
||||
public void CleanupHook()
|
||||
{
|
||||
if (_keyboardHook != null)
|
||||
{
|
||||
_keyboardHook.Dispose();
|
||||
_keyboardHook = null;
|
||||
}
|
||||
|
||||
_currentlyPressedKeys.Clear();
|
||||
_keyPressOrder.Clear();
|
||||
_activeTarget = null;
|
||||
}
|
||||
|
||||
private void KeyDown(int key)
|
||||
{
|
||||
if (_activeTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualKey virtualKey = (VirtualKey)key;
|
||||
|
||||
if (_currentlyPressedKeys.Contains(virtualKey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if no keys are pressed, clear the lists when a new key is pressed
|
||||
if (_currentlyPressedKeys.Count == 0)
|
||||
{
|
||||
_activeTarget.ClearKeys();
|
||||
_keyPressOrder.Clear();
|
||||
}
|
||||
|
||||
// Count current modifiers
|
||||
int modifierCount = _currentlyPressedKeys.Count(k => _activeTarget.IsModifierKey(k));
|
||||
|
||||
// If adding this key would exceed the limits (4 modifiers + 1 action key), don't add it and show notification
|
||||
if ((_activeTarget.IsModifierKey(virtualKey) && modifierCount >= 4) ||
|
||||
(!_activeTarget.IsModifierKey(virtualKey) && _currentlyPressedKeys.Count >= 5))
|
||||
{
|
||||
_activeTarget.OnInputLimitReached();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a different variant of a modifier key already pressed
|
||||
if (_activeTarget.IsModifierKey(virtualKey))
|
||||
{
|
||||
// Remove existing variant of this modifier key if a new one is pressed
|
||||
// This is to ensure that only one variant of a modifier key is displayed at a time
|
||||
RemoveExistingModifierVariant(virtualKey);
|
||||
}
|
||||
|
||||
if (_currentlyPressedKeys.Add(virtualKey))
|
||||
{
|
||||
_keyPressOrder.Add(virtualKey);
|
||||
|
||||
// Notify the target page
|
||||
_activeTarget.OnKeyDown(virtualKey, GetFormattedKeyList());
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyUp(int key)
|
||||
{
|
||||
if (_activeTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualKey virtualKey = (VirtualKey)key;
|
||||
|
||||
if (_currentlyPressedKeys.Remove(virtualKey))
|
||||
{
|
||||
_keyPressOrder.Remove(virtualKey);
|
||||
|
||||
_activeTarget.OnKeyUp(virtualKey, GetFormattedKeyList());
|
||||
}
|
||||
}
|
||||
|
||||
// Display the modifier keys and the action key in order, e.g. "Ctrl + Alt + A"
|
||||
private List<string> GetFormattedKeyList()
|
||||
{
|
||||
if (_activeTarget == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
List<string> keyList = new List<string>();
|
||||
List<VirtualKey> modifierKeys = new List<VirtualKey>();
|
||||
VirtualKey? actionKey = null;
|
||||
|
||||
foreach (var key in _keyPressOrder)
|
||||
{
|
||||
if (!_currentlyPressedKeys.Contains(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_activeTarget.IsModifierKey(key))
|
||||
{
|
||||
if (!modifierKeys.Contains(key))
|
||||
{
|
||||
modifierKeys.Add(key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
actionKey = key;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in modifierKeys)
|
||||
{
|
||||
keyList.Add(_mappingService.GetKeyDisplayName((int)key));
|
||||
}
|
||||
|
||||
if (actionKey.HasValue)
|
||||
{
|
||||
keyList.Add(_mappingService.GetKeyDisplayName((int)actionKey.Value));
|
||||
}
|
||||
|
||||
return keyList;
|
||||
}
|
||||
|
||||
private void RemoveExistingModifierVariant(VirtualKey key)
|
||||
{
|
||||
KeyType keyType = (KeyType)KeyboardManagerInterop.GetKeyType((int)key);
|
||||
|
||||
// No need to remove if the key is an action key
|
||||
if (keyType == KeyType.Action)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var existingKey in _currentlyPressedKeys.ToList())
|
||||
{
|
||||
if (existingKey != key)
|
||||
{
|
||||
KeyType existingKeyType = (KeyType)KeyboardManagerInterop.GetKeyType((int)existingKey);
|
||||
|
||||
// Remove the existing key if it is a modifier key and has the same type as the new key
|
||||
if (existingKeyType == keyType)
|
||||
{
|
||||
_currentlyPressedKeys.Remove(existingKey);
|
||||
_keyPressOrder.Remove(existingKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
CleanupHook();
|
||||
_mappingService?.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IKeyboardHookTarget
|
||||
{
|
||||
void OnKeyDown(VirtualKey key, List<string> formattedKeys);
|
||||
|
||||
void OnKeyUp(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
}
|
||||
|
||||
void ClearKeys();
|
||||
|
||||
bool IsModifierKey(VirtualKey key);
|
||||
|
||||
void OnInputLimitReached();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public partial class Remapping : INotifyPropertyChanged
|
||||
{
|
||||
public List<string> OriginalKeys { get; set; } = new List<string>();
|
||||
|
||||
public List<string> RemappedKeys { get; set; } = new List<string>();
|
||||
|
||||
public bool IsAllApps { get; set; } = true;
|
||||
|
||||
public string AppName { get; set; } = "All Apps";
|
||||
|
||||
private bool IsEnabledValue { get; set; } = true;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => IsEnabledValue;
|
||||
set
|
||||
{
|
||||
if (IsEnabledValue != value)
|
||||
{
|
||||
IsEnabledValue = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,128 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public static class RemappingHelper
|
||||
{
|
||||
public static bool SaveMapping(KeyboardMappingService mappingService, List<string> originalKeys, List<string> remappedKeys, bool isAppSpecific, string appName)
|
||||
{
|
||||
if (mappingService == null)
|
||||
{
|
||||
Logger.LogError("Mapping service is null, cannot save mapping");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (originalKeys == null || originalKeys.Count == 0 || remappedKeys == null || remappedKeys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (originalKeys.Count == 1)
|
||||
{
|
||||
int originalKey = mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
if (remappedKeys.Count == 1)
|
||||
{
|
||||
int targetKey = mappingService.GetKeyCodeFromName(remappedKeys[0]);
|
||||
if (targetKey != 0)
|
||||
{
|
||||
mappingService.AddSingleKeyMapping(originalKey, targetKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string targetKeys = string.Join(";", remappedKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
mappingService.AddSingleKeyMapping(originalKey, targetKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeysString = string.Join(";", originalKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
string targetKeysString = string.Join(";", remappedKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
if (isAppSpecific && !string.IsNullOrEmpty(appName))
|
||||
{
|
||||
mappingService.AddShortcutMapping(originalKeysString, targetKeysString, appName);
|
||||
}
|
||||
else
|
||||
{
|
||||
mappingService.AddShortcutMapping(originalKeysString, targetKeysString);
|
||||
}
|
||||
}
|
||||
|
||||
return mappingService.SaveSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error saving mapping: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DeleteRemapping(KeyboardMappingService mappingService, Remapping remapping)
|
||||
{
|
||||
if (mappingService == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (remapping.OriginalKeys.Count == 1)
|
||||
{
|
||||
// Single key mapping
|
||||
int originalKey = mappingService.GetKeyCodeFromName(remapping.OriginalKeys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
if (mappingService.DeleteSingleKeyMapping(originalKey))
|
||||
{
|
||||
// Save settings after successful deletion
|
||||
return mappingService.SaveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (remapping.OriginalKeys.Count > 1)
|
||||
{
|
||||
// Shortcut mapping
|
||||
string originalKeysString = string.Join(";", remapping.OriginalKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
bool deleteResult;
|
||||
if (!remapping.IsAllApps && !string.IsNullOrEmpty(remapping.AppName))
|
||||
{
|
||||
// App-specific shortcut key mapping
|
||||
deleteResult = mappingService.DeleteShortcutMapping(originalKeysString, remapping.AppName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Global shortcut key mapping
|
||||
deleteResult = mappingService.DeleteShortcutMapping(originalKeysString);
|
||||
}
|
||||
|
||||
return deleteResult ? mappingService.SaveSettings() : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Error deleting remapping: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public class URLShortcut
|
||||
{
|
||||
public List<string> Shortcut { get; set; } = new List<string>();
|
||||
|
||||
public string URL { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public enum ValidationErrorType
|
||||
{
|
||||
NoError,
|
||||
EmptyOriginalKeys,
|
||||
EmptyRemappedKeys,
|
||||
ModifierOnly,
|
||||
EmptyAppName,
|
||||
IllegalShortcut,
|
||||
DuplicateMapping,
|
||||
SelfMapping,
|
||||
}
|
||||
}
|
||||
@@ -1,252 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Helpers
|
||||
{
|
||||
public static class ValidationHelper
|
||||
{
|
||||
public static readonly Dictionary<ValidationErrorType, (string Title, string Message)> ValidationMessages = new()
|
||||
{
|
||||
{ ValidationErrorType.EmptyOriginalKeys, ("Missing Original Keys", "Please enter at least one original key to create a remapping.") },
|
||||
{ ValidationErrorType.EmptyRemappedKeys, ("Missing Target Keys", "Please enter at least one target key to create a remapping.") },
|
||||
{ ValidationErrorType.ModifierOnly, ("Invalid Shortcut", "Shortcuts must contain at least one action key in addition to modifier keys (Ctrl, Alt, Shift, Win).") },
|
||||
{ ValidationErrorType.EmptyAppName, ("Missing Application Name", "You've selected app-specific remapping but haven't specified an application name. Please enter the application name.") },
|
||||
{ ValidationErrorType.IllegalShortcut, ("Reserved System Shortcut", "Win+L and Ctrl+Alt+Delete are reserved system shortcuts and cannot be remapped.") },
|
||||
{ ValidationErrorType.DuplicateMapping, ("Duplicate Remapping", "This key or shortcut is already remapped.") },
|
||||
{ ValidationErrorType.SelfMapping, ("Invalid Remapping", "A key or shortcut cannot be remapped to itself. Please choose a different target.") },
|
||||
};
|
||||
|
||||
public static ValidationErrorType ValidateKeyMapping(
|
||||
List<string> originalKeys,
|
||||
List<string> remappedKeys,
|
||||
bool isAppSpecific,
|
||||
string appName,
|
||||
KeyboardMappingService mappingService,
|
||||
bool isEditMode = false,
|
||||
Remapping? editingRemapping = null)
|
||||
{
|
||||
// Check if original keys are empty
|
||||
if (originalKeys == null || originalKeys.Count == 0)
|
||||
{
|
||||
return ValidationErrorType.EmptyOriginalKeys;
|
||||
}
|
||||
|
||||
// Check if remapped keys are empty
|
||||
if (remappedKeys == null || remappedKeys.Count == 0)
|
||||
{
|
||||
return ValidationErrorType.EmptyRemappedKeys;
|
||||
}
|
||||
|
||||
// Check if shortcut contains only modifier keys
|
||||
if ((originalKeys.Count > 1 && ContainsOnlyModifierKeys(originalKeys)) ||
|
||||
(remappedKeys.Count > 1 && ContainsOnlyModifierKeys(remappedKeys)))
|
||||
{
|
||||
return ValidationErrorType.ModifierOnly;
|
||||
}
|
||||
|
||||
// Check if app specific is checked but no app name is provided
|
||||
if (isAppSpecific && string.IsNullOrWhiteSpace(appName))
|
||||
{
|
||||
return ValidationErrorType.EmptyAppName;
|
||||
}
|
||||
|
||||
// Check if this is a shortcut (multiple keys) and if it's an illegal combination
|
||||
if (originalKeys.Count > 1)
|
||||
{
|
||||
string shortcutKeysString = string.Join(";", originalKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
if (KeyboardManagerInterop.IsShortcutIllegal(shortcutKeysString))
|
||||
{
|
||||
return ValidationErrorType.IllegalShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for duplicate mappings
|
||||
if (IsDuplicateMapping(originalKeys, isAppSpecific, appName, mappingService, isEditMode, editingRemapping))
|
||||
{
|
||||
return ValidationErrorType.DuplicateMapping;
|
||||
}
|
||||
|
||||
// Check for self-mapping
|
||||
if (IsSelfMapping(originalKeys, remappedKeys, mappingService))
|
||||
{
|
||||
return ValidationErrorType.SelfMapping;
|
||||
}
|
||||
|
||||
return ValidationErrorType.NoError;
|
||||
}
|
||||
|
||||
public static bool IsDuplicateMapping(
|
||||
List<string> originalKeys,
|
||||
bool isAppSpecific,
|
||||
string appName,
|
||||
KeyboardMappingService mappingService,
|
||||
bool isEditMode = false,
|
||||
Remapping? editingRemapping = null)
|
||||
{
|
||||
if (mappingService == null || originalKeys == null || originalKeys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For single key remapping
|
||||
if (originalKeys.Count == 1)
|
||||
{
|
||||
int originalKeyCode = mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
if (originalKeyCode == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the key is already remapped
|
||||
foreach (var mapping in mappingService.GetSingleKeyMappings())
|
||||
{
|
||||
if (mapping.OriginalKey == originalKeyCode)
|
||||
{
|
||||
// Skip if the remapping is the same as the one being edited
|
||||
if (isEditMode && editingRemapping != null &&
|
||||
editingRemapping.OriginalKeys.Count == 1 &&
|
||||
mappingService.GetKeyCodeFromName(editingRemapping.OriginalKeys[0]) == originalKeyCode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For shortcut remapping
|
||||
else
|
||||
{
|
||||
string originalKeysString = string.Join(";", originalKeys.Select(
|
||||
k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
// Don't check for duplicates if the original keys are the same as the remapping being edited
|
||||
bool isEditingExistingRemapping = false;
|
||||
if (isEditMode && editingRemapping != null)
|
||||
{
|
||||
string editingOriginalKeysString = string.Join(";", editingRemapping.OriginalKeys.Select(k =>
|
||||
mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
if (KeyboardManagerInterop.AreShortcutsEqual(originalKeysString, editingOriginalKeysString))
|
||||
{
|
||||
isEditingExistingRemapping = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the shortcut is already remapped in the same app context
|
||||
foreach (var mapping in mappingService.GetShortcutMappingsByType(ShortcutOperationType.RemapShortcut))
|
||||
{
|
||||
if (KeyboardManagerInterop.AreShortcutsEqual(originalKeysString, mapping.OriginalKeys))
|
||||
{
|
||||
// If both are global (all apps)
|
||||
if (!isAppSpecific && string.IsNullOrEmpty(mapping.TargetApp))
|
||||
{
|
||||
// Skip if the remapping is the same as the one being edited
|
||||
if (editingRemapping != null && editingRemapping.OriginalKeys.Count > 1 && editingRemapping.IsAllApps && isEditingExistingRemapping)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If both are for the same specific app
|
||||
else if (isAppSpecific && !string.IsNullOrEmpty(mapping.TargetApp)
|
||||
&& string.Equals(mapping.TargetApp, appName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Skip if the remapping is the same as the one being edited
|
||||
if (editingRemapping != null && editingRemapping.OriginalKeys.Count > 1 && !editingRemapping.IsAllApps &&
|
||||
string.Equals(editingRemapping.AppName, appName, StringComparison.OrdinalIgnoreCase) && isEditingExistingRemapping)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsSelfMapping(List<string> originalKeys, List<string> remappedKeys, KeyboardMappingService mappingService)
|
||||
{
|
||||
if (mappingService == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If either list is empty, it's not a self-mapping
|
||||
if (originalKeys == null || remappedKeys == null ||
|
||||
originalKeys.Count == 0 || remappedKeys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string originalKeysString = string.Join(";", originalKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
string remappedKeysString = string.Join(";", remappedKeys.Select(k => mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
return KeyboardManagerInterop.AreShortcutsEqual(originalKeysString, remappedKeysString);
|
||||
}
|
||||
|
||||
public static bool ContainsOnlyModifierKeys(List<string> keys)
|
||||
{
|
||||
if (keys == null || keys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string key in keys)
|
||||
{
|
||||
int keyCode = KeyboardManagerInterop.GetKeyCodeFromName(key);
|
||||
var keyType = (KeyType)KeyboardManagerInterop.GetKeyType(keyCode);
|
||||
|
||||
// If any key is an action key, return false
|
||||
if (keyType == KeyType.Action)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All keys are modifier keys
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsKeyOrphaned(int originalKey, KeyboardMappingService mappingService)
|
||||
{
|
||||
// Check all single key mappings
|
||||
foreach (var mapping in mappingService.GetSingleKeyMappings())
|
||||
{
|
||||
if (!mapping.IsShortcut && int.TryParse(mapping.TargetKey, out int targetKey) && targetKey == originalKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check all shortcut mappings
|
||||
foreach (var mapping in mappingService.GetShortcutMappings())
|
||||
{
|
||||
string[] targetKeys = mapping.TargetKeys.Split(';');
|
||||
if (targetKeys.Length == 1 && int.TryParse(targetKeys[0], out int shortcutTargetKey) && shortcutTargetKey == originalKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// No mapping found for the original key
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public class KeyMapping
|
||||
{
|
||||
public int OriginalKey { get; set; }
|
||||
|
||||
public string TargetKey { get; set; } = string.Empty;
|
||||
|
||||
public bool IsShortcut { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public class KeyToTextMapping
|
||||
{
|
||||
public int OriginalKey { get; set; }
|
||||
|
||||
public string TargetText { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public enum KeyType
|
||||
{
|
||||
Win = 0,
|
||||
Ctrl = 1,
|
||||
Alt = 2,
|
||||
Shift = 3,
|
||||
Action = 4,
|
||||
}
|
||||
}
|
||||
@@ -1,143 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public static class KeyboardManagerInterop
|
||||
{
|
||||
private const string DllName = "Powertoys.KeyboardManagerEditorLibraryWrapper.dll";
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern IntPtr CreateMappingConfiguration();
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern void DestroyMappingConfiguration(IntPtr config);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool LoadMappingSettings(IntPtr config);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool SaveMappingSettings(IntPtr config);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern int GetSingleKeyRemapCount(IntPtr config);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetSingleKeyRemap(IntPtr config, int index, ref SingleKeyMapping mapping);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern int GetSingleKeyToTextRemapCount(IntPtr config);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetSingleKeyToTextRemap(IntPtr config, int index, ref KeyboardTextMapping mapping);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern int GetShortcutRemapCount(IntPtr config);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetShortcutRemap(IntPtr config, int index, ref ShortcutMapping mapping);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern int GetShortcutRemapCountByType(IntPtr config, int operationType);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool GetShortcutRemapByType(IntPtr config, int operationType, int index, ref ShortcutMapping mapping);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AddSingleKeyRemap(IntPtr config, int originalKey, int targetKey);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AddSingleKeyToShortcutRemap(IntPtr config, int originalKey, [MarshalAs(UnmanagedType.LPWStr)] string targetKeys);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AddShortcutRemap(
|
||||
IntPtr config,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string originalKeys,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string targetKeys,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string targetApp);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern int GetKeyCodeFromName([MarshalAs(UnmanagedType.LPWStr)] string keyName);
|
||||
|
||||
[DllImport(DllName, CharSet = CharSet.Unicode)]
|
||||
internal static extern void GetKeyDisplayName(int keyCode, [Out] StringBuilder keyName, int maxLength);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern void FreeString(IntPtr str);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern int GetKeyType(int keyCode);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool IsShortcutIllegal([MarshalAs(UnmanagedType.LPWStr)] string shortcutKeys);
|
||||
|
||||
[DllImport(DllName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AreShortcutsEqual([MarshalAs(UnmanagedType.LPWStr)] string lShort, [MarshalAs(UnmanagedType.LPWStr)] string rShortcut);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern bool DeleteSingleKeyRemap(IntPtr mappingConfiguration, int originalKey);
|
||||
|
||||
[DllImport(DllName)]
|
||||
internal static extern bool DeleteShortcutRemap(IntPtr mappingConfiguration, [MarshalAs(UnmanagedType.LPWStr)] string originalKeys, [MarshalAs(UnmanagedType.LPWStr)] string targetApp);
|
||||
|
||||
public static string GetStringAndFree(IntPtr handle)
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string? result = Marshal.PtrToStringUni(handle);
|
||||
FreeString(handle);
|
||||
return result ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SingleKeyMapping
|
||||
{
|
||||
public int OriginalKey;
|
||||
public IntPtr TargetKey;
|
||||
[MarshalAs(UnmanagedType.Bool)]
|
||||
public bool IsShortcut;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KeyboardTextMapping
|
||||
{
|
||||
public int OriginalKey;
|
||||
public IntPtr TargetText;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ShortcutMapping
|
||||
{
|
||||
public IntPtr OriginalKeys;
|
||||
public IntPtr TargetKeys;
|
||||
public IntPtr TargetApp;
|
||||
public int OperationType;
|
||||
public IntPtr TargetText;
|
||||
public IntPtr ProgramPath;
|
||||
public IntPtr ProgramArgs;
|
||||
public IntPtr UriToOpen;
|
||||
}
|
||||
}
|
||||
@@ -1,223 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public class KeyboardMappingService : IDisposable
|
||||
{
|
||||
private IntPtr _configHandle;
|
||||
private bool _disposed;
|
||||
|
||||
public KeyboardMappingService()
|
||||
{
|
||||
_configHandle = KeyboardManagerInterop.CreateMappingConfiguration();
|
||||
if (_configHandle == IntPtr.Zero)
|
||||
{
|
||||
Logger.LogError("Failed to create mapping configuration");
|
||||
throw new InvalidOperationException("Failed to create mapping configuration");
|
||||
}
|
||||
|
||||
KeyboardManagerInterop.LoadMappingSettings(_configHandle);
|
||||
}
|
||||
|
||||
public List<KeyMapping> GetSingleKeyMappings()
|
||||
{
|
||||
var result = new List<KeyMapping>();
|
||||
int count = KeyboardManagerInterop.GetSingleKeyRemapCount(_configHandle);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var mapping = default(SingleKeyMapping);
|
||||
if (KeyboardManagerInterop.GetSingleKeyRemap(_configHandle, i, ref mapping))
|
||||
{
|
||||
result.Add(new KeyMapping
|
||||
{
|
||||
OriginalKey = mapping.OriginalKey,
|
||||
TargetKey = KeyboardManagerInterop.GetStringAndFree(mapping.TargetKey),
|
||||
IsShortcut = mapping.IsShortcut,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<ShortcutKeyMapping> GetShortcutMappings()
|
||||
{
|
||||
var result = new List<ShortcutKeyMapping>();
|
||||
int count = KeyboardManagerInterop.GetShortcutRemapCount(_configHandle);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var mapping = default(ShortcutMapping);
|
||||
if (KeyboardManagerInterop.GetShortcutRemap(_configHandle, i, ref mapping))
|
||||
{
|
||||
result.Add(new ShortcutKeyMapping
|
||||
{
|
||||
OriginalKeys = KeyboardManagerInterop.GetStringAndFree(mapping.OriginalKeys),
|
||||
TargetKeys = KeyboardManagerInterop.GetStringAndFree(mapping.TargetKeys),
|
||||
TargetApp = KeyboardManagerInterop.GetStringAndFree(mapping.TargetApp),
|
||||
OperationType = (ShortcutOperationType)mapping.OperationType,
|
||||
TargetText = KeyboardManagerInterop.GetStringAndFree(mapping.TargetText),
|
||||
ProgramPath = KeyboardManagerInterop.GetStringAndFree(mapping.ProgramPath),
|
||||
ProgramArgs = KeyboardManagerInterop.GetStringAndFree(mapping.ProgramArgs),
|
||||
UriToOpen = KeyboardManagerInterop.GetStringAndFree(mapping.UriToOpen),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<ShortcutKeyMapping> GetShortcutMappingsByType(ShortcutOperationType operationType)
|
||||
{
|
||||
var result = new List<ShortcutKeyMapping>();
|
||||
int count = KeyboardManagerInterop.GetShortcutRemapCountByType(_configHandle, (int)operationType);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var mapping = default(ShortcutMapping);
|
||||
if (KeyboardManagerInterop.GetShortcutRemapByType(_configHandle, (int)operationType, i, ref mapping))
|
||||
{
|
||||
result.Add(new ShortcutKeyMapping
|
||||
{
|
||||
OriginalKeys = KeyboardManagerInterop.GetStringAndFree(mapping.OriginalKeys),
|
||||
TargetKeys = KeyboardManagerInterop.GetStringAndFree(mapping.TargetKeys),
|
||||
TargetApp = KeyboardManagerInterop.GetStringAndFree(mapping.TargetApp),
|
||||
OperationType = (ShortcutOperationType)mapping.OperationType,
|
||||
TargetText = KeyboardManagerInterop.GetStringAndFree(mapping.TargetText),
|
||||
ProgramPath = KeyboardManagerInterop.GetStringAndFree(mapping.ProgramPath),
|
||||
ProgramArgs = KeyboardManagerInterop.GetStringAndFree(mapping.ProgramArgs),
|
||||
UriToOpen = KeyboardManagerInterop.GetStringAndFree(mapping.UriToOpen),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<KeyToTextMapping> GetKeyToTextMappings()
|
||||
{
|
||||
var result = new List<KeyToTextMapping>();
|
||||
int count = KeyboardManagerInterop.GetSingleKeyToTextRemapCount(_configHandle);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var mapping = default(KeyboardTextMapping);
|
||||
if (KeyboardManagerInterop.GetSingleKeyToTextRemap(_configHandle, i, ref mapping))
|
||||
{
|
||||
result.Add(new KeyToTextMapping
|
||||
{
|
||||
OriginalKey = mapping.OriginalKey,
|
||||
TargetText = KeyboardManagerInterop.GetStringAndFree(mapping.TargetText),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public string GetKeyDisplayName(int keyCode)
|
||||
{
|
||||
var keyName = new StringBuilder(64);
|
||||
KeyboardManagerInterop.GetKeyDisplayName(keyCode, keyName, keyName.Capacity);
|
||||
return keyName.ToString();
|
||||
}
|
||||
|
||||
public int GetKeyCodeFromName(string keyName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(keyName))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return KeyboardManagerInterop.GetKeyCodeFromName(keyName);
|
||||
}
|
||||
|
||||
public bool AddSingleKeyMapping(int originalKey, int targetKey)
|
||||
{
|
||||
return KeyboardManagerInterop.AddSingleKeyRemap(_configHandle, originalKey, targetKey);
|
||||
}
|
||||
|
||||
public bool AddSingleKeyMapping(int originalKey, string targetKeys)
|
||||
{
|
||||
if (string.IsNullOrEmpty(targetKeys))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetKeys.Contains(';') && int.TryParse(targetKeys, out int targetKey))
|
||||
{
|
||||
return KeyboardManagerInterop.AddSingleKeyRemap(_configHandle, originalKey, targetKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return KeyboardManagerInterop.AddSingleKeyToShortcutRemap(_configHandle, originalKey, targetKeys);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddShortcutMapping(string originalKeys, string targetKeys, string targetApp = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(originalKeys) || string.IsNullOrEmpty(targetKeys))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return KeyboardManagerInterop.AddShortcutRemap(_configHandle, originalKeys, targetKeys, targetApp);
|
||||
}
|
||||
|
||||
public bool SaveSettings()
|
||||
{
|
||||
return KeyboardManagerInterop.SaveMappingSettings(_configHandle);
|
||||
}
|
||||
|
||||
public bool DeleteSingleKeyMapping(int originalKey)
|
||||
{
|
||||
return KeyboardManagerInterop.DeleteSingleKeyRemap(_configHandle, originalKey);
|
||||
}
|
||||
|
||||
public bool DeleteShortcutMapping(string originalKeys, string targetApp = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(originalKeys))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return KeyboardManagerInterop.DeleteShortcutRemap(_configHandle, originalKeys, targetApp ?? string.Empty);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (_configHandle != IntPtr.Zero)
|
||||
{
|
||||
KeyboardManagerInterop.DestroyMappingConfiguration(_configHandle);
|
||||
_configHandle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~KeyboardMappingService()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public class ShortcutKeyMapping
|
||||
{
|
||||
public string OriginalKeys { get; set; } = string.Empty;
|
||||
|
||||
public string TargetKeys { get; set; } = string.Empty;
|
||||
|
||||
public string TargetApp { get; set; } = string.Empty;
|
||||
|
||||
public ShortcutOperationType OperationType { get; set; }
|
||||
|
||||
public string TargetText { get; set; } = string.Empty;
|
||||
|
||||
public string ProgramPath { get; set; } = string.Empty;
|
||||
|
||||
public string ProgramArgs { get; set; } = string.Empty;
|
||||
|
||||
public string UriToOpen { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Interop
|
||||
{
|
||||
public enum ShortcutOperationType
|
||||
{
|
||||
RemapShortcut = 0,
|
||||
RunProgram = 1,
|
||||
OpenUri = 2,
|
||||
}
|
||||
}
|
||||
@@ -18,21 +18,6 @@
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\$(MSBuildProjectName)</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableDefaultXamlItems>true</EnableDefaultXamlItems>
|
||||
<EnableXamlJitOptimization>true</EnableXamlJitOptimization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Pages\ExistingUI.xaml" />
|
||||
<None Remove="Pages\Programs.xaml" />
|
||||
<None Remove="Pages\Text.xaml" />
|
||||
<None Remove="Pages\URLs.xaml" />
|
||||
<None Remove="Styles\CommonStyle.xaml" />
|
||||
<None Remove="Styles\InputControl.xaml" />
|
||||
<None Remove="Styles\KeyVisual.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
@@ -48,7 +33,6 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Markdown" />
|
||||
<!-- This line forces the WebView2 version used by Windows App SDK to be the one we expect from Directory.Packages.props . -->
|
||||
<PackageReference Include="Microsoft.Web.WebView2" />
|
||||
</ItemGroup>
|
||||
@@ -58,44 +42,7 @@
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Styles\CommonStyle.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\URLs.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\Text.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\Programs.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\Remappings.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Styles\InputControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Styles\KeyVisual.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\ExistingUI.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Folder Include="Assets\" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -6,68 +6,13 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:KeyboardManagerEditorUI"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:pages="using:KeyboardManagerEditorUI.Pages"
|
||||
Title="KeyboardManagerEditorUI"
|
||||
mc:Ignorable="d">
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
<Grid
|
||||
x:Name="LayoutRoot"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="titleBar">
|
||||
<StackPanel
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="16,8,8,8"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Horizontal">
|
||||
<Image Width="16" Source="ms-appx:///Assets/FluentIconsKeyboardManager.png" />
|
||||
<TextBlock
|
||||
Margin="12,0,0,0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Keyboard Manager" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<NavigationView
|
||||
x:Name="RootView"
|
||||
Grid.Row="1"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
IsBackEnabled="False"
|
||||
IsPaneToggleButtonVisible="False"
|
||||
IsSettingsVisible="False"
|
||||
PaneDisplayMode="Top"
|
||||
SelectionChanged="RootView_SelectionChanged">
|
||||
<NavigationView.MenuItems>
|
||||
<NavigationViewItem Content="Remappings" Tag="Remappings">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Programs" Tag="Programs">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Text" Tag="Text">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="URLs" Tag="URLs">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.MenuItems>
|
||||
<NavigationView.Content>
|
||||
<Frame x:Name="NavigationFrame" Margin="0,0,0,0" />
|
||||
</NavigationView.Content>
|
||||
</NavigationView>
|
||||
</Grid>
|
||||
</Window>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button x:Name="myButton" Click="MyButton_Click">Click Me</Button>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
@@ -22,52 +20,23 @@ using Windows.Foundation.Collections;
|
||||
|
||||
namespace KeyboardManagerEditorUI
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainWindow : Window
|
||||
{
|
||||
[DllImport("KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool CheckIfRemappingsAreValid();
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.ExtendsContentIntoTitleBar = true;
|
||||
this.SetTitleBar(titleBar);
|
||||
|
||||
this.Activated += MainWindow_Activated;
|
||||
this.Closed += MainWindow_Closed;
|
||||
|
||||
// Set the default page
|
||||
RootView.SelectedItem = RootView.MenuItems[0];
|
||||
}
|
||||
|
||||
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
|
||||
private void MyButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
// Release the keyboard hook when the window is deactivated
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
}
|
||||
|
||||
private void MainWindow_Closed(object sender, WindowEventArgs args)
|
||||
{
|
||||
KeyboardHookHelper.Instance.Dispose();
|
||||
this.Activated -= MainWindow_Activated;
|
||||
this.Closed -= MainWindow_Closed;
|
||||
}
|
||||
|
||||
private void RootView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
{
|
||||
// Cleanup the keyboard hook when the selected page changes
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (args.SelectedItem is NavigationViewItem selectedItem)
|
||||
{
|
||||
switch ((string)selectedItem.Tag)
|
||||
{
|
||||
case "Remappings": NavigationFrame.Navigate(typeof(Pages.Remappings)); break;
|
||||
case "Programs": NavigationFrame.Navigate(typeof(Pages.Programs)); break;
|
||||
case "Text": NavigationFrame.Navigate(typeof(Pages.Text)); break;
|
||||
case "URLs": NavigationFrame.Navigate(typeof(Pages.URLs)); break;
|
||||
}
|
||||
}
|
||||
// Call the C++ function to check if the current remappings are valid
|
||||
myButton.Content = CheckIfRemappingsAreValid() ? "Valid" : "Invalid";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Pages.ExistingUI"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- WinUI3 implementation of the existing Keyboard Manager UI -->
|
||||
<Grid Padding="16">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="10"
|
||||
FontSize="24"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
Text="Remap keys" />
|
||||
|
||||
<TextBlock
|
||||
Margin="10"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Select the key you want to change and then configure the key, shortcut or text you want it to send."
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Margin="10"
|
||||
FontSize="16"
|
||||
FontStyle="Italic"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Example of a remapping: Select A and send "Ctrl+C", "A" would be your "Select" and "Ctrl+C" would be your "To send" command."
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<Grid Margin="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="240" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="240" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Margin="0,12,0,0"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Text="Selected:" />
|
||||
<Grid Grid.Row="1" Margin="0,8,0,0">
|
||||
<StackPanel>
|
||||
<ToggleButton Click="Button_Click" Content="Select" />
|
||||
<ComboBox
|
||||
x:Name="keyComboBox"
|
||||
Width="250"
|
||||
PlaceholderText="select keys please" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="24,0,24,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Text="" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="0,12,0,0"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Text="To Send:" />
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="0,8,0,0">
|
||||
<StackPanel>
|
||||
<ToggleButton Click="Button_Click" Content="Click to select" />
|
||||
<ComboBox
|
||||
x:Name="newKeyComboBox"
|
||||
Width="250"
|
||||
PlaceholderText="select keys please" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Button
|
||||
Margin="10"
|
||||
Content="+ Add Key Remapping"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,102 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
// WinUI3 implementation of the Existing Keyboard Manager UI
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
public sealed partial class ExistingUI : UserControl
|
||||
{
|
||||
public class KeyboardKey
|
||||
{
|
||||
public int KeyCode { get; set; }
|
||||
|
||||
public string KeyName { get; set; } = string.Empty;
|
||||
|
||||
public override string ToString() => KeyName;
|
||||
}
|
||||
|
||||
// Struct to hold key code and name pairs
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct KeyNamePair
|
||||
{
|
||||
public int KeyCode;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||
public string KeyName;
|
||||
}
|
||||
|
||||
[DllImport("PowerToys.KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern int GetKeyboardKeysList(bool isShortcut, [Out] KeyNamePair[] keyList, int maxCount);
|
||||
|
||||
[DllImport("PowerToys.KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool CheckIfRemappingsAreValid();
|
||||
|
||||
public List<KeyboardKey> KeysList { get; private set; } = new List<KeyboardKey>();
|
||||
|
||||
public ExistingUI()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
LoadKeyboardKeys();
|
||||
|
||||
keyComboBox.ItemsSource = KeysList;
|
||||
keyComboBox.DisplayMemberPath = "KeyName";
|
||||
|
||||
newKeyComboBox.ItemsSource = KeysList;
|
||||
newKeyComboBox.DisplayMemberPath = "KeyCode";
|
||||
}
|
||||
|
||||
private void LoadKeyboardKeys()
|
||||
{
|
||||
const int MaxKeys = 300;
|
||||
KeyNamePair[] keyNamePairs = new KeyNamePair[MaxKeys];
|
||||
|
||||
int count = GetKeyboardKeysList(false, keyNamePairs, MaxKeys);
|
||||
|
||||
KeysList = new List<KeyboardKey>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
KeysList.Add(new KeyboardKey
|
||||
{
|
||||
KeyCode = keyNamePairs[i].KeyCode,
|
||||
KeyName = keyNamePairs[i].KeyName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.AddedItems.Count > 0 && e.AddedItems[0] is KeyboardKey key)
|
||||
{
|
||||
Console.WriteLine($"selected key: {key.KeyName} (code: {key.KeyCode})");
|
||||
}
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var button = sender as Button;
|
||||
if (button == null)
|
||||
{
|
||||
// button.Background = (SolidColorBrush)Application.Current.Resources["SystemControlBackgroundAccentBrush"];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.Programs"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:styles="using:KeyboardManagerEditorUI.Styles"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Program shortcuts allow you to open specific applications when you use the configured shortcut"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewShortcutBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewShortcutBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock VerticalAlignment="Center" Text="New" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Shortcut" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Program" />
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind Shortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:URLShortcut">
|
||||
<Grid Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="232" />
|
||||
<ColumnDefinition Width="238" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<styles:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
VisualType="SmallOutline" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<TextBlock VerticalAlignment="Center" Text="WindowsTerminal.exe" />
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock>
|
||||
<Run FontWeight="SemiBold" Text="Arguments:" />
|
||||
<Run Text="-config json" />
|
||||
<LineBreak />
|
||||
<Run FontWeight="SemiBold" Text="Starting directory:" />
|
||||
<Run Text="C:\Users\Dev\PowerToys\bin" />
|
||||
<LineBreak />
|
||||
<Run FontWeight="SemiBold" Text="Launch state:" />
|
||||
<Run Text="Minimized" />
|
||||
</TextBlock>
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
</StackPanel>
|
||||
<ToggleSwitch
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0,0,-112,0"
|
||||
HorizontalAlignment="Right"
|
||||
IsOn="True"
|
||||
OffContent=""
|
||||
OnContent="" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
Title="New program shortcut"
|
||||
Width="480"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
PrimaryButtonText="Save"
|
||||
SecondaryButtonText="Cancel">
|
||||
<StackPanel Width="480" Orientation="Vertical">
|
||||
<TextBlock Margin="0,12,0,8" Text="Shortcut" />
|
||||
<ToggleButton
|
||||
x:Name="OriginalToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<styles:KeyVisual Content="Win (Left)" VisualType="SmallOutline" />
|
||||
<styles:KeyVisual Content="T" VisualType="SmallOutline" />
|
||||
</StackPanel>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<Grid
|
||||
Margin="0,24,0,0"
|
||||
ColumnSpacing="4"
|
||||
RowSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBox Header="Application" Text="WindowsTerminal.exe" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Height="32"
|
||||
VerticalAlignment="Bottom">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
</Button>
|
||||
<TextBox
|
||||
Grid.Row="1"
|
||||
Header="Arguments"
|
||||
Text="-config" />
|
||||
<TextBox
|
||||
Grid.Row="2"
|
||||
Header="Start in directory"
|
||||
Text="C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.21.10351.0_x64__8wekyb3d8bbwe" />
|
||||
<Button
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Height="32"
|
||||
VerticalAlignment="Bottom">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
</Button>
|
||||
<ComboBox
|
||||
Grid.Row="3"
|
||||
Header="Launch state"
|
||||
SelectedIndex="0">
|
||||
<ComboBoxItem Content="Show window" />
|
||||
<ComboBoxItem Content="Minimized" />
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,46 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
public sealed partial class Programs : Page
|
||||
{
|
||||
public ObservableCollection<URLShortcut> Shortcuts { get; set; }
|
||||
|
||||
public Programs()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Shortcuts = new ObservableCollection<URLShortcut>();
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Win (Left)", "T", }, URL = "https://www.bing.com" });
|
||||
}
|
||||
|
||||
private async void NewShortcutBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.Remappings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:styles="using:KeyboardManagerEditorUI.Styles"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Remappings allow you to reconfigure a single key or shortcut to a new key or shortcut"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewRemappingBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewRemappingBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock VerticalAlignment="Center" Text="New" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Original key(s)" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="New key(s)" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="2"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Applicable apps" />
|
||||
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind RemappingList}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:Remapping">
|
||||
<Grid Height="Auto" MinHeight="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="236" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind OriginalKeys}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel
|
||||
MaxWidth="230"
|
||||
HorizontalSpacing="4"
|
||||
Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<styles:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
VisualType="SmallOutline" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<ItemsControl
|
||||
Grid.Column="1"
|
||||
Margin="0,6,0,6"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind RemappedKeys}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel
|
||||
MaxWidth="230"
|
||||
HorizontalSpacing="4"
|
||||
Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<styles:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
VisualType="Small" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="-4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind AppName}" />
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
Margin="0,0,4,0"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Delete remapping"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="DeleteButton_Click"
|
||||
ToolTipService.ToolTip="Delete remapping">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
Title="Remappings"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
PrimaryButtonText="Save"
|
||||
SecondaryButtonText="Cancel">
|
||||
<Grid>
|
||||
<styles:InputControl x:Name="RemappingControl" />
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
<TeachingTip
|
||||
x:Name="OrphanedKeysTeachingTip"
|
||||
Title="Orphaned Keys Warning"
|
||||
ActionButtonClick="OrphanedKeysTeachingTip_ActionButtonClick"
|
||||
ActionButtonContent="Continue anyway"
|
||||
ActionButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
CloseButtonClick="OrphanedKeysTeachingTip_CloseButtonClick"
|
||||
CloseButtonContent="Cancel"
|
||||
IsLightDismissEnabled="False"
|
||||
PreferredPlacement="Center">
|
||||
<TeachingTip.IconSource>
|
||||
<SymbolIconSource Symbol="Important" />
|
||||
</TeachingTip.IconSource>
|
||||
</TeachingTip>
|
||||
<TeachingTip
|
||||
x:Name="ValidationTeachingTip"
|
||||
CloseButtonClick="ValidationTeachingTip_CloseButtonClick"
|
||||
CloseButtonContent="OK"
|
||||
IsLightDismissEnabled="True"
|
||||
PreferredPlacement="Center">
|
||||
<TeachingTip.IconSource>
|
||||
<SymbolIconSource Symbol="Important" />
|
||||
</TeachingTip.IconSource>
|
||||
</TeachingTip>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,370 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using static KeyboardManagerEditorUI.Helpers.ValidationHelper;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// The Remapping page that allow users to configure a single key or shortcut to a new key or shortcut
|
||||
/// </summary>
|
||||
public sealed partial class Remappings : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
|
||||
// Flag to indicate if the user is editing an existing remapping
|
||||
private bool _isEditMode;
|
||||
private Remapping? _editingRemapping;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
// The list of single key mappings
|
||||
public ObservableCollection<KeyMapping> SingleKeyMappings { get; } = new ObservableCollection<KeyMapping>();
|
||||
|
||||
// The list of shortcut key mappings
|
||||
public ObservableCollection<ShortcutKeyMapping> ShortcutKeyMappings { get; } = new ObservableCollection<ShortcutKeyMapping>();
|
||||
|
||||
// The full list of remappings
|
||||
public ObservableCollection<Remapping> RemappingList { get; set; }
|
||||
|
||||
public Remappings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
RemappingList = new ObservableCollection<Remapping>();
|
||||
_mappingService = new KeyboardMappingService();
|
||||
|
||||
// Load all existing remappings
|
||||
LoadMappings();
|
||||
|
||||
this.Unloaded += Remappings_Unloaded;
|
||||
}
|
||||
|
||||
private void Remappings_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Make sure we unregister the handler when the page is unloaded
|
||||
UnregisterWindowActivationHandler();
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private void RegisterWindowActivationHandler()
|
||||
{
|
||||
// Get the current window that contains this page
|
||||
var app = Application.Current as App;
|
||||
if (app?.GetWindow() is Window window)
|
||||
{
|
||||
// Register for window activation events
|
||||
window.Activated += Dialog_WindowActivated;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterWindowActivationHandler()
|
||||
{
|
||||
var app = Application.Current as App;
|
||||
if (app?.GetWindow() is Window window)
|
||||
{
|
||||
// Unregister to prevent memory leaks
|
||||
window.Activated -= Dialog_WindowActivated;
|
||||
}
|
||||
}
|
||||
|
||||
private void Dialog_WindowActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// When window is deactivated (user switched to another app)
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
// Make sure to cleanup the keyboard hook when the window loses focus
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
RemappingControl.ResetToggleButtons();
|
||||
RemappingControl.UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
}
|
||||
|
||||
private async void NewRemappingBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isEditMode = false;
|
||||
_editingRemapping = null;
|
||||
|
||||
RemappingControl.SetOriginalKeys(new List<string>());
|
||||
RemappingControl.SetRemappedKeys(new List<string>());
|
||||
RemappingControl.SetApp(false, string.Empty);
|
||||
RemappingControl.SetUpToggleButtonInitialStatus();
|
||||
|
||||
RegisterWindowActivationHandler();
|
||||
|
||||
// Show the dialog to add a new remapping
|
||||
KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick;
|
||||
await KeyDialog.ShowAsync();
|
||||
KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick;
|
||||
|
||||
UnregisterWindowActivationHandler();
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is Remapping selectedRemapping && selectedRemapping.IsEnabled)
|
||||
{
|
||||
// Set to edit mode
|
||||
_isEditMode = true;
|
||||
_editingRemapping = selectedRemapping;
|
||||
|
||||
RemappingControl.SetOriginalKeys(selectedRemapping.OriginalKeys);
|
||||
RemappingControl.SetRemappedKeys(selectedRemapping.RemappedKeys);
|
||||
RemappingControl.SetApp(!selectedRemapping.IsAllApps, selectedRemapping.AppName);
|
||||
RemappingControl.SetUpToggleButtonInitialStatus();
|
||||
|
||||
RegisterWindowActivationHandler();
|
||||
|
||||
KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick;
|
||||
await KeyDialog.ShowAsync();
|
||||
KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick;
|
||||
|
||||
UnregisterWindowActivationHandler();
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
// Reset the edit status
|
||||
_isEditMode = false;
|
||||
_editingRemapping = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
List<string> originalKeys = RemappingControl.GetOriginalKeys();
|
||||
List<string> remappedKeys = RemappingControl.GetRemappedKeys();
|
||||
bool isAppSpecific = RemappingControl.GetIsAppSpecific();
|
||||
string appName = RemappingControl.GetAppName();
|
||||
|
||||
// Make sure _mappingService is not null before validating and saving
|
||||
if (_mappingService == null)
|
||||
{
|
||||
Logger.LogError("Mapping service is null, cannot validate mapping");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the remapping
|
||||
ValidationErrorType errorType = ValidationHelper.ValidateKeyMapping(
|
||||
originalKeys, remappedKeys, isAppSpecific, appName, _mappingService, _isEditMode, _editingRemapping);
|
||||
|
||||
if (errorType != ValidationErrorType.NoError)
|
||||
{
|
||||
ShowValidationError(errorType, args);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for orphaned keys
|
||||
if (originalKeys.Count == 1 && _mappingService != null)
|
||||
{
|
||||
int originalKeyCode = _mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
|
||||
if (IsKeyOrphaned(originalKeyCode, _mappingService))
|
||||
{
|
||||
string keyName = _mappingService.GetKeyDisplayName(originalKeyCode);
|
||||
|
||||
OrphanedKeysTeachingTip.Target = RemappingControl;
|
||||
OrphanedKeysTeachingTip.Subtitle = $"The key {keyName} will become orphaned (inaccessible) after remapping. Please confirm if you want to proceed.";
|
||||
OrphanedKeysTeachingTip.Tag = args;
|
||||
OrphanedKeysTeachingTip.IsOpen = true;
|
||||
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If in edit mode, delete the existing remapping before saving the new one
|
||||
if (_isEditMode && _editingRemapping != null)
|
||||
{
|
||||
if (!RemappingHelper.DeleteRemapping(_mappingService!, _editingRemapping))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If no errors, proceed to save the remapping
|
||||
bool saved = RemappingHelper.SaveMapping(_mappingService!, originalKeys, remappedKeys, isAppSpecific, appName);
|
||||
if (saved)
|
||||
{
|
||||
// Display the remapping in the list after saving
|
||||
LoadMappings();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.DataContext is Remapping remapping)
|
||||
{
|
||||
if (RemappingHelper.DeleteRemapping(_mappingService!, remapping))
|
||||
{
|
||||
LoadMappings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidationTeachingTip_CloseButtonClick(TeachingTip sender, object args)
|
||||
{
|
||||
sender.IsOpen = false;
|
||||
}
|
||||
|
||||
private void OrphanedKeysTeachingTip_ActionButtonClick(TeachingTip sender, object args)
|
||||
{
|
||||
// User pressed continue anyway button
|
||||
sender.IsOpen = false;
|
||||
|
||||
if (_isEditMode && _editingRemapping != null)
|
||||
{
|
||||
if (!RemappingHelper.DeleteRemapping(_mappingService!, _editingRemapping))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool saved = RemappingHelper.SaveMapping(
|
||||
_mappingService!, RemappingControl.GetOriginalKeys(), RemappingControl.GetRemappedKeys(), RemappingControl.GetIsAppSpecific(), RemappingControl.GetAppName());
|
||||
if (saved)
|
||||
{
|
||||
KeyDialog.Hide();
|
||||
LoadMappings();
|
||||
}
|
||||
}
|
||||
|
||||
private void OrphanedKeysTeachingTip_CloseButtonClick(TeachingTip sender, object args)
|
||||
{
|
||||
// Just close the teaching tip if the user canceled
|
||||
sender.IsOpen = false;
|
||||
}
|
||||
|
||||
private void LoadMappings()
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SingleKeyMappings.Clear();
|
||||
ShortcutKeyMappings.Clear();
|
||||
RemappingList.Clear();
|
||||
|
||||
// Load all single key mappings
|
||||
foreach (var mapping in _mappingService.GetSingleKeyMappings())
|
||||
{
|
||||
SingleKeyMappings.Add(mapping);
|
||||
|
||||
string[] targetKeyCodes = mapping.TargetKey.Split(';');
|
||||
var targetKeyNames = new List<string>();
|
||||
|
||||
foreach (var keyCode in targetKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
targetKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
RemappingList.Add(new Remapping
|
||||
{
|
||||
OriginalKeys = new List<string> { _mappingService.GetKeyDisplayName(mapping.OriginalKey) },
|
||||
RemappedKeys = targetKeyNames,
|
||||
IsAllApps = true,
|
||||
});
|
||||
}
|
||||
|
||||
// Load all shortcut key mappings
|
||||
foreach (var mapping in _mappingService.GetShortcutMappingsByType(ShortcutOperationType.RemapShortcut))
|
||||
{
|
||||
ShortcutKeyMappings.Add(mapping);
|
||||
|
||||
string[] originalKeyCodes = mapping.OriginalKeys.Split(';');
|
||||
string[] targetKeyCodes = mapping.TargetKeys.Split(';');
|
||||
|
||||
var originalKeyNames = new List<string>();
|
||||
var targetKeyNames = new List<string>();
|
||||
|
||||
foreach (var keyCode in originalKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
originalKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var keyCode in targetKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
targetKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
RemappingList.Add(new Remapping
|
||||
{
|
||||
OriginalKeys = originalKeyNames,
|
||||
RemappedKeys = targetKeyNames,
|
||||
IsAllApps = string.IsNullOrEmpty(mapping.TargetApp),
|
||||
AppName = string.IsNullOrEmpty(mapping.TargetApp) ? "All Apps" : mapping.TargetApp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShowValidationError(ValidationErrorType errorType, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
var (title, message) = ValidationMessages[errorType];
|
||||
|
||||
ValidationTeachingTip.Title = title;
|
||||
ValidationTeachingTip.Subtitle = message;
|
||||
ValidationTeachingTip.Target = RemappingControl;
|
||||
ValidationTeachingTip.Tag = args;
|
||||
ValidationTeachingTip.IsOpen = true;
|
||||
args.Cancel = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources
|
||||
_mappingService?.Dispose();
|
||||
_mappingService = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.Text"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:styles="using:KeyboardManagerEditorUI.Styles"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Text shortcuts allow you to insert text in any inputfield when you use the configured text"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewShortcutBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewShortcutBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock VerticalAlignment="Center" Text="New" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Shortcut" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Text" />
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind Shortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:URLShortcut">
|
||||
<Grid Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="232" />
|
||||
<ColumnDefinition Width="238" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<styles:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
VisualType="SmallOutline" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind URL}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<ToggleSwitch
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0,0,-112,0"
|
||||
HorizontalAlignment="Right"
|
||||
IsOn="True"
|
||||
OffContent=""
|
||||
OnContent="" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
Title="New text shortcut"
|
||||
Width="480"
|
||||
Height="360"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
PrimaryButtonText="Save"
|
||||
SecondaryButtonText="Cancel">
|
||||
<StackPanel
|
||||
Width="360"
|
||||
Height="360"
|
||||
Orientation="Vertical">
|
||||
<TextBlock Margin="0,12,0,8" Text="Shortcut" />
|
||||
<ToggleButton
|
||||
x:Name="OriginalToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<styles:KeyVisual Content="Shift" VisualType="SmallOutline" />
|
||||
<styles:KeyVisual Content="Win" VisualType="SmallOutline" />
|
||||
<styles:KeyVisual Content="M" VisualType="SmallOutline" />
|
||||
</StackPanel>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<TextBox
|
||||
Height="200"
|
||||
Margin="0,24,0,0"
|
||||
Header="Text to insert"
|
||||
Text="https://www.microsoft.com"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,50 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Text : Page
|
||||
{
|
||||
public ObservableCollection<URLShortcut> Shortcuts { get; set; }
|
||||
|
||||
public Text()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Shortcuts = new ObservableCollection<URLShortcut>();
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Alt (Left)", "H" }, URL = "Hello" });
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Win (Left)", "P", }, URL = "PowerToys" });
|
||||
}
|
||||
|
||||
private async void NewShortcutBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.URLs"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:styles="using:KeyboardManagerEditorUI.Styles"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="URL shortcuts allow you to open a URL when you use the configured shortcut"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewShortcutBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewShortcutBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock VerticalAlignment="Center" Text="New" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Shortcut" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="URL" />
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind Shortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:URLShortcut">
|
||||
<Grid Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="232" />
|
||||
<ColumnDefinition Width="238" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<styles:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
VisualType="SmallOutline" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<HyperlinkButton
|
||||
Grid.Column="1"
|
||||
Margin="-12,0,0,0"
|
||||
Content="{x:Bind URL}" />
|
||||
<ToggleSwitch
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0,0,-112,0"
|
||||
HorizontalAlignment="Right"
|
||||
IsOn="True"
|
||||
OffContent=""
|
||||
OnContent="" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
Title="New URL shortcut"
|
||||
Width="480"
|
||||
Height="360"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
PrimaryButtonText="Save"
|
||||
SecondaryButtonText="Cancel">
|
||||
<StackPanel
|
||||
Width="320"
|
||||
Height="264"
|
||||
Orientation="Vertical">
|
||||
<TextBlock Margin="0,12,0,8" Text="Shortcut" />
|
||||
<ToggleButton
|
||||
x:Name="OriginalToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<styles:KeyVisual Content="Win (Left)" VisualType="SmallOutline" />
|
||||
<styles:KeyVisual Content="B" VisualType="SmallOutline" />
|
||||
</StackPanel>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
<TextBox
|
||||
Margin="0,24,0,0"
|
||||
Header="URL to open"
|
||||
Text="https://www.bing.com" />
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,117 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class URLs : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ObservableCollection<URLShortcut> Shortcuts { get; set; }
|
||||
|
||||
[DllImport("PowerToys.KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
|
||||
private static extern void GetKeyDisplayName(int keyCode, [Out] StringBuilder keyName, int maxLength);
|
||||
|
||||
public URLs()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Shortcuts = new ObservableCollection<URLShortcut>();
|
||||
|
||||
_mappingService = new KeyboardMappingService();
|
||||
|
||||
foreach (var mapping in _mappingService.GetShortcutMappingsByType(ShortcutOperationType.OpenUri))
|
||||
{
|
||||
string[] originalKeyCodes = mapping.OriginalKeys.Split(';');
|
||||
var originalKeyNames = new List<string>();
|
||||
foreach (var keyCode in originalKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
originalKeyNames.Add(GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
var shortcut = new URLShortcut
|
||||
{
|
||||
Shortcut = originalKeyNames,
|
||||
URL = mapping.UriToOpen,
|
||||
};
|
||||
Shortcuts.Add(shortcut);
|
||||
}
|
||||
|
||||
/*
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Shift", "Win", "M" }, URL = "https://www.microsoft.com" });
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Win", "P", }, URL = "https://www.bing.com" });
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Shift", "Win", "M" }, URL = "https://www.windows.com" });
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Win", "U", }, URL = "https://www.bing.com" });
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Ctrl", "P" }, URL = "https://www.surface.com" });
|
||||
Shortcuts.Add(new URLShortcut() { Shortcut = new List<string>() { "Alt", "Ctrl", "Shift" }, URL = "https://www.bing.com" });
|
||||
*/
|
||||
}
|
||||
|
||||
public static string GetKeyDisplayName(int keyCode)
|
||||
{
|
||||
var keyName = new StringBuilder(64);
|
||||
GetKeyDisplayName(keyCode, keyName, keyName.Capacity);
|
||||
return keyName.ToString();
|
||||
}
|
||||
|
||||
private async void NewShortcutBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources
|
||||
_mappingService?.Dispose();
|
||||
_mappingService = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Styles">
|
||||
|
||||
<x:Double x:Key="ContentDialogMaxWidth">960</x:Double>
|
||||
|
||||
<Style x:Key="CustomShortcutToggleButtonStyle" TargetType="ToggleButton">
|
||||
<Setter Property="Background" Value="{ThemeResource ToggleButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ToggleButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ToggleButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1,1,1,1" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Checked">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Transparent" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundChecked}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundChecked}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderThickness">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1,1,1,4" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CheckedPointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundCheckedPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundCheckedPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderThickness">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1,1,1,4" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CheckedPressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundCheckedPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundCheckedPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderThickness">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="1,1,1,4" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="CheckedDisabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundCheckedDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundCheckedDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushCheckedDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Indeterminate">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundIndeterminate}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundIndeterminate}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushIndeterminate}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="IndeterminatePointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundIndeterminatePointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushIndeterminatePointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundIndeterminatePointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="IndeterminatePressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundIndeterminatePressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushIndeterminatePressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundIndeterminatePressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="IndeterminateDisabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBackgroundIndeterminateDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonForegroundIndeterminateDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ToggleButtonBorderBrushIndeterminateDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Styles.InputControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Styles"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="240" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="240" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Margin="0,12,0,0" Text="Original key(s)" />
|
||||
<Grid Grid.Column="2">
|
||||
<TextBlock Margin="0,12,0,0" Text="New key(s)" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" Margin="0,8,0,0">
|
||||
<ToggleButton
|
||||
x:Name="OriginalToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Checked="OriginalToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="OriginalKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="4" Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" VisualType="SmallOutline" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="24,0,24,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Text="" />
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="0,8,0,0">
|
||||
<ToggleButton
|
||||
x:Name="RemappedToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Checked="RemappedToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="RemappedKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="4" Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" VisualType="Small" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<CheckBox
|
||||
x:Name="AllAppsCheckBox"
|
||||
Margin="0,24,0,12"
|
||||
Content="Only apply this remapping to a specific application" />
|
||||
<TextBox
|
||||
x:Name="AppNameTextBox"
|
||||
Header="Application name"
|
||||
IsEnabled="{Binding ElementName=AllAppsCheckBox, Path=IsChecked}"
|
||||
PlaceholderText="e.g.: outlook.exe" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,434 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Windows.System;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Styles
|
||||
{
|
||||
public sealed partial class InputControl : UserControl, IDisposable, IKeyboardHookTarget
|
||||
{
|
||||
// Collection to store original and remapped keys
|
||||
private ObservableCollection<string> _originalKeys = new ObservableCollection<string>();
|
||||
private ObservableCollection<string> _remappedKeys = new ObservableCollection<string>();
|
||||
|
||||
// TeachingTip for notifications
|
||||
private TeachingTip? currentNotification;
|
||||
private DispatcherTimer? notificationTimer;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public static readonly DependencyProperty InputModeProperty =
|
||||
DependencyProperty.Register(
|
||||
"InputMode",
|
||||
typeof(KeyInputMode),
|
||||
typeof(InputControl),
|
||||
new PropertyMetadata(KeyInputMode.OriginalKeys));
|
||||
|
||||
public KeyInputMode InputMode
|
||||
{
|
||||
get { return (KeyInputMode)GetValue(InputModeProperty); }
|
||||
set { SetValue(InputModeProperty, value); }
|
||||
}
|
||||
|
||||
public InputControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
this.OriginalKeys.ItemsSource = _originalKeys;
|
||||
this.RemappedKeys.ItemsSource = _remappedKeys;
|
||||
|
||||
this.Unloaded += InputControl_Unloaded;
|
||||
|
||||
// Set the default focus state
|
||||
OriginalToggleBtn.IsChecked = true;
|
||||
|
||||
// Ensure AllAppsCheckBox is in the correct state initially
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
private void InputControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Reset the control when it is unloaded
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void OnKeyDown(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
if (InputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
_remappedKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_remappedKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_originalKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
public void ClearKeys()
|
||||
{
|
||||
if (InputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
_remappedKeys.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalKeys.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInputLimitReached()
|
||||
{
|
||||
ShowNotificationTip("Shortcuts can only have up to 4 modifier keys");
|
||||
}
|
||||
|
||||
public void ShowNotificationTip(string message)
|
||||
{
|
||||
// If there's already an active notification, close and remove it first
|
||||
CloseExistingNotification();
|
||||
|
||||
// Create a new notification
|
||||
currentNotification = new TeachingTip
|
||||
{
|
||||
Title = "Input Limit Reached",
|
||||
Subtitle = message,
|
||||
IsLightDismissEnabled = true,
|
||||
PreferredPlacement = TeachingTipPlacementMode.Top,
|
||||
XamlRoot = this.XamlRoot,
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Important },
|
||||
};
|
||||
|
||||
// Target the toggle button that triggered the notification
|
||||
currentNotification.Target = InputMode == KeyInputMode.RemappedKeys ? RemappedToggleBtn : OriginalToggleBtn;
|
||||
|
||||
// Add the notification to the root panel and show it
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Add(currentNotification);
|
||||
currentNotification.IsOpen = true;
|
||||
|
||||
// Create a timer to auto-dismiss the notification
|
||||
notificationTimer = new DispatcherTimer();
|
||||
notificationTimer.Interval = TimeSpan.FromMilliseconds(EditorConstants.DefaultNotificationTimeout);
|
||||
notificationTimer.Tick += (s, e) =>
|
||||
{
|
||||
CloseExistingNotification();
|
||||
notificationTimer = null;
|
||||
};
|
||||
notificationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to close existing notifications
|
||||
private void CloseExistingNotification()
|
||||
{
|
||||
// Stop any running timer
|
||||
if (notificationTimer != null)
|
||||
{
|
||||
notificationTimer.Stop();
|
||||
notificationTimer = null;
|
||||
}
|
||||
|
||||
// Close and remove any existing notification
|
||||
if (currentNotification != null && currentNotification.IsOpen)
|
||||
{
|
||||
currentNotification.IsOpen = false;
|
||||
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Remove(currentNotification);
|
||||
}
|
||||
|
||||
currentNotification = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void CleanupKeyboardHook()
|
||||
{
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
public bool IsModifierKey(VirtualKey key)
|
||||
{
|
||||
return key == VirtualKey.Control
|
||||
|| key == VirtualKey.LeftControl
|
||||
|| key == VirtualKey.RightControl
|
||||
|| key == VirtualKey.Menu
|
||||
|| key == VirtualKey.LeftMenu
|
||||
|| key == VirtualKey.RightMenu
|
||||
|| key == VirtualKey.Shift
|
||||
|| key == VirtualKey.LeftShift
|
||||
|| key == VirtualKey.RightShift
|
||||
|| key == VirtualKey.LeftWindows
|
||||
|| key == VirtualKey.RightWindows;
|
||||
}
|
||||
|
||||
public void SetRemappedKeys(List<string> keys)
|
||||
{
|
||||
_remappedKeys.Clear();
|
||||
if (keys != null)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_remappedKeys.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
public void SetOriginalKeys(List<string> keys)
|
||||
{
|
||||
_originalKeys.Clear();
|
||||
if (keys != null)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_originalKeys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetOriginalKeys()
|
||||
{
|
||||
return _originalKeys.ToList();
|
||||
}
|
||||
|
||||
public List<string> GetRemappedKeys()
|
||||
{
|
||||
return _remappedKeys.ToList();
|
||||
}
|
||||
|
||||
public bool GetIsAppSpecific()
|
||||
{
|
||||
return AllAppsCheckBox.IsChecked ?? false;
|
||||
}
|
||||
|
||||
public string GetAppName()
|
||||
{
|
||||
return AppNameTextBox.Text ?? string.Empty;
|
||||
}
|
||||
|
||||
private void RemappedToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Only set NewMode to true if RemappedToggleBtn is checked
|
||||
if (RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
InputMode = KeyInputMode.RemappedKeys;
|
||||
|
||||
// Make sure OriginalToggleBtn is unchecked
|
||||
if (OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
}
|
||||
|
||||
private void OriginalToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Only set NewMode to false if OriginalToggleBtn is checked
|
||||
if (OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
InputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
// Make sure RemappedToggleBtn is unchecked
|
||||
if (RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetApp(bool isSpecificApp, string appName)
|
||||
{
|
||||
if (isSpecificApp)
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = true;
|
||||
AppNameTextBox.Text = appName;
|
||||
AppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = false;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void AllAppsCheckBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (RemappedToggleBtn != null && RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null && OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
CleanupKeyboardHook();
|
||||
|
||||
AppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void AllAppsCheckBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void AppNameTextBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Reset the focus state when the AppNameTextBox is focused
|
||||
if (RemappedToggleBtn != null && RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null && OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AllAppsCheckBox.Checked += AllAppsCheckBox_Checked;
|
||||
AllAppsCheckBox.Unchecked += AllAppsCheckBox_Unchecked;
|
||||
|
||||
AppNameTextBox.GotFocus += AppNameTextBox_GotFocus;
|
||||
}
|
||||
|
||||
public void ResetToggleButtons()
|
||||
{
|
||||
// Reset toggle button status without clearing the key displays
|
||||
if (RemappedToggleBtn != null)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUpToggleButtonInitialStatus()
|
||||
{
|
||||
// Ensure OriginalToggleBtn is checked
|
||||
if (OriginalToggleBtn != null && OriginalToggleBtn.IsChecked != true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = true;
|
||||
}
|
||||
|
||||
// Make sure RemappedToggleBtn is not checked
|
||||
if (RemappedToggleBtn != null && RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAllAppsCheckBoxState()
|
||||
{
|
||||
// Only enable app-specific remapping for shortcuts (multiple keys)
|
||||
bool isShortcut = _originalKeys.Count > 1;
|
||||
|
||||
AllAppsCheckBox.IsEnabled = isShortcut;
|
||||
|
||||
// If it's not a shortcut, ensure the checkbox is unchecked and app textbox is hidden
|
||||
if (!isShortcut)
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = false;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
CloseExistingNotification();
|
||||
Reset();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Reset displayed keys
|
||||
_originalKeys.Clear();
|
||||
_remappedKeys.Clear();
|
||||
|
||||
// Reset toggle button status
|
||||
if (RemappedToggleBtn != null)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
InputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
// Reset app name text box
|
||||
if (AppNameTextBox != null)
|
||||
{
|
||||
AppNameTextBox.Text = string.Empty;
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
|
||||
// Close any existing notifications
|
||||
CloseExistingNotification();
|
||||
|
||||
// Reset the focus status
|
||||
if (this.FocusState != FocusState.Unfocused)
|
||||
{
|
||||
this.IsTabStop = false;
|
||||
this.IsTabStop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Markup;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Windows.System;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Styles
|
||||
{
|
||||
[TemplatePart(Name = KeyPresenter, Type = typeof(ContentPresenter))]
|
||||
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
|
||||
[TemplateVisualState(Name = "Default", GroupName = "StateStates")]
|
||||
[TemplateVisualState(Name = "Error", GroupName = "StateStates")]
|
||||
public sealed partial class KeyVisual : Control
|
||||
{
|
||||
private const string KeyPresenter = "KeyPresenter";
|
||||
private KeyVisual? _keyVisual;
|
||||
private ContentPresenter? _keyPresenter;
|
||||
|
||||
public object Content
|
||||
{
|
||||
get => (object)GetValue(ContentProperty);
|
||||
set => SetValue(ContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(KeyVisual), new PropertyMetadata(default(string), OnContentChanged));
|
||||
|
||||
public VisualType VisualType
|
||||
{
|
||||
get => (VisualType)GetValue(VisualTypeProperty);
|
||||
set => SetValue(VisualTypeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty VisualTypeProperty = DependencyProperty.Register("VisualType", typeof(VisualType), typeof(KeyVisual), new PropertyMetadata(default(VisualType), OnSizeChanged));
|
||||
|
||||
public bool IsError
|
||||
{
|
||||
get => (bool)GetValue(IsErrorProperty);
|
||||
set => SetValue(IsErrorProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register("IsError", typeof(bool), typeof(KeyVisual), new PropertyMetadata(false, OnIsErrorChanged));
|
||||
|
||||
public KeyVisual()
|
||||
{
|
||||
this.DefaultStyleKey = typeof(KeyVisual);
|
||||
this.Style = GetStyleSize("TextKeyVisualStyle");
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
IsEnabledChanged -= KeyVisual_IsEnabledChanged;
|
||||
_keyVisual = (KeyVisual)this;
|
||||
_keyPresenter = (ContentPresenter)_keyVisual.GetTemplateChild(KeyPresenter);
|
||||
Update();
|
||||
SetEnabledState();
|
||||
SetErrorState();
|
||||
IsEnabledChanged += KeyVisual_IsEnabledChanged;
|
||||
base.OnApplyTemplate();
|
||||
}
|
||||
|
||||
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((KeyVisual)d).Update();
|
||||
}
|
||||
|
||||
private static void OnSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((KeyVisual)d).Update();
|
||||
}
|
||||
|
||||
private static void OnIsErrorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
((KeyVisual)d).SetErrorState();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_keyVisual == null || _keyVisual._keyPresenter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_keyVisual.Content != null)
|
||||
{
|
||||
if (_keyVisual.Content.GetType() == typeof(string))
|
||||
{
|
||||
_keyVisual.Style = GetStyleSize("TextKeyVisualStyle");
|
||||
_keyVisual._keyPresenter.Content = _keyVisual.Content;
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyVisual.Style = GetStyleSize("IconKeyVisualStyle");
|
||||
|
||||
switch ((int)_keyVisual.Content)
|
||||
{
|
||||
/* We can enable other glyphs in the future
|
||||
case 13: // The Enter key or button.
|
||||
_keyVisual._keyPresenter.Content = "\uE751"; break;
|
||||
|
||||
case 8: // The Back key or button.
|
||||
_keyVisual._keyPresenter.Content = "\uE750"; break;
|
||||
|
||||
case 16: // The right Shift key or button.
|
||||
case 160: // The left Shift key or button.
|
||||
case 161: // The Shift key or button.
|
||||
_keyVisual._keyPresenter.Content = "\uE752"; break; */
|
||||
|
||||
case 38: _keyVisual._keyPresenter.Content = "\uE0E4"; break; // The Up Arrow key or button.
|
||||
case 40: _keyVisual._keyPresenter.Content = "\uE0E5"; break; // The Down Arrow key or button.
|
||||
case 37: _keyVisual._keyPresenter.Content = "\uE0E2"; break; // The Left Arrow key or button.
|
||||
case 39: _keyVisual._keyPresenter.Content = "\uE0E3"; break; // The Right Arrow key or button.
|
||||
|
||||
case 91: // The left Windows key
|
||||
case 92: // The right Windows key
|
||||
PathIcon winIcon = (PathIcon)XamlReader.Load(@"<PathIcon xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" Data=""M9,17V9h8v8ZM0,17V9H8v8ZM9,8V0h8V8ZM0,8V0H8V8Z"" />") as PathIcon;
|
||||
Viewbox winIconContainer = new Viewbox();
|
||||
winIconContainer.Child = winIcon;
|
||||
winIconContainer.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
winIconContainer.VerticalAlignment = VerticalAlignment.Center;
|
||||
|
||||
double iconDimensions = GetIconSize();
|
||||
winIconContainer.Height = iconDimensions;
|
||||
winIconContainer.Width = iconDimensions;
|
||||
_keyVisual._keyPresenter.Content = winIconContainer;
|
||||
break;
|
||||
default: _keyVisual._keyPresenter.Content = ((VirtualKey)_keyVisual.Content).ToString(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Style GetStyleSize(string styleName)
|
||||
{
|
||||
if (VisualType == VisualType.Small)
|
||||
{
|
||||
return (Style)App.Current.Resources["Small" + styleName];
|
||||
}
|
||||
else if (VisualType == VisualType.SmallOutline)
|
||||
{
|
||||
return (Style)App.Current.Resources["SmallOutline" + styleName];
|
||||
}
|
||||
else if (VisualType == VisualType.LargeOutline)
|
||||
{
|
||||
return (Style)App.Current.Resources["LargeOutline" + styleName];
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Style)App.Current.Resources["Default" + styleName];
|
||||
}
|
||||
}
|
||||
|
||||
public double GetIconSize()
|
||||
{
|
||||
if (VisualType == VisualType.Small || VisualType == VisualType.SmallOutline)
|
||||
{
|
||||
return (double)App.Current.Resources["SmallIconSize"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return (double)App.Current.Resources["DefaultIconSize"];
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyVisual_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
SetEnabledState();
|
||||
}
|
||||
|
||||
private void SetErrorState()
|
||||
{
|
||||
VisualStateManager.GoToState(this, IsError ? "Error" : "Default", true);
|
||||
}
|
||||
|
||||
private void SetEnabledState()
|
||||
{
|
||||
VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
public enum VisualType
|
||||
{
|
||||
Small,
|
||||
SmallOutline,
|
||||
Large,
|
||||
LargeOutline,
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Styles">
|
||||
|
||||
<x:Double x:Key="DefaultIconSize">16</x:Double>
|
||||
<x:Double x:Key="SmallIconSize">12</x:Double>
|
||||
<Style x:Key="DefaultTextKeyVisualStyle" TargetType="local:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="32" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Background" Value="{ThemeResource AccentButtonBackgroundPressed}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextOnAccentFillColorPrimaryBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="4,0,0,4" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:KeyVisual">
|
||||
<Grid>
|
||||
<Grid Height="{TemplateBinding MinHeight}">
|
||||
<Border
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="4" />
|
||||
<Rectangle
|
||||
x:Name="ContentHolder"
|
||||
Fill="{TemplateBinding Background}"
|
||||
RadiusX="4"
|
||||
RadiusY="4" />
|
||||
<ContentPresenter
|
||||
x:Name="KeyPresenter"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="Center"
|
||||
Content="{TemplateBinding Content}"
|
||||
FontSize="12"
|
||||
FontWeight="Normal"
|
||||
Foreground="{TemplateBinding Foreground}" />
|
||||
</Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentHolder.Fill" Value="{ThemeResource AccentButtonBackgroundDisabled}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource AccentButtonForegroundDisabled}" />
|
||||
<Setter Target="ContentHolder.Stroke" Value="{ThemeResource AccentButtonBorderBrushDisabled}" />
|
||||
<!--<Setter Target="ContentHolder.StrokeThickness" Value="{TemplateBinding BorderThickness}" />-->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="StateStates">
|
||||
<VisualState x:Name="Default" />
|
||||
<VisualState x:Name="Error">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentHolder.Fill" Value="{ThemeResource InfoBarErrorSeverityBackgroundBrush}" />
|
||||
<Setter Target="KeyPresenter.Foreground" Value="{ThemeResource InfoBarErrorSeverityIconBackground}" />
|
||||
<Setter Target="ContentHolder.Stroke" Value="{ThemeResource InfoBarErrorSeverityIconBackground}" />
|
||||
<Setter Target="ContentHolder.StrokeThickness" Value="2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallTextKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="local:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="12,0,12,2" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="LargeOutlineTextKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="local:KeyVisual">
|
||||
<Setter Property="Background" Value="{ThemeResource ControlFillColorDefaultBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ControlStrongStrokeColorDefaultBrush}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallOutlineTextKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="local:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Background" Value="{ThemeResource ControlFillColorDefaultBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ControlStrongStrokeColorDefaultBrush}" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="BorderThickness" Value="1,1,1,1" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="8,0,8,2" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style
|
||||
x:Key="DefaultIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="local:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="56" />
|
||||
<Setter Property="MinHeight" Value="48" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="Padding" Value="16,8,16,8" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="local:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="FontSize" Value="10" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style
|
||||
x:Key="SmallOutlineIconKeyVisualStyle"
|
||||
BasedOn="{StaticResource DefaultTextKeyVisualStyle}"
|
||||
TargetType="local:KeyVisual">
|
||||
<Setter Property="MinWidth" Value="40" />
|
||||
<Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="FontSize" Value="9" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -46,10 +45,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
_appWindow = AppWindow.GetFromWindowId(_windowId);
|
||||
this.Activated += Window_Activated_SetIcon;
|
||||
|
||||
OverlappedPresenter presenter = _appWindow.Presenter as OverlappedPresenter;
|
||||
presenter.IsMinimizable = false;
|
||||
presenter.IsMaximizable = false;
|
||||
|
||||
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
||||
_currentDPI = dpi;
|
||||
float scalingFactor = (float)dpi / DefaultDPI;
|
||||
@@ -63,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;
|
||||
@@ -154,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