Merge branch 'microsoft:master' into master

This commit is contained in:
Den Delimarsky 2021-05-09 17:54:24 -07:00
commit 6e16f5c918
104 changed files with 3289 additions and 3488 deletions

View File

@ -1,4 +1,4 @@
blank_issues_enabled: false blank_issues_enabled: true
contact_links: contact_links:
- name: "\U0001F4F7 Video Conference Mute Issue" - name: "\U0001F4F7 Video Conference Mute Issue"
url: https://github.com/microsoft/PowerToys/issues/6246 url: https://github.com/microsoft/PowerToys/issues/6246

View File

@ -1,11 +1,9 @@
aac aac
AAD
abcd abcd
abcdef abcdef
abcdefgh abcdefgh
abgr abgr
ABlocked ABlocked
ABOUTBOX
Abug Abug
accctrl accctrl
Acceleratorkeys Acceleratorkeys
@ -22,12 +20,10 @@ actioned
activatable activatable
ACTIVATEAPP ACTIVATEAPP
Addavirtualdesktop Addavirtualdesktop
Addins
addrum addrum
ADDUNDORECORD ADDUNDORECORD
ADifferent ADifferent
ADMINS ADMINS
adopsinsider
advapi advapi
advfirewall advfirewall
AFeature AFeature
@ -35,27 +31,22 @@ AFX
AGGREGATABLE AGGREGATABLE
AHybrid AHybrid
Aissue Aissue
akamaihd
ALarger ALarger
alekhyareddy alekhyareddy
alertsolid
ALIGNLEFT ALIGNLEFT
ALLAPPS ALLAPPS
Alloc Alloc
ALLOWUNDO ALLOWUNDO
ALogo
ALPHATYPE ALPHATYPE
Altdown Altdown
altform altform
amd amd
Amicrosoft Amicrosoft
AMirror
AModifier AModifier
AMPROPERTY AMPROPERTY
AMPROPSETID AMPROPSETID
anges anges
ansicolor ansicolor
antialiased
ANull ANull
AOC AOC
aocfnapldcnfbofgmbbllojgocaelgdd aocfnapldcnfbofgmbbllojgocaelgdd
@ -108,7 +99,6 @@ asm
asmx asmx
aspnet aspnet
aspx aspx
asyncwebview
ASYNCWINDOWPLACEMENT ASYNCWINDOWPLACEMENT
ASYNCWINDOWPOS ASYNCWINDOWPOS
atl atl
@ -120,7 +110,6 @@ atlstr
attr attr
Attribs Attribs
aumid aumid
Aut
AUTHN AUTHN
AUTOAPPEND AUTOAPPEND
autocomplete autocomplete
@ -176,14 +165,12 @@ bootstrapper
BOTTOMALIGN BOTTOMALIGN
BPBF BPBF
bpp bpp
Breadcrumb
bricelam bricelam
BRIGHTGREEN BRIGHTGREEN
Browsable Browsable
bsd bsd
bstr bstr
bti bti
Btn
BTNFACE BTNFACE
Bto Bto
buf buf
@ -196,7 +183,6 @@ buildtransitive
BValue BValue
bytearray bytearray
callbackptr callbackptr
capitalized
CANRENAME CANRENAME
Captureascreenshot Captureascreenshot
CAPTURECHANGED CAPTURECHANGED
@ -223,13 +209,8 @@ chdir
checkbox checkbox
checkboxes checkboxes
CHECKCANCELED CHECKCANCELED
Checkedin
checknetisolation
Chicklet
CHILDACTIVATE CHILDACTIVATE
Childof
CHILDWINDOW CHILDWINDOW
choiceref
chrdavis chrdavis
Chris's Chris's
chrono chrono
@ -250,7 +231,6 @@ CLIENTPULL
clientside clientside
CLIPCHILDREN CLIPCHILDREN
CLIPSIBLINGS CLIPSIBLINGS
CLogo
clrcall clrcall
cls cls
CLSCTX CLSCTX
@ -277,7 +257,6 @@ Codespaces
COINIT COINIT
colorconv colorconv
colorpicker colorpicker
colorpickerref
COLORREF COLORREF
colorscheme colorscheme
COLUMNCLICK COLUMNCLICK
@ -290,12 +269,10 @@ comhost
cominterop cominterop
commandline commandline
commctrl commctrl
Commdlg
commondataservicecds
Compat Compat
COMPOSITIONFULL COMPOSITIONFULL
comsupp comsupp
concat Concat
concrt concrt
config config
CONFLICTINGMODIFIERKEY CONFLICTINGMODIFIERKEY
@ -312,8 +289,6 @@ CONTROLL
CONTROLPARENT CONTROLPARENT
Controlz Controlz
coords coords
COPYDATASTRUCT
corehr
cortana cortana
cotaskmem cotaskmem
COULDNOT COULDNOT
@ -321,7 +296,6 @@ countof
countslabelrenamingfmt countslabelrenamingfmt
countslabelselectedfmt countslabelselectedfmt
cout cout
CPlus
CPower CPower
cpp cpp
CPPARM CPPARM
@ -339,8 +313,6 @@ cref
CRename CRename
critsec critsec
crlf crlf
CRM
crmcustomerinsightsapp
CRSEL CRSEL
crutkas crutkas
CSearch CSearch
@ -371,7 +343,6 @@ CURSORINFO
cursorpos cursorpos
CUSTOMACTIONTEST CUSTOMACTIONTEST
cvd cvd
cw
cwchar cwchar
cwd cwd
cx cx
@ -393,7 +364,6 @@ DARKPURPLE
DARKRED DARKRED
DARKTEAL DARKTEAL
DARKYELLOW DARKYELLOW
Dataflows
Datavalue Datavalue
DATAW DATAW
davidegiacometti davidegiacometti
@ -443,18 +413,15 @@ desktopshorcutinstalled
desktopwindowxamlsource desktopwindowxamlsource
dest dest
DEU DEU
Devagya
devblogs devblogs
devdocs devdocs
devenum devenum
DEVMON DEVMON
df df
DFactory DFactory
Dialpad
diffing diffing
difftime difftime
dimm dimm
dirname
dirs dirs
DISABLEASACTIONKEY DISABLEASACTIONKEY
dispid dispid
@ -476,15 +443,12 @@ dllexport
dllhost dllhost
dllimport dllimport
dllmain dllmain
DLogo
DMap
DNLEN DNLEN
doctype doctype
DONTVALIDATEPATH DONTVALIDATEPATH
dotnet dotnet
DOUBLEBUFFER DOUBLEBUFFER
DOverlay DOverlay
downlevel
DOWNLOADONLY DOWNLOADONLY
dpi dpi
DPICHANGED DPICHANGED
@ -492,14 +456,11 @@ DPolicy
DPopup DPopup
DPSAPI DPSAPI
Draggen Draggen
Drakula
DRAWFRAME DRAWFRAME
drawingcolor drawingcolor
dreamsofameaningfullife dreamsofameaningfullife
drivedetectionwarning drivedetectionwarning
DRM
dropdown dropdown
dropref
dshow dshow
dst dst
DSVG DSVG
@ -527,110 +488,21 @@ dword
dworigin dworigin
dwrite dwrite
dxgi dxgi
EABF
EAC
EACB
EACC
EADA
EADB
EADF
EAE
EAEE
EAEF
EAF
EAFC
EAFD
Easeof Easeof
eb EB
EBC
EBD
EBDA
EBE
EBEC
EBEE
EBEF
EBF
EBFC
ECAA
ECAB
ECAC
ECAF
ECCA
ECCB
ECCD
ECDC
ECDE
ECDF
ECEB
ECED
ECEE
ecef ecef
ECFE
ECFF
ecount ecount
EDB EDB
EDBB
EDBC
EDBD
EDBE
EDBF
EDCA
EDCB
EDCC
EDCD
EDCE
EDCF
EDDB
EDDC
EDDD
EDDE
EDDF
EDE
ededf ededf
EDFF
edgelogo
edgeoldlogo
EDITKEYBOARD EDITKEYBOARD
editkeyboardwindow editkeyboardwindow
editorbody
editorconfig editorconfig
editorhead
editorheadbuttons
editortitle
editorzone
EDITSHORTCUTS EDITSHORTCUTS
editshortcutswindow editshortcutswindow
Edu
EEB
EEBA
EEBB
EEBC
EEBD
EEBE
EEBF
EECB
EECC
EECD
EECE
EECF
EED
EEED
EEEF
EEF
efa efa
EFB
EFBA
EFC
EFDA
EFDE
EFE
EFEB
EFFC
EFFE
efgh efgh
EFile EFile
egistry egistry
ELogo
elseif elseif
Emoji Emoji
emptyrecyclebin emptyrecyclebin
@ -642,7 +514,6 @@ endregion
Enque Enque
ENTERSIZEMOVE ENTERSIZEMOVE
Entireitemname Entireitemname
Entitlements
entrypoint entrypoint
ENU ENU
enum enum
@ -665,7 +536,6 @@ etw
EUQ EUQ
ev ev
evenodd evenodd
eventdatemissed
eventlog eventlog
everytime everytime
EWXFORCE EWXFORCE
@ -705,7 +575,6 @@ FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR FANCYZONESEDITOR
Farbraum Farbraum
FARPROC FARPROC
Favicon
fd fd
feimage feimage
ffcd ffcd
@ -746,7 +615,6 @@ FPower
FRAMECHANGED FRAMECHANGED
FRAMEDOWNLOAD FRAMEDOWNLOAD
Froml Froml
FSharp
fstream fstream
ftps ftps
FTYPE FTYPE
@ -779,7 +647,6 @@ GETSTATE
GETTEXT GETTEXT
GETTEXTLENGTH GETTEXTLENGTH
gh gh
Giftbox
github github
githubusercontent githubusercontent
gitignore gitignore
@ -788,7 +655,6 @@ gmx
GNumber GNumber
google google
GPTR GPTR
grayscale
gui gui
guiddef guiddef
GUITHREADINFO GUITHREADINFO
@ -889,6 +755,7 @@ hxx
Hyperlink Hyperlink
IAction IAction
IActivated IActivated
IAnimatable
IApp IApp
IApplication IApplication
IAppx IAppx
@ -910,7 +777,6 @@ IComparer
ICONERROR ICONERROR
ICONINFORMATION ICONINFORMATION
ICONQUESTION ICONQUESTION
Iconset
IContext IContext
ICONWARNING ICONWARNING
ICore ICore
@ -934,6 +800,7 @@ IDrop
idx idx
IDXGI IDXGI
IDYES IDYES
IEasing
IEnum IEnum
IEnumerable IEnumerable
IEnumerator IEnumerator
@ -953,12 +820,10 @@ IFile
ifndef ifndef
IFolder IFolder
ifstream ifstream
IIcon
iid iid
IImage IImage
Iindex Iindex
IInitialize IInitialize
IInput
IInspectable IInspectable
IItem IItem
IJson IJson
@ -977,8 +842,6 @@ IMoniker
IMonitor IMonitor
IMouse IMouse
impl impl
INav
Inbox
INDEXTOSTATEIMAGEMASK INDEXTOSTATEIMAGEMASK
indierawk indierawk
Infobar Infobar
@ -1017,7 +880,6 @@ INSTALLSTATE
INSTALLUILEVEL INSTALLUILEVEL
Inste Inste
Intelli Intelli
Intellisense
interactable interactable
Interlop Interlop
interop interop
@ -1108,15 +970,13 @@ jp
jpe jpe
jpeg jpeg
jpg jpg
jsoref
JPN JPN
json json
jsonval jsonval
jsx JSX
junja junja
jxr jxr
jyuwono jyuwono
Kaizala
kbd kbd
KBDLLHOOKSTRUCT KBDLLHOOKSTRUCT
kbm kbm
@ -1132,10 +992,8 @@ keydown
keydropdowncontrol keydropdowncontrol
keyevent keyevent
KEYEVENTF KEYEVENTF
keynum
keyof
keyhook keyhook
keyparts keynum
keyremaps keyremaps
keystokes keystokes
Keystool Keystool
@ -1158,6 +1016,7 @@ LBUTTON
LBUTTONDBLCLK LBUTTONDBLCLK
LBUTTONDOWN LBUTTONDOWN
LBUTTONUP LBUTTONUP
LCIDTo
lcl lcl
Lclean Lclean
LCONTROL LCONTROL
@ -1189,7 +1048,6 @@ Linux
listbox listbox
listview listview
llkhf llkhf
LLogo
Llvm Llvm
lmcons lmcons
LMEM LMEM
@ -1263,8 +1121,7 @@ LVS
LVSIL LVSIL
LWA LWA
lwin lwin
Lync LZW
lzw
mailto mailto
MAINICON MAINICON
Mainwindow Mainwindow
@ -1288,7 +1145,6 @@ MATCHMODE
MAXIMIZEBOX MAXIMIZEBOX
MAXSHORTCUTSIZE MAXSHORTCUTSIZE
maxversiontested maxversiontested
mayitbeegh
MBUTTON MBUTTON
MBUTTONDBLCLK MBUTTONDBLCLK
MBUTTONDOWN MBUTTONDOWN
@ -1325,11 +1181,9 @@ miniz
minlevel minlevel
MINMAXINFO MINMAXINFO
Miracast Miracast
mixin
MJPG MJPG
mkdir mkdir
MLogo mmi
MMI
mockapi mockapi
MODECHANGE MODECHANGE
moderncop moderncop
@ -1349,7 +1203,6 @@ MOUSESWITCH
MOUSEWHEEL MOUSEWHEEL
MOVESIZEEND MOVESIZEEND
MOVESIZESTART MOVESIZESTART
Moveto
MOVEWINDOWS MOVEWINDOWS
moz moz
mozilla mozilla
@ -1393,7 +1246,6 @@ mutexes
muxc muxc
mvvm mvvm
MYICON MYICON
myuri
NAMECHANGE NAMECHANGE
nameof nameof
NAMEONLY NAMEONLY
@ -1487,10 +1339,8 @@ notmatch
Noto Noto
NOTRACK NOTRACK
NOUPDATE NOUPDATE
nowrap
NOZORDER NOZORDER
npm npm
npmjs
npos npos
NResize NResize
ntdll ntdll
@ -1516,7 +1366,6 @@ Oem
officehubintl officehubintl
ofs ofs
ofstream ofstream
Oject
oldcolor oldcolor
olditem olditem
oldnewthing oldnewthing
@ -1527,11 +1376,7 @@ OleAut
OLECHAR OLECHAR
OLEDB OLEDB
OLIVEGREEN OLIVEGREEN
OLogo
Onboarding
onebranch onebranch
onedrive
onedrivelogo
ONITEM ONITEM
onstd onstd
oobe oobe
@ -1548,7 +1393,6 @@ ostr
ostream ostream
ostringstream ostringstream
OSVERSIONINFOEXW OSVERSIONINFOEXW
osx
otating otating
OUTOFCONTEXT OUTOFCONTEXT
OUTOFMEMORY OUTOFMEMORY
@ -1562,7 +1406,6 @@ PAINTSTRUCT
PAIT PAIT
PALEBLUE PALEBLUE
PALETTEWINDOW PALETTEWINDOW
Pano
paramref paramref
params params
PARENTNOTIFY PARENTNOTIFY
@ -1579,7 +1422,6 @@ pcb
pcelt pcelt
pch pch
PCIDLIST PCIDLIST
PCOPYDATASTRUCT
PCorswitchaccounts PCorswitchaccounts
PCWSTR PCWSTR
pdb pdb
@ -1599,9 +1441,6 @@ pgp
pguid pguid
phbm phbm
phbmp phbmp
Phishing
php
phptest
phwnd phwnd
pici pici
pid pid
@ -1616,7 +1455,6 @@ PKBDLLHOOKSTRUCT
placeholders placeholders
plib plib
PLK PLK
PLogo
plugin plugin
pluginsmodel pluginsmodel
plvdi plvdi
@ -1627,14 +1465,12 @@ pnm
pnmdr pnmdr
pnmlv pnmlv
POINTL POINTL
pointstar
policheck policheck
polymorpism polymorpism
popd popd
popup popup
POPUPWINDOW POPUPWINDOW
posix posix
powerappscds
powerlauncher powerlauncher
powerpreview powerpreview
powerrename powerrename
@ -1673,7 +1509,6 @@ prgms
pri pri
PRINTCLIENT PRINTCLIENT
printf printf
Printfax
prm prm
PROCESSKEY PROCESSKEY
PRODUCTVERSION PRODUCTVERSION
@ -1717,8 +1552,6 @@ pwcs
PWSTR PWSTR
pwtd pwtd
px px
py
Qand
QI QI
qianlifeng qianlifeng
qit qit
@ -1824,7 +1657,6 @@ roslyn
royvou royvou
rpc rpc
RRF RRF
RSHIFT
rshift rshift
Rsp Rsp
rst rst
@ -1844,7 +1676,6 @@ runsettings
runtimeclass runtimeclass
runtimeconfig runtimeconfig
runtimes runtimes
rutkas
rv rv
rvalue rvalue
rvm rvm
@ -1865,7 +1696,6 @@ SCOPEID
screenshot screenshot
scrollable scrollable
scrollviewer scrollviewer
scss
sddl sddl
sdk sdk
SDKDDK SDKDDK
@ -1901,9 +1731,7 @@ Setttings
SETWORKAREA SETWORKAREA
sfgao sfgao
SFGAOF SFGAOF
Sharei
SHAREIMAGELISTS SHAREIMAGELISTS
Sharepoint
sharpkeys sharpkeys
shcore shcore
shellapi shellapi
@ -1912,7 +1740,6 @@ shellex
SHELLEXECUTEINFO SHELLEXECUTEINFO
SHELLEXECUTEINFOW SHELLEXECUTEINFOW
shellscalingapi shellscalingapi
Shelveset
SHFILEINFO SHFILEINFO
SHGFI SHGFI
SHIFTDRAG SHIFTDRAG
@ -1923,11 +1750,11 @@ shlwapi
shobjidl shobjidl
SHORTCUTATLEAST SHORTCUTATLEAST
shortcutcontrol shortcutcontrol
Shortcutguide
SHORTCUTMAXONEACTIONKEY SHORTCUTMAXONEACTIONKEY
SHORTCUTNOREPEATEDMODIFIER SHORTCUTNOREPEATEDMODIFIER
SHORTCUTONEACTIONKEY SHORTCUTONEACTIONKEY
SHORTCUTSTARTWITHMODIFIER SHORTCUTSTARTWITHMODIFIER
Shortcutguide
Shortcuttool Shortcuttool
shortdate shortdate
SHORTPATH SHORTPATH
@ -1944,13 +1771,11 @@ sidepanel
siex siex
SIGABRT SIGABRT
sigdn sigdn
Signin
signtool signtool
SINGLEKEY SINGLEKEY
singlekeyremapcontrol singlekeyremapcontrol
SINGLESEL SINGLESEL
singletones singletones
sixpointstar
SIZEBOX SIZEBOX
sizeg sizeg
Sizename Sizename
@ -1963,11 +1788,8 @@ sketchapp
SKIPOWNPROCESS SKIPOWNPROCESS
sku sku
SKYBLUE SKYBLUE
Skype
SLGP SLGP
Slideshow
sln sln
SLogo
SMALLICON SMALLICON
SMTO SMTO
Snd Snd
@ -1980,9 +1802,7 @@ spdo
spdth spdth
spec'ing spec'ing
spesi spesi
spinbuttonref
splitwstring splitwstring
spoprod
sppd sppd
sppre sppre
spsi spsi
@ -2006,9 +1826,7 @@ sstream
STACKFRAME STACKFRAME
stackoverflow stackoverflow
stackpanel stackpanel
Staffhub
standalone standalone
Starburst
STARTF STARTF
startupapps startupapps
STARTUPINFO STARTUPINFO
@ -2025,8 +1843,8 @@ stdcall
stdcpp stdcpp
stdcpplatest stdcpplatest
stdexcept stdexcept
stdio
stdin stdin
stdio
stdlib stdlib
STDMETHODCALLTYPE STDMETHODCALLTYPE
STDMETHODIMP STDMETHODIMP
@ -2041,9 +1859,8 @@ stoul
stoull stoull
strcmp strcmp
strftime strftime
Strikethrough
Stringified Stringified
stringify Stringify
STRINGIZE STRINGIZE
stringstream stringstream
stringtable stringtable
@ -2056,14 +1873,11 @@ Stubless
STYLECHANGED STYLECHANGED
STYLECHANGING STYLECHANGING
stylecop stylecop
stylesheet
Subdir Subdir
subfolder subfolder
subkey subkey
SUBLANG SUBLANG
subquery subquery
subsetted
subsetter
substr substr
Superbar Superbar
sut sut
@ -2071,13 +1885,10 @@ SVE
svg svg
SVGIn SVGIn
svgpreviewhandler svgpreviewhandler
svgr
SVGSVG
Switchbetweenvirtualdesktops Switchbetweenvirtualdesktops
SWP SWP
swprintf swprintf
SYMED SYMED
Symlink
SYMOPT SYMOPT
SYNCPAINT SYNCPAINT
sys sys
@ -2096,7 +1907,6 @@ SYSTEMTIME
sz sz
tadele tadele
Tahoma Tahoma
talenthrcore
talynone talynone
TApp TApp
TApplication TApplication
@ -2108,7 +1918,6 @@ targetnametoken
targetsize targetsize
targetver targetver
taskbar taskbar
Taskboard
TASKID TASKID
taskkill taskkill
tasklist tasklist
@ -2120,7 +1929,6 @@ tcscpy
TCustom TCustom
td td
TDevice TDevice
Telemarketer
Templated Templated
templatenamespace templatenamespace
Temporarilypeekatthedesktop Temporarilypeekatthedesktop
@ -2134,7 +1942,6 @@ textblock
textbox textbox
TEXTINCLUDE TEXTINCLUDE
textref textref
TFVC
tga tga
thead thead
THEMECHANGED THEMECHANGED
@ -2143,7 +1950,6 @@ THISCOMPONENT
thre thre
tif tif
TILEDWINDOW TILEDWINDOW
Timeline
TIMERID TIMERID
timeunion timeunion
timeutil timeutil
@ -2154,11 +1960,6 @@ tlbimp
tmp tmp
TNP TNP
todo todo
toggleleft
toggleoff
toggleon
toggleref
toggleright
toggleswitch toggleswitch
toolbar toolbar
toolset toolset
@ -2176,16 +1977,11 @@ traies
TRAYMOUSEMESSAGE TRAYMOUSEMESSAGE
TRK TRK
trl trl
truetype
trunc trunc
tslint
TStr TStr
tsx tsx
tt tt
TVM
tw tw
twelvepointstar
txyewy
TYMED TYMED
typedef typedef
TYPEKEY TYPEKEY
@ -2194,12 +1990,10 @@ typename
typeof typeof
typeparam typeparam
TYPESHORTCUT TYPESHORTCUT
typings
Tz Tz
UAC UAC
UAL UAL
uap uap
UCHAR
udit udit
UIA UIA
Uid Uid
@ -2222,7 +2016,6 @@ UNCPRIORITY
undef undef
UNDNAME UNDNAME
unescape unescape
Ungroup
unicode unicode
Unindent Unindent
uninit uninit
@ -2242,13 +2035,10 @@ UNLEN
unlicense unlicense
Unmap Unmap
UNORM UNORM
Unpublish
unregister unregister
unregistering unregistering
unremapped unremapped
unsubscribe unsubscribe
Unsync
Untag
Updatelayout Updatelayout
UPDOWNKEYDROPSLIST UPDOWNKEYDROPSLIST
UPGRADINGPRODUCTCODE UPGRADINGPRODUCTCODE
@ -2302,7 +2092,6 @@ viewbox
viewmodel viewmodel
virtualization virtualization
Visibletrue Visibletrue
Visio
visualbrush visualbrush
visualstudio visualstudio
vk vk
@ -2310,19 +2099,16 @@ VKey
vm vm
vmax vmax
vmin vmin
Voicemail
VOS VOS
VREDRAW VREDRAW
VSC VSC
VSCBD VSCBD
vscode vscode
vsconfig
VSCROLL VSCROLL
vse vse
vsonline vsonline
vstemplate vstemplate
VSTHRD VSTHRD
VSTS
VSTT VSTT
VTABLE VTABLE
Vtbl Vtbl
@ -2347,20 +2133,15 @@ wcsnicmp
wdp wdp
wdupenv wdupenv
weakme weakme
webapp
webcam webcam
webclient webclient
webkit
webp
webpack webpack
webpage webpage
website website
webview
wekyb wekyb
Whichdoes Whichdoes
whitespaces whitespaces
WIC WIC
Wifi
wifstream wifstream
wih wih
wiki wiki
@ -2378,7 +2159,7 @@ WINDOWNAME
WINDOWPLACEMENT WINDOWPLACEMENT
WINDOWPOSCHANGED WINDOWPOSCHANGED
WINDOWPOSCHANGING WINDOWPOSCHANGING
windowsapp Windowsapp
WINDOWSBUILDNUMBER WINDOWSBUILDNUMBER
windowsdesktop windowsdesktop
windowssearch windowssearch
@ -2412,7 +2193,6 @@ WKSG
wmain wmain
WMKEYDOWN WMKEYDOWN
WMKEYUP WMKEYUP
wmonk
wmp wmp
WMSYSKEYDOWN WMSYSKEYDOWN
WMSYSKEYUP WMSYSKEYUP
@ -2421,7 +2201,6 @@ WNDCLASS
WNDCLASSEX WNDCLASSEX
WNDCLASSEXW WNDCLASSEXW
WNDPROC WNDPROC
woff
wofstream wofstream
wordpad wordpad
workaround workaround
@ -2454,14 +2233,12 @@ wxs
xa xa
xamarin xamarin
xaml xaml
XAngle
XAttribute XAttribute
xbf xbf
XBUTTON XBUTTON
XBUTTONDBLCLK XBUTTONDBLCLK
XBUTTONDOWN XBUTTONDOWN
XBUTTONUP XBUTTONUP
XCOPY
XDiff XDiff
XDocument XDocument
XElement XElement
@ -2484,15 +2261,12 @@ XToolset
xunit xunit
XY XY
Yaml Yaml
YAngle
YDiff YDiff
YESNO YESNO
YIncrement YIncrement
yinwang yinwang
YLogo
yml yml
YOffset YOffset
YourUserName
YStr YStr
YUY YUY
YUYV YUYV

View File

@ -26,7 +26,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
- Windows 10 v1903 (build 18362) or newer. - Windows 10 v1903 (build 18362) or newer.
- ⚠️ PowerToys minimum version of Windows 10 is v1903 starting with the 0.37 release - ⚠️ PowerToys minimum version of Windows 10 is v1903 starting with the 0.37 release
- Have [.NET Core 3.1.13 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.13-windows-x64-installer). The installer should handle this but we want to directly make people aware. - Have [.NET Core 3.1.14 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-desktop-3.1.14-windows-x64-installer). The installer should handle this but we want to directly make people aware.
### Via GitHub with EXE [Recommended] ### Via GitHub with EXE [Recommended]

View File

@ -28,3 +28,10 @@
- [ ] Test the plugin with a local build. Build the installer, install, check that the plugin works as expected - [ ] Test the plugin with a local build. Build the installer, install, check that the plugin works as expected
- [ ] All plugin's binaries have to be included in the signed build [`pipeline.user.windows.yml`](/.pipelines/pipeline.user.windows.yml) - [ ] All plugin's binaries have to be included in the signed build [`pipeline.user.windows.yml`](/.pipelines/pipeline.user.windows.yml)
- [ ] The plugin target framework has to be .NET Core 3.1. All dependencies have to have .NET 5 version - [ ] The plugin target framework has to be .NET Core 3.1. All dependencies have to have .NET 5 version
Some localization steps can only be done after the first pass by the localization team to provide the localized resources.
In the PR that adds a new plugin, reference a new issue to track the work for fully enabling localization for the new plugin.
- [ ] Add the resource folder to https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L825
- [ ] Add the resource files under the section https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L882

View File

@ -219,16 +219,6 @@ int Bootstrapper(HINSTANCE hInstance)
SetupLogger(logDir, severity); SetupLogger(logDir, severity);
spdlog::debug("PowerToys Bootstrapper is launched\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}\nextract_msi: {}\n", noFullUI, g_Silent, noStartPT, skipDotnetInstall, logLevel, installDirArg, extractMsiOnly); spdlog::debug("PowerToys Bootstrapper is launched\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}\nextract_msi: {}\n", noFullUI, g_Silent, noStartPT, skipDotnetInstall, logLevel, installDirArg, extractMsiOnly);
const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
// Do not support installing on Windows < 1903
if (myVersion >= VersionHelper{0, 36, 0} && updating::is_old_windows_version())
{
ShowMessageBoxError(IDS_OLD_WINDOWS_ERROR);
spdlog::error("PowerToys {} requires at least Windows 1903 to run.", myVersion.toString());
return 1;
}
// If a user requested an MSI -> extract it and exit // If a user requested an MSI -> extract it and exit
if (extractMsiOnly) if (extractMsiOnly)
@ -241,9 +231,20 @@ int Bootstrapper(HINSTANCE hInstance)
{ {
spdlog::error("MSI installer couldn't be extracted"); spdlog::error("MSI installer couldn't be extracted");
} }
return 0; return 0;
} }
const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
// Do not support installing on Windows < 1903
if (updating::is_1809_or_older())
{
ShowMessageBoxError(IDS_OLD_WINDOWS_ERROR);
spdlog::error("PowerToys {} requires at least Windows 1903 to run.", myVersion.toString());
return 1;
}
// Check if there's a newer version installed // Check if there's a newer version installed
const auto installedVersion = updating::get_installed_powertoys_version(); const auto installedVersion = updating::get_installed_powertoys_version();
if (installedVersion && *installedVersion >= myVersion) if (installedVersion && *installedVersion >= myVersion)

View File

@ -822,7 +822,7 @@
<Fragment> <Fragment>
<!-- Resource directories should be added only if the installer is built on the build farm --> <!-- Resource directories should be added only if the installer is built on the build farm -->
<?ifdef env.IsPipeline?> <?ifdef env.IsPipeline?>
<?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder?> <?foreach ParentDirectory in LauncherInstallFolder;FancyZonesInstallFolder;ImageResizerInstallFolder;ColorPickerInstallFolder;FileExplorerPreviewInstallFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder?>
<DirectoryRef Id="$(var.ParentDirectory)"> <DirectoryRef Id="$(var.ParentDirectory)">
<!-- Resource file directories --> <!-- Resource file directories -->
<?foreach Language in $(var.LocLanguageList)?> <?foreach Language in $(var.LocLanguageList)?>
@ -879,7 +879,7 @@
<Component Id="SVGPreviewHandler_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)FileExplorerPreviewInstallFolder"> <Component Id="SVGPreviewHandler_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)FileExplorerPreviewInstallFolder">
<File Id="SVGPreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\FileExplorerPreview\$(var.Language)\SvgPreviewHandler.resources.dll" /> <File Id="SVGPreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\FileExplorerPreview\$(var.Language)\SvgPreviewHandler.resources.dll" />
</Component> </Component>
<!-- Launcher plugin resources --> <!-- PowerToys Run aka Launcher plugin resources -->
<Component Id="Launcher_Calculator_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)CalculatorPluginFolder"> <Component Id="Launcher_Calculator_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)CalculatorPluginFolder">
<File Id="Launcher_Calculator_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\Calculator\$(var.Language)\Microsoft.PowerToys.Run.Plugin.Calculator.resources.dll" /> <File Id="Launcher_Calculator_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\Calculator\$(var.Language)\Microsoft.PowerToys.Run.Plugin.Calculator.resources.dll" />
</Component> </Component>
@ -910,6 +910,9 @@
<Component Id="Launcher_Service_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)ServicePluginFolder"> <Component Id="Launcher_Service_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)ServicePluginFolder">
<File Id="Launcher_Service_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\Service\$(var.Language)\Microsoft.PowerToys.Run.Plugin.Service.resources.dll" /> <File Id="Launcher_Service_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\Service\$(var.Language)\Microsoft.PowerToys.Run.Plugin.Service.resources.dll" />
</Component> </Component>
<Component Id="Launcher_System_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)SystemPluginFolder">
<File Id="Launcher_System_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\launcher\Plugins\System\$(var.Language)\Microsoft.PowerToys.Run.Plugin.System.resources.dll" />
</Component>
<?undef IdSafeLanguage?> <?undef IdSafeLanguage?>
<?endforeach?> <?endforeach?>
<?endif?> <?endif?>

View File

@ -1,11 +1,10 @@
#include "pch.h" #include "pch.h"
#include <array>
#include <algorithm>
#include "keyboard_layout_impl.h" #include "keyboard_layout_impl.h"
#include "shared_constants.h" #include "shared_constants.h"
#include <winrt/Windows.UI.Core.h>
using namespace winrt;
LayoutMap::LayoutMap() : LayoutMap::LayoutMap() :
impl(new LayoutMap::LayoutMapImpl()) impl(new LayoutMap::LayoutMapImpl())
{ {

View File

@ -10,7 +10,7 @@ namespace fs = std::filesystem;
namespace updating namespace updating
{ {
constexpr size_t REQUIRED_MINIMAL_PATCH = 13; constexpr size_t REQUIRED_MINIMAL_PATCH = 14;
bool dotnet_is_installed() bool dotnet_is_installed()
{ {
@ -46,7 +46,7 @@ namespace updating
std::optional<fs::path> download_dotnet() std::optional<fs::path> download_dotnet()
{ {
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/aa717f57-3ae5-48fa-a3ab-0018338d0726/fb37276b1575772461701339110e7a54/windowsdesktop-runtime-3.1.13-win-x64.exe"; const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/88437980-f813-4a01-865c-f992ad4909bb/9a936984781f6ce3526ffc946267e0ea/windowsdesktop-runtime-3.1.14-win-x64.exe";
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe"; const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME; auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;

View File

@ -193,7 +193,7 @@ namespace updating
co_return false; co_return false;
} }
bool is_old_windows_version() bool is_1809_or_older()
{ {
return !Is19H1OrHigher(); return !Is19H1OrHigher();
} }

View File

@ -17,5 +17,5 @@ namespace updating
std::optional<VersionHelper> get_installed_powertoys_version(); std::optional<VersionHelper> get_installed_powertoys_version();
std::future<bool> uninstall_previous_msix_version_async(); std::future<bool> uninstall_previous_msix_version_async();
bool is_old_windows_version(); bool is_1809_or_older();
} }

View File

@ -1,60 +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.Windows;
using System.Windows.Media.Animation;
using Microsoft.Xaml.Behaviors;
namespace ColorPicker.Behaviors
{
public class MoveWindowBehavior : Behavior<Window>
{
public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(double), typeof(MoveWindowBehavior), new PropertyMetadata(new PropertyChangedCallback(LeftPropertyChanged)));
private static void LeftPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var sender = ((MoveWindowBehavior)d).AssociatedObject;
var move = new DoubleAnimation(sender.Left, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
move.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut };
sender.BeginAnimation(Window.LeftProperty, move, HandoffBehavior.Compose);
}
public static readonly DependencyProperty TopProperty = DependencyProperty.Register("Top", typeof(double), typeof(MoveWindowBehavior), new PropertyMetadata(new PropertyChangedCallback(TopPropertyChanged)));
private static void TopPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var sender = ((MoveWindowBehavior)d).AssociatedObject;
var move = new DoubleAnimation(sender.Top, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
move.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseInOut };
sender.BeginAnimation(Window.TopProperty, move, HandoffBehavior.Compose);
}
public double Left
{
get
{
return (double)GetValue(LeftProperty);
}
set
{
SetValue(LeftProperty, value);
}
}
public double Top
{
get
{
return (double)GetValue(TopProperty);
}
set
{
SetValue(TopProperty, value);
}
}
}
}

View File

@ -11,20 +11,55 @@ namespace ColorPicker.Behaviors
{ {
public class ResizeBehavior : Behavior<FrameworkElement> public class ResizeBehavior : Behavior<FrameworkElement>
{ {
// animation behavior variables
// used when size is getting bigger
private static readonly TimeSpan _animationTime = TimeSpan.FromMilliseconds(200);
private static readonly IEasingFunction _easeFunction = new SineEase() { EasingMode = EasingMode.EaseOut };
// used when size is getting smaller
private static readonly TimeSpan _animationTimeSmaller = _animationTime;
private static readonly IEasingFunction _easeFunctionSmaller = new QuadraticEase() { EasingMode = EasingMode.EaseIn };
private static void CustomAnimation(DependencyProperty prop, IAnimatable sender, double fromValue, double toValue)
{
// if the animation is to/from a value of 0, it will cancel the current animation
DoubleAnimation move = null;
if (toValue > 0 && fromValue > 0)
{
// if getting bigger
if (fromValue < toValue)
{
move = new DoubleAnimation(fromValue, toValue, new Duration(_animationTime), FillBehavior.Stop)
{
EasingFunction = _easeFunction,
};
}
else
{
move = new DoubleAnimation(fromValue, toValue, new Duration(_animationTimeSmaller), FillBehavior.Stop)
{
EasingFunction = _easeFunctionSmaller,
};
}
}
// HandoffBehavior must be SnapshotAndReplace
// Compose does not allow cancellation
sender.BeginAnimation(prop, move, HandoffBehavior.SnapshotAndReplace);
}
public static readonly DependencyProperty WidthProperty = DependencyProperty.Register("Width", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(WidthPropertyChanged))); public static readonly DependencyProperty WidthProperty = DependencyProperty.Register("Width", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(WidthPropertyChanged)));
private static void WidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void WidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
var sender = ((ResizeBehavior)d).AssociatedObject; var sender = ((ResizeBehavior)d).AssociatedObject;
var move = new DoubleAnimation(sender.Width, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
move.Completed += (s, e1) =>
{
sender.BeginAnimation(FrameworkElement.WidthProperty, null);
sender.Width = (double)e.NewValue;
};
move.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut }; var fromValue = sender.Width;
sender.BeginAnimation(FrameworkElement.WidthProperty, move, HandoffBehavior.Compose); var toValue = (double)e.NewValue;
// setting Width before animation prevents jumping
sender.Width = toValue;
CustomAnimation(FrameworkElement.WidthProperty, sender, fromValue, toValue);
} }
public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(HeightPropertyChanged))); public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(double), typeof(ResizeBehavior), new PropertyMetadata(new PropertyChangedCallback(HeightPropertyChanged)));
@ -32,15 +67,13 @@ namespace ColorPicker.Behaviors
private static void HeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) private static void HeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
var sender = ((ResizeBehavior)d).AssociatedObject; var sender = ((ResizeBehavior)d).AssociatedObject;
var move = new DoubleAnimation(sender.Height, (double)e.NewValue, new Duration(TimeSpan.FromMilliseconds(150)), FillBehavior.Stop);
move.Completed += (s, e1) =>
{
sender.BeginAnimation(FrameworkElement.HeightProperty, null);
sender.Height = (double)e.NewValue;
};
move.EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseOut }; var fromValue = sender.Height;
sender.BeginAnimation(FrameworkElement.HeightProperty, move, HandoffBehavior.Compose); var toValue = (double)e.NewValue;
// setting Height before animation prevents jumping
sender.Height = toValue;
CustomAnimation(FrameworkElement.HeightProperty, sender, fromValue, toValue);
} }
public double Width public double Width

View File

@ -18,33 +18,27 @@ namespace ColorPicker.Helpers
[Export(typeof(ZoomWindowHelper))] [Export(typeof(ZoomWindowHelper))]
public class ZoomWindowHelper public class ZoomWindowHelper
{ {
private const int ZoomWindowChangeDelayInMS = 50;
private const int ZoomFactor = 2; private const int ZoomFactor = 2;
private const int BaseZoomImageSize = 50; private const int BaseZoomImageSize = 50;
private const int MaxZoomLevel = 4; private const int MaxZoomLevel = 4;
private const int MinZoomLevel = 0; private const int MinZoomLevel = 0;
private static readonly Bitmap _bmp = new Bitmap(BaseZoomImageSize, BaseZoomImageSize, PixelFormat.Format32bppArgb);
private static readonly Graphics _graphics = Graphics.FromImage(_bmp);
private readonly IZoomViewModel _zoomViewModel; private readonly IZoomViewModel _zoomViewModel;
private readonly AppStateHandler _appStateHandler; private readonly AppStateHandler _appStateHandler;
private readonly IThrottledActionInvoker _throttledActionInvoker;
private int _currentZoomLevel; private int _currentZoomLevel;
private int _previousZoomLevel; private int _previousZoomLevel;
private ZoomWindow _zoomWindow; private ZoomWindow _zoomWindow;
private double _lastLeft;
private double _lastTop;
private double _previousScaledX;
private double _previousScaledY;
[ImportingConstructor] [ImportingConstructor]
public ZoomWindowHelper(IZoomViewModel zoomViewModel, AppStateHandler appStateHandler, IThrottledActionInvoker throttledActionInvoker) public ZoomWindowHelper(IZoomViewModel zoomViewModel, AppStateHandler appStateHandler)
{ {
_zoomViewModel = zoomViewModel; _zoomViewModel = zoomViewModel;
_appStateHandler = appStateHandler; _appStateHandler = appStateHandler;
_throttledActionInvoker = throttledActionInvoker;
_appStateHandler.AppClosed += AppStateHandler_AppClosed; _appStateHandler.AppClosed += AppStateHandler_AppClosed;
_appStateHandler.AppHidden += AppStateHandler_AppClosed; _appStateHandler.AppHidden += AppStateHandler_AppClosed;
} }
@ -73,7 +67,7 @@ namespace ColorPicker.Helpers
{ {
_currentZoomLevel = 0; _currentZoomLevel = 0;
_previousZoomLevel = 0; _previousZoomLevel = 0;
HideZoomWindow(); HideZoomWindow(true);
} }
private void SetZoomImage(System.Windows.Point point) private void SetZoomImage(System.Windows.Point point)
@ -89,28 +83,17 @@ namespace ColorPicker.Helpers
{ {
var x = (int)point.X - (BaseZoomImageSize / 2); var x = (int)point.X - (BaseZoomImageSize / 2);
var y = (int)point.Y - (BaseZoomImageSize / 2); var y = (int)point.Y - (BaseZoomImageSize / 2);
var rect = new Rectangle(x, y, BaseZoomImageSize, BaseZoomImageSize);
using (var bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb)) _graphics.CopyFromScreen(x, y, 0, 0, _bmp.Size, CopyPixelOperation.SourceCopy);
{
var g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
var bitmapImage = BitmapToImageSource(bmp); _zoomViewModel.ZoomArea = BitmapToImageSource(_bmp);
_zoomViewModel.ZoomArea = bitmapImage;
_zoomViewModel.ZoomFactor = 1;
}
} }
else else
{ {
var enlarge = (_currentZoomLevel - _previousZoomLevel) > 0 ? true : false; _zoomViewModel.ZoomFactor = Math.Pow(ZoomFactor, _currentZoomLevel - 1);
var currentZoomFactor = enlarge ? ZoomFactor : 1.0 / ZoomFactor;
_zoomViewModel.ZoomFactor *= currentZoomFactor;
} }
ShowZoomWindow((int)point.X, (int)point.Y); ShowZoomWindow(point);
} }
private static BitmapSource BitmapToImageSource(Bitmap bitmap) private static BitmapSource BitmapToImageSource(Bitmap bitmap)
@ -130,84 +113,67 @@ namespace ColorPicker.Helpers
} }
} }
private void HideZoomWindow() private void HideZoomWindow(bool fully = false)
{ {
if (_zoomWindow != null) if (_zoomWindow != null)
{ {
_zoomWindow.Hide(); _zoomWindow.Opacity = 0;
_zoomViewModel.DesiredWidth = 0;
_zoomViewModel.DesiredHeight = 0;
if (fully)
{
_zoomWindow.Hide();
}
} }
} }
private void ShowZoomWindow(int x, int y) private void ShowZoomWindow(System.Windows.Point point)
{ {
if (_zoomWindow == null) _zoomWindow ??= new ZoomWindow
{ {
_zoomWindow = new ZoomWindow(); Content = _zoomViewModel,
_zoomWindow.Content = _zoomViewModel; Opacity = 0,
_zoomWindow.Loaded += ZoomWindow_Loaded; };
_zoomWindow.IsVisibleChanged += ZoomWindow_IsVisibleChanged;
}
// we just started zooming, remember where we opened zoom window
if (_currentZoomLevel == 1 && _previousZoomLevel == 0)
{
var dpi = MonitorResolutionHelper.GetCurrentMonitorDpi();
_previousScaledX = x / dpi.DpiScaleX;
_previousScaledY = y / dpi.DpiScaleY;
}
_lastLeft = Math.Floor(_previousScaledX - (BaseZoomImageSize * Math.Pow(ZoomFactor, _currentZoomLevel - 1) / 2));
_lastTop = Math.Floor(_previousScaledY - (BaseZoomImageSize * Math.Pow(ZoomFactor, _currentZoomLevel - 1) / 2));
var justShown = false;
if (!_zoomWindow.IsVisible) if (!_zoomWindow.IsVisible)
{ {
_zoomWindow.Left = _lastLeft;
_zoomWindow.Top = _lastTop;
_zoomViewModel.Height = BaseZoomImageSize;
_zoomViewModel.Width = BaseZoomImageSize;
_zoomWindow.Show(); _zoomWindow.Show();
justShown = true; }
if (_zoomWindow.Opacity < 0.5)
{
var halfWidth = _zoomWindow.Width / 2;
var halfHeight = _zoomWindow.Height / 2;
// usually takes 1-3 iterations to converge
// 5 is just an arbitrary limit to prevent infinite loops
for (var i = 0; i < 5; i++)
{
// mouse position relative to top left of _zoomWindow
var scaledPoint = _zoomWindow.PointFromScreen(point);
var diffX = scaledPoint.X - halfWidth;
var diffY = scaledPoint.Y - halfHeight;
// minimum difference that is considered important
const double minDiff = 0.05;
if (Math.Abs(diffX) < minDiff && Math.Abs(diffY) < minDiff)
{
break;
}
_zoomWindow.Left += diffX;
_zoomWindow.Top += diffY;
}
// make sure color picker window is on top of just opened zoom window // make sure color picker window is on top of just opened zoom window
AppStateHandler.SetTopMost(); AppStateHandler.SetTopMost();
_zoomWindow.Opacity = 1;
} }
// dirty hack - sometimes when we just show a window on a second monitor with different DPI, _zoomViewModel.DesiredHeight = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
// window position is not set correctly on a first time, we need to "ping" it again to make it appear on the proper location _zoomViewModel.DesiredWidth = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
if (justShown)
{
_zoomWindow.Left = _lastLeft + 1;
_zoomWindow.Top = _lastTop + 1;
SessionEventHelper.Event.ZoomUsed = true;
}
_throttledActionInvoker.ScheduleAction(
() =>
{
_zoomWindow.DesiredLeft = _lastLeft;
_zoomWindow.DesiredTop = _lastTop;
_zoomViewModel.DesiredHeight = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
_zoomViewModel.DesiredWidth = BaseZoomImageSize * _zoomViewModel.ZoomFactor;
},
ZoomWindowChangeDelayInMS);
}
private void ZoomWindow_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// need to set at this point again, to avoid issues moving between screens with different scaling
if ((bool)e.NewValue)
{
_zoomWindow.Left = _lastLeft;
_zoomWindow.Top = _lastTop;
}
}
private void ZoomWindow_Loaded(object sender, RoutedEventArgs e)
{
// need to call it again at load time, because it does was not dpi aware at the first time of Show() call
_zoomWindow.Left = _lastLeft;
_zoomWindow.Top = _lastTop;
} }
private void AppStateHandler_AppClosed(object sender, EventArgs e) private void AppStateHandler_AppClosed(object sender, EventArgs e)

View File

@ -133,7 +133,17 @@ namespace ColorPicker.ViewModels
{ {
ClipboardHelper.CopyToClipboard(ColorText); ClipboardHelper.CopyToClipboard(ColorText);
_userSettings.ColorHistory.Insert(0, GetColorString()); var color = GetColorString();
var oldIndex = _userSettings.ColorHistory.IndexOf(color);
if (oldIndex != -1)
{
_userSettings.ColorHistory.Move(oldIndex, 0);
}
else
{
_userSettings.ColorHistory.Insert(0, color);
}
if (_userSettings.ColorHistory.Count > _userSettings.ColorHistoryLimit.Value) if (_userSettings.ColorHistory.Count > _userSettings.ColorHistoryLimit.Value)
{ {

View File

@ -11,6 +11,8 @@
Focusable="False"> Focusable="False">
<Border x:Name="WindowBorder" <Border x:Name="WindowBorder"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="{DynamicResource WindowBorderBrush}" BorderBrush="{DynamicResource WindowBorderBrush}"
Margin="12" Margin="12"
BorderThickness="1" BorderThickness="1"

View File

@ -8,7 +8,6 @@
mc:Ignorable="d" mc:Ignorable="d"
Title="Zoom window" Title="Zoom window"
WindowStyle="None" WindowStyle="None"
SizeToContent="WidthAndHeight"
Topmost="True" Topmost="True"
AllowsTransparency="True" AllowsTransparency="True"
Background="Transparent" Background="Transparent"
@ -17,6 +16,5 @@
Focusable="False"> Focusable="False">
<e:Interaction.Behaviors> <e:Interaction.Behaviors>
<behaviors:CloseZoomWindowBehavior/> <behaviors:CloseZoomWindowBehavior/>
<behaviors:MoveWindowBehavior Left="{Binding DesiredLeft, Mode=TwoWay}" Top="{Binding DesiredTop}"/>
</e:Interaction.Behaviors> </e:Interaction.Behaviors>
</Window> </Window>

View File

@ -10,50 +10,16 @@ namespace ColorPicker
/// <summary> /// <summary>
/// Interaction logic for ZoomWindow.xaml /// Interaction logic for ZoomWindow.xaml
/// </summary> /// </summary>
public partial class ZoomWindow : Window, INotifyPropertyChanged public partial class ZoomWindow : Window
{ {
private double _left;
private double _top;
public ZoomWindow() public ZoomWindow()
{ {
InitializeComponent(); InitializeComponent();
DataContext = this; DataContext = this;
}
public double DesiredLeft // must be large enough to fit max zoom
{ Width = 500;
get Height = 500;
{
return _left;
}
set
{
_left = value;
NotifyPropertyChanged(nameof(DesiredLeft));
}
}
public double DesiredTop
{
get
{
return _top;
}
set
{
_top = value;
NotifyPropertyChanged(nameof(DesiredTop));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
} }
} }

View File

@ -10,21 +10,9 @@
<ui:ThemeResources /> <ui:ThemeResources />
<ui:XamlControlsResources /> <ui:XamlControlsResources />
<ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyles.xaml" /> <ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyles.xaml" />
<ResourceDictionary Source="pack://application:,,,/Styles/GridViewStyles.xaml" />
<ResourceDictionary Source="pack://application:,,,/Styles/LayoutPreviewStyles.xaml" /> <ResourceDictionary Source="pack://application:,,,/Styles/LayoutPreviewStyles.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<Style x:Key="UWPFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border Margin="-2"
CornerRadius="4"
BorderThickness="2"
BorderBrush="{DynamicResource PrimaryForegroundBrush}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -12,7 +12,7 @@
BorderThickness="0" BorderThickness="0"
xmlns:ui="http://schemas.modernwpf.com/2019" xmlns:ui="http://schemas.modernwpf.com/2019"
ui:WindowHelper.UseModernWindowStyle="True" ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.IsIconVisible="True" ui:TitleBar.IsIconVisible="False"
SizeToContent="Height" SizeToContent="Height"
Background="{DynamicResource PrimaryBackgroundBrush}" Background="{DynamicResource PrimaryBackgroundBrush}"
ResizeMode="NoResize" ResizeMode="NoResize"
@ -45,6 +45,7 @@
FontFamily="Segoe MDL2 Assets" FontFamily="Segoe MDL2 Assets"
Content="&#xE109;" Content="&#xE109;"
FontSize="24" FontSize="24"
TabIndex="0"
ToolTip="{x:Static props:Resources.Add_zone}" ToolTip="{x:Static props:Resources.Add_zone}"
DataContext="{Binding Path=Model, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" DataContext="{Binding Path=Model, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
IsEnabled="{Binding IsZoneAddingAllowed}" IsEnabled="{Binding IsZoneAddingAllowed}"
@ -60,9 +61,11 @@
Style="{StaticResource DefaultButtonStyle}" Style="{StaticResource DefaultButtonStyle}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Column="2" Grid.Column="2"
TabIndex="2"
Click="OnCancel" /> Click="OnCancel" />
<Button Content="{x:Static props:Resources.Save_Apply}" <Button Content="{x:Static props:Resources.Save_Apply}"
Style="{StaticResource AccentButtonStyle}" Style="{StaticResource AccentButtonStyle}"
TabIndex="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Click="OnSaveApplyTemplate" /> Click="OnSaveApplyTemplate" />
</Grid> </Grid>

View File

@ -12,7 +12,7 @@
BorderThickness="0" BorderThickness="0"
xmlns:ui="http://schemas.modernwpf.com/2019" xmlns:ui="http://schemas.modernwpf.com/2019"
ui:WindowHelper.UseModernWindowStyle="True" ui:WindowHelper.UseModernWindowStyle="True"
ui:TitleBar.IsIconVisible="True" ui:TitleBar.IsIconVisible="False"
SizeToContent="Height" SizeToContent="Height"
Background="{DynamicResource PrimaryBackgroundBrush}" Background="{DynamicResource PrimaryBackgroundBrush}"
ResizeMode="NoResize" ResizeMode="NoResize"
@ -61,10 +61,12 @@
<Button Content="{x:Static props:Resources.Cancel}" <Button Content="{x:Static props:Resources.Cancel}"
Style="{StaticResource DefaultButtonStyle}" Style="{StaticResource DefaultButtonStyle}"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
TabIndex="1"
Grid.Column="2" Grid.Column="2"
Click="OnCancel" /> Click="OnCancel" />
<Button Content="{x:Static props:Resources.Save_Apply}" <Button Content="{x:Static props:Resources.Save_Apply}"
Style="{StaticResource AccentButtonStyle}" Style="{StaticResource AccentButtonStyle}"
TabIndex="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Click="OnSaveApplyTemplate" /> Click="OnSaveApplyTemplate" />
</Grid> </Grid>

View File

@ -31,48 +31,54 @@
<Converters:LayoutTypeTemplateToVisibilityConverter x:Key="LayoutTypeTemplateToVisibilityConverter" /> <Converters:LayoutTypeTemplateToVisibilityConverter x:Key="LayoutTypeTemplateToVisibilityConverter" />
<Converters:LayoutModelTypeBlankToVisibilityConverter x:Key="LayoutModelTypeBlankToVisibilityConverter" /> <Converters:LayoutModelTypeBlankToVisibilityConverter x:Key="LayoutModelTypeBlankToVisibilityConverter" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<ContextMenu x:Key="LayoutContextMenu" Visibility="{Binding Path=Type, Converter={StaticResource LayoutModelTypeBlankToVisibilityConverter}}">
<MenuItem Header="{x:Static props:Resources.Edit}"
Click="EditLayout_Click">
<MenuItem.Icon>
<ui:FontIcon Glyph="&#xE104;" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static props:Resources.Edit_zones}"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}"
Click="EditZones_Click">
<MenuItem.Icon>
<ui:FontIcon Glyph="&#xED35;" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static props:Resources.Duplicate}"
Click="DuplicateLayout_Click"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}">
<MenuItem.Icon>
<ui:FontIcon Glyph="&#xE8C8;" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static props:Resources.Create_Custom_From_Template}"
Click="DuplicateLayout_Click"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeTemplateToVisibilityConverter}}">
<MenuItem.Icon>
<ui:FontIcon Glyph="&#xE8C8;" />
</MenuItem.Icon>
</MenuItem>
<Separator Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}" />
<MenuItem Header="{x:Static props:Resources.Delete}"
Click="DeleteLayout_Click"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeCustomToVisibilityConverter}}">
<MenuItem.Icon>
<ui:FontIcon Glyph="&#xE107;" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
<DropShadowEffect x:Key="CardShadow" BlurRadius="6"
Opacity="0.24"
ShadowDepth="1" />
<Style x:Key="CardStyle"
TargetType="Border">
<Setter Property="Background"
Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="BorderThickness"
Value="2" />
<Setter Property="CornerRadius"
Value="4" />
<Setter Property="Focusable"
Value="True" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource UWPFocusVisualStyle}" />
<Setter Property="Border.Effect"
Value="{StaticResource CardShadow}" />
<Style.Triggers>
<Trigger Property="Border.IsMouseOver"
Value="True">
<Setter Property="Border.Background"
Value="{DynamicResource LayoutItemBackgroundPointerOverBrush}" />
<Setter Property="Border.BorderBrush"
Value="{DynamicResource LayoutItemBackgroundPointerOverBrush}" />
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="MonitorItemTemplate"> <DataTemplate x:Key="MonitorItemTemplate">
<Border x:Name="MonitorItem" <Border x:Name="MonitorItem"
Width="{Binding DisplayWidth}" Width="{Binding DisplayWidth}"
Height="{Binding DisplayHeight}" Height="{Binding DisplayHeight}"
AutomationProperties.Name="{x:Static props:Resources.Monitor}" AutomationProperties.Name="{x:Static props:Resources.Monitor}"
AutomationProperties.HelpText="{Binding Index}" AutomationProperties.HelpText="{Binding Index}">
Margin="8,2,8,8"
KeyDown="MonitorItem_KeyDown"
MouseDown="MonitorItem_MouseDown"
Style="{StaticResource CardStyle}">
<Border.ToolTip> <Border.ToolTip>
<ToolTip> <ToolTip>
<StackPanel> <StackPanel>
@ -94,7 +100,7 @@
FontWeight="SemiBold" FontWeight="SemiBold"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Foreground="{DynamicResource PrimaryForegroundBrush}" /> Foreground="{Binding (TextElement.Foreground), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}" />
<TextBlock Name="ResolutionText" <TextBlock Name="ResolutionText"
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
Text="{Binding Dimensions}" Text="{Binding Dimensions}"
@ -104,114 +110,59 @@
FontWeight="SemiBold" FontWeight="SemiBold"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Foreground="{DynamicResource SecondaryForegroundBrush}" /> Opacity="0.6"
Foreground="{Binding (TextElement.Foreground), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}" />
</StackPanel> </StackPanel>
</Border> </Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Selected}"
Value="true">
<Setter TargetName="IndexText"
Property="Foreground"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter TargetName="ResolutionText"
Property="Foreground"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter TargetName="MonitorItem"
Property="BorderBrush"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="LayoutItemTemplate"> <DataTemplate x:Key="LayoutItemTemplate">
<Grid Background="Transparent" <Grid Background="Transparent">
Width="216" <Grid
KeyDown="LayoutItem_KeyDown" Width="180"
MouseDown="LayoutItem_Click" Height="140"
Margin="0,0,0,12"> Margin="16">
<Grid.RowDefinitions>
<Border x:Name="LayoutItem" <RowDefinition Height="24" />
Style="{StaticResource CardStyle}" <RowDefinition Height="124" />
Margin="8" <RowDefinition Height="0" />
FocusManager.GotFocus="LayoutItem_Focused"> </Grid.RowDefinitions>
<TextBlock Name="layoutName"
<Grid Margin="16"> TextTrimming="CharacterEllipsis"
<Grid.RowDefinitions> Text="{Binding Name}"
<RowDefinition Height="24" /> FontSize="15"
<RowDefinition Height="124" /> FontWeight="SemiBold"
<RowDefinition Height="0" /> Margin="0,-4,24,0"
</Grid.RowDefinitions> ToolTip="{Binding Name}"
<TextBlock Name="layoutName" Foreground="{Binding (TextElement.Foreground), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}" />
TextTrimming="CharacterEllipsis" <local:LayoutPreview Grid.Row="1"
Text="{Binding Name}" Margin="0,8,0,8"
FontSize="15" VerticalAlignment="Stretch"
Grid.Row="0" HorizontalAlignment="Stretch" />
FontWeight="SemiBold" </Grid>
HorizontalAlignment="Left" <Button Content="&#xE104;"
VerticalAlignment="Top" x:Name="EditLayoutButton"
Margin="0,-4,24,0" FontFamily="Segoe MDL2 Assets"
ToolTip="{Binding Name}" FontSize="14"
Foreground="{DynamicResource PrimaryForegroundBrush}" /> HorizontalAlignment="Right"
VerticalAlignment="Top"
<Button Content="&#xE104;" Margin="4"
x:Name="EditLayoutButton" Height="36"
FontFamily="Segoe MDL2 Assets" Width="36"
FontSize="14" Padding="0"
HorizontalAlignment="Right" Grid.RowSpan="4"
VerticalAlignment="Bottom" BorderBrush="Transparent"
Margin="-8,-16,-8,0" Click="EditLayout_Click"
Height="36" Background="Transparent"
Width="36" Visibility="{Binding Path=Type, Converter={StaticResource LayoutModelTypeBlankToVisibilityConverter}}"
Padding="0" Foreground="{Binding (TextElement.Foreground), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}}"
BorderBrush="Transparent" ToolTip="{x:Static props:Resources.Edit}"
Click="EditLayout_Click" AutomationProperties.HelpText="{x:Static props:Resources.Edit}"
Background="Transparent" AutomationProperties.Name="{Binding Name}"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutModelTypeBlankToVisibilityConverter}}" Style="{StaticResource AccentButtonStyle}"
Foreground="{DynamicResource PrimaryForegroundBrush}" ui:ControlHelper.CornerRadius="36" />
ToolTip="{x:Static props:Resources.Edit_Layout}"
AutomationProperties.Name="{x:Static props:Resources.Edit_Layout}"
Style="{StaticResource AccentButtonStyle}"
ui:ControlHelper.CornerRadius="36">
</Button>
<local:LayoutPreview Grid.Row="1"
Margin="0,8,0,8"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" />
</Grid>
</Border>
<Border x:Name="SelectionHighlight"
CornerRadius="4"
BorderThickness="3"
Margin="6"
Visibility="Collapsed"
BorderBrush="{DynamicResource SystemControlBackgroundAccentBrush}" />
</Grid> </Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsApplied}"
Value="true">
<Setter TargetName="layoutName"
Property="Foreground"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
<Setter TargetName="SelectionHighlight"
Property="Visibility"
Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}"
Value="true">
<Setter TargetName="LayoutItem"
Property="BorderBrush"
Value="{DynamicResource LayoutItemBorderPointerOverBrush}" />
<Setter TargetName="EditLayoutButton"
Property="Foreground"
Value="{DynamicResource SystemControlBackgroundAccentBrush}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate> </DataTemplate>
</Window.Resources> </Window.Resources>
@ -221,8 +172,6 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="1" <Grid Grid.Row="1"
Background="{DynamicResource PrimaryBackgroundBrush}"> Background="{DynamicResource PrimaryBackgroundBrush}">
<ScrollViewer> <ScrollViewer>
@ -240,20 +189,26 @@
FontWeight="SemiBold" FontWeight="SemiBold"
FontSize="24" /> FontSize="24" />
<ItemsControl ItemsSource="{Binding DefaultModels}" <ui:GridView ItemsSource="{Binding DefaultModels}"
Grid.Row="1" Grid.Row="1"
ItemTemplate="{StaticResource LayoutItemTemplate}" ItemTemplate="{StaticResource LayoutItemTemplate}"
AutomationProperties.LabeledBy="{Binding ElementName=TemplatesHeaderBlock}" AutomationProperties.LabeledBy="{Binding ElementName=TemplatesHeaderBlock}"
TabIndex="4" TabIndex="1"
x:Name="DefaultModelsItemsControl" IsItemClickEnabled="True"
Margin="-8,8,-8,0"> SelectionMode="Single"
<ItemsControl.ItemsPanel> IsSelectionEnabled="True"
<ItemsPanelTemplate> ItemClick="Layout_ItemClick"
<WrapPanel Orientation="Horizontal" MouseDoubleClick="LayoutItem_MouseDoubleClick"
x:Name="DefaultModelsWrapPanel" /> Margin="-8,8,-8,0">
</ItemsPanelTemplate> <ui:GridView.ItemContainerStyle>
</ItemsControl.ItemsPanel> <Style BasedOn="{StaticResource LayoutItemContainerStyle}"
</ItemsControl> TargetType="ui:GridViewItem">
<Setter Property="ContextMenu"
Value="{StaticResource LayoutContextMenu}">
</Setter>
</Style>
</ui:GridView.ItemContainerStyle>
</ui:GridView>
<TextBlock Text="{x:Static props:Resources.Custom}" <TextBlock Text="{x:Static props:Resources.Custom}"
x:Name="CustomHeaderBlock" x:Name="CustomHeaderBlock"
@ -289,20 +244,28 @@
Data="M45,48H25.5V45H45V25.5H25.5v-3H45V3H25.5V0H48V48ZM22.5,48H3V45H22.5V3H3V0H25.5V48ZM0,48V0H3V48Z" /> Data="M45,48H25.5V45H45V25.5H25.5v-3H45V3H25.5V0H48V48ZM22.5,48H3V45H22.5V3H3V0H25.5V48ZM0,48V0H3V48Z" />
<TextBlock Text="{x:Static props:Resources.No_Custom_Layouts_Message}" Margin="0,16,0,0" Foreground="{DynamicResource SecondaryForegroundBrush}" /> <TextBlock Text="{x:Static props:Resources.No_Custom_Layouts_Message}" Margin="0,16,0,0" Foreground="{DynamicResource SecondaryForegroundBrush}" />
</StackPanel> </StackPanel>
<ItemsControl ItemsSource="{Binding CustomModels}" <ui:GridView ItemsSource="{Binding CustomModels}"
TabIndex="6" TabIndex="2"
AutomationProperties.LabeledBy="{Binding ElementName=CustomHeaderBlock}" ItemTemplate="{StaticResource LayoutItemTemplate}"
ItemTemplate="{StaticResource LayoutItemTemplate}" IsSelectionEnabled="True"
Margin="-8,12,-8,0" SelectionMode="Single"
Grid.Row="4"> IsItemClickEnabled="True"
<ItemsControl.ItemsPanel> ItemClick="Layout_ItemClick"
<ItemsPanelTemplate> MouseDoubleClick="LayoutItem_MouseDoubleClick"
<WrapPanel Orientation="Horizontal" AutomationProperties.LabeledBy="{Binding ElementName=CustomHeaderBlock}"
x:Name="CustomModelsWrapPanel" /> Margin="-8,8,-8,0"
</ItemsPanelTemplate> Grid.Row="4">
</ItemsControl.ItemsPanel> <ui:GridView.ItemContainerStyle>
</ItemsControl> <Style BasedOn="{StaticResource LayoutItemContainerStyle}"
TargetType="ui:GridViewItem">
<Setter Property="ContextMenu"
Value="{StaticResource LayoutContextMenu}">
</Setter>
</Style>
</ui:GridView.ItemContainerStyle>
</ui:GridView>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
@ -311,7 +274,7 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static props:Resources.Create_new_layout}" AutomationProperties.Name="{x:Static props:Resources.Create_new_layout}"
TabIndex="5" TabIndex="3"
Padding="0" Padding="0"
Height="36" Height="36"
Margin="16" Margin="16"
@ -350,19 +313,15 @@
<local1:MonitorViewModel x:Name="monitorViewModel" /> <local1:MonitorViewModel x:Name="monitorViewModel" />
</ScrollViewer.DataContext> </ScrollViewer.DataContext>
<Grid> <Grid>
<ItemsControl x:Name="MainWindowItemControl" <ui:GridView TabIndex="0"
TabIndex="0" HorizontalAlignment="Center"
ItemTemplate="{StaticResource MonitorItemTemplate}" IsSelectionEnabled="True"
ItemsSource="{Binding MonitorInfoForViewModel}"> SelectionMode="Single"
<ItemsControl.ItemsPanel> IsItemClickEnabled="True"
<ItemsPanelTemplate> ItemClick="Monitor_ItemClick"
<StackPanel Background="Transparent" ItemContainerStyle="{StaticResource MonitorItemContainerStyle}"
Orientation="Horizontal" ItemTemplate="{StaticResource MonitorItemTemplate}"
HorizontalAlignment="Center" ItemsSource="{Binding MonitorInfoForViewModel}" />
Margin="8, 0, 8, 16" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid> </Grid>
</ScrollViewer> </ScrollViewer>
</Grid> </Grid>
@ -466,6 +425,7 @@
AutomationProperties.HelpText="{x:Static props:Resources.QuickKey_Description}" AutomationProperties.HelpText="{x:Static props:Resources.QuickKey_Description}"
AutomationProperties.Name="{x:Static props:Resources.QuickKey_Title}" AutomationProperties.Name="{x:Static props:Resources.QuickKey_Title}"
ItemsSource="{Binding QuickKeysAvailable}" ItemsSource="{Binding QuickKeysAvailable}"
KeyDown="ComboBox_KeyDown"
SelectedItem="{Binding QuickKey}" /> SelectedItem="{Binding QuickKey}" />
</StackPanel> </StackPanel>
@ -516,6 +476,7 @@
KeyDown="EditDialogNumberBox_KeyDown" KeyDown="EditDialogNumberBox_KeyDown"
Margin="12,0,0,0" Margin="12,0,0,0"
Header="{x:Static props:Resources.Number_of_zones}" Header="{x:Static props:Resources.Number_of_zones}"
AutomationProperties.Name="{x:Static props:Resources.Number_of_zones}"
SpinButtonPlacementMode="Compact" SpinButtonPlacementMode="Compact"
Text="{Binding TemplateZoneCount}" /> Text="{Binding TemplateZoneCount}" />
</StackPanel> </StackPanel>

View File

@ -60,14 +60,24 @@ namespace FancyZonesEditor
{ {
if (e.Key == Key.Escape) if (e.Key == Key.Escape)
{ {
if (_openedDialog != null) CloseDialog(sender);
{ }
_openedDialog.Hide(); }
}
else private void LayoutItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{ {
OnClosing(sender, null); CloseDialog(sender);
} }
private void CloseDialog(object sender)
{
if (_openedDialog != null)
{
_openedDialog.Hide();
}
else
{
OnClosing(sender, null);
} }
} }
@ -104,13 +114,6 @@ namespace FancyZonesEditor
// Select(((Grid)sender).DataContext as LayoutModel); // Select(((Grid)sender).DataContext as LayoutModel);
} }
private void LayoutItem_Click(object sender, MouseButtonEventArgs e)
{
LayoutModel selectedLayoutModel = ((Grid)sender).DataContext as LayoutModel;
Select(selectedLayoutModel);
Apply();
}
private void LayoutItem_Focused(object sender, RoutedEventArgs e) private void LayoutItem_Focused(object sender, RoutedEventArgs e)
{ {
// Ignore focus on Edit button click // Ignore focus on Edit button click
@ -261,6 +264,8 @@ namespace FancyZonesEditor
private void EditZones_Click(object sender, RoutedEventArgs e) private void EditZones_Click(object sender, RoutedEventArgs e)
{ {
var dataContext = ((FrameworkElement)sender).DataContext;
Select((LayoutModel)dataContext);
EditLayoutDialog.Hide(); EditLayoutDialog.Hide();
var mainEditor = App.Overlay; var mainEditor = App.Overlay;
if (!(mainEditor.CurrentDataContext is LayoutModel model)) if (!(mainEditor.CurrentDataContext is LayoutModel model))
@ -332,19 +337,6 @@ namespace FancyZonesEditor
InvalidateVisual(); InvalidateVisual();
} }
private void MonitorItem_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Space)
{
monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext);
}
}
private void MonitorItem_MouseDown(object sender, MouseButtonEventArgs e)
{
monitorViewModel.SelectCommand.Execute((MonitorInfoModel)(sender as Border).DataContext);
}
// EditLayout: Cancel changes // EditLayout: Cancel changes
private void EditLayoutDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) private void EditLayoutDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{ {
@ -380,12 +372,12 @@ namespace FancyZonesEditor
private async void DeleteLayout(FrameworkElement element) private async void DeleteLayout(FrameworkElement element)
{ {
var dialog = new ModernWpf.Controls.ContentDialog() var dialog = new ContentDialog()
{ {
Title = FancyZonesEditor.Properties.Resources.Are_You_Sure, Title = Properties.Resources.Are_You_Sure,
Content = FancyZonesEditor.Properties.Resources.Are_You_Sure_Description, Content = Properties.Resources.Are_You_Sure_Description,
PrimaryButtonText = FancyZonesEditor.Properties.Resources.Delete, PrimaryButtonText = Properties.Resources.Delete,
SecondaryButtonText = FancyZonesEditor.Properties.Resources.Cancel, SecondaryButtonText = Properties.Resources.Cancel,
}; };
var result = await dialog.ShowAsync(); var result = await dialog.ShowAsync();
@ -430,5 +422,29 @@ namespace FancyZonesEditor
e.Handled = true; e.Handled = true;
} }
} }
private void Layout_ItemClick(object sender, ItemClickEventArgs e)
{
Select(e.ClickedItem as LayoutModel);
Apply();
}
private void Monitor_ItemClick(object sender, ItemClickEventArgs e)
{
monitorViewModel.SelectCommand.Execute(e.ClickedItem as MonitorInfoModel);
}
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Space)
{
e.Handled = true;
ComboBox selectedComboBox = sender as ComboBox;
if (!selectedComboBox.IsDropDownOpen)
{
selectedComboBox.IsDropDownOpen = true;
}
}
}
} }
} }

View File

@ -286,7 +286,7 @@ namespace FancyZonesEditor.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit zone layout. /// Looks up a localized string similar to Edit zones.
/// </summary> /// </summary>
public static string Edit_zones { public static string Edit_zones {
get { get {

View File

@ -148,9 +148,6 @@
<value>Highlight distance</value> <value>Highlight distance</value>
<comment>Distance of when an adjacent zone should light up when the window is close to it</comment> <comment>Distance of when an adjacent zone should light up when the window is close to it</comment>
</data> </data>
<data name="Edit_Layout" xml:space="preserve">
<value>Edit layout</value>
</data>
<data name="Fancy_Zones_Editor_App_Title" xml:space="preserve"> <data name="Fancy_Zones_Editor_App_Title" xml:space="preserve">
<value>FancyZones Editor</value> <value>FancyZones Editor</value>
</data> </data>
@ -295,7 +292,7 @@
<value>Are you sure you want to delete this layout?</value> <value>Are you sure you want to delete this layout?</value>
</data> </data>
<data name="Edit_zones" xml:space="preserve"> <data name="Edit_zones" xml:space="preserve">
<value>Edit zone layout</value> <value>Edit zones</value>
</data> </data>
<data name="No_Custom_Layouts_Message" xml:space="preserve"> <data name="No_Custom_Layouts_Message" xml:space="preserve">
<value>Create or duplicate a layout to get started</value> <value>Create or duplicate a layout to get started</value>
@ -360,4 +357,7 @@
<data name="QuickKey_Title" xml:space="preserve"> <data name="QuickKey_Title" xml:space="preserve">
<value>Layout shortcut</value> <value>Layout shortcut</value>
</data> </data>
<data name="Edit_Layout" xml:space="preserve">
<value>Edit layout</value>
</data>
</root> </root>

View File

@ -0,0 +1,337 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019">
<Style x:Key="UWPFocusVisualStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border Margin="-2"
CornerRadius="4"
BorderThickness="2"
BorderBrush="{DynamicResource PrimaryForegroundBrush}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DropShadowEffect x:Key="CardDropShadow"
BlurRadius="6"
Opacity="0.24"
ShadowDepth="1" />
<Style x:Key="MonitorItemContainerStyle"
TargetType="ui:GridViewItem">
<Setter Property="Background"
Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="IsSelected"
Value="{Binding Selected, Mode=OneWay}" />
<Setter Property="AutomationProperties.Name"
Value="{Binding Index}" />
<Setter Property="KeyboardNavigation.TabNavigation"
Value="Local" />
<Setter Property="MinWidth"
Value="0" />
<Setter Property="MinHeight"
Value="0" />
<!--<Setter Property="IsHoldingEnabled" Value="True" />-->
<Setter Property="CornerRadius"
Value="4" />
<Setter Property="Margin"
Value="8" />
<Setter Property="UseSystemFocusVisuals"
Value="{DynamicResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin"
Value="-2" />
<Setter Property="FocusVisualStyle"
Value="{DynamicResource UWPFocusVisualStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ui:GridViewItem">
<Border x:Name="ContentBorder"
Height="{Binding DisplayHeight}"
Width="{Binding DisplayWidth}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Effect="{StaticResource CardDropShadow}">
<VisualStateManager.CustomVisualStateManager>
<ui:SimpleVisualStateManager />
</VisualStateManager.CustomVisualStateManager>
<Grid>
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<Border x:Name="BorderRectangle"
IsHitTestVisible="False"
BorderBrush="{DynamicResource SystemControlHighlightListAccentLowBrush}"
BorderThickness="2"
CornerRadius="4"
Opacity="0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- Focused -->
<Trigger Property="ui:FocusVisualHelper.ShowFocusVisual"
Value="True">
<Setter TargetName="BorderRectangle"
Property="Visibility"
Value="Collapsed" />
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Selected}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="ContentPresenter"
Property="TextElement.Foreground"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="BorderBrush"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
</MultiDataTrigger>
<!-- PointerOver -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsSelected"
Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="BorderBrush"
Value="{DynamicResource SystemControlHighlightListLowBrush}" />
<Setter TargetName="ContentPresenter"
Property="TextElement.Foreground"
Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryBrush"
Value="{DynamicResource SystemControlHighlightListLowBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryThickness"
Value="2" />
</MultiTrigger>
<!-- Selected -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="False" />
<Condition Property="IsSelected"
Value="True" />
</MultiTrigger.Conditions>
<!--<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="Stroke"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter TargetName="ContentPresenter"
Property="TextElement.Foreground"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryBrush"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryThickness"
Value="2" />-->
</MultiTrigger>
<!-- PointerOverSelected -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsSelected"
Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="BorderBrush"
Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryBrush"
Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryThickness"
Value="2" />
</MultiTrigger>
<!-- Disabled -->
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="ContentBorder"
Property="Opacity"
Value="{DynamicResource ListViewItemDisabledThemeOpacity}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="LayoutItemContainerStyle"
TargetType="ui:GridViewItem">
<Setter Property="Background"
Value="{DynamicResource LayoutItemBackgroundBrush}" />
<Setter Property="IsSelected"
Value="{Binding IsApplied, Mode=OneWay}" />
<Setter Property="AutomationProperties.Name"
Value="{Binding Name}" />
<Setter Property="KeyboardNavigation.TabNavigation"
Value="Local" />
<!--<Setter Property="IsHoldingEnabled" Value="True" />-->
<Setter Property="CornerRadius"
Value="4" />
<Setter Property="Margin"
Value="8" />
<Setter Property="UseSystemFocusVisuals"
Value="{DynamicResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin"
Value="-2" />
<Setter Property="FocusVisualStyle"
Value="{DynamicResource UWPFocusVisualStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ui:GridViewItem">
<Border x:Name="ContentBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Effect="{StaticResource CardDropShadow}">
<VisualStateManager.CustomVisualStateManager>
<ui:SimpleVisualStateManager />
</VisualStateManager.CustomVisualStateManager>
<Grid>
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<Border x:Name="BorderRectangle"
IsHitTestVisible="False"
BorderBrush="{DynamicResource SystemControlHighlightListAccentLowBrush}"
BorderThickness="2"
CornerRadius="4"
Opacity="0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- Focused -->
<Trigger Property="ui:FocusVisualHelper.ShowFocusVisual"
Value="True">
<Setter TargetName="BorderRectangle"
Property="Visibility"
Value="Collapsed" />
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsApplied}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="ContentPresenter"
Property="TextElement.Foreground"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="BorderBrush"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
</MultiDataTrigger>
<!-- PointerOver -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsSelected"
Value="False" />
</MultiTrigger.Conditions>
<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="BorderBrush"
Value="{DynamicResource SystemControlHighlightListLowBrush}" />
<Setter TargetName="ContentPresenter"
Property="TextElement.Foreground"
Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryBrush"
Value="{DynamicResource SystemControlHighlightListLowBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryThickness"
Value="2" />
</MultiTrigger>
<!-- Selected -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="False" />
<Condition Property="IsSelected"
Value="True" />
</MultiTrigger.Conditions>
<!--<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="Stroke"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter TargetName="ContentPresenter"
Property="TextElement.Foreground"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryBrush"
Value="{DynamicResource SystemControlHighlightAccentBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryThickness"
Value="2" />-->
</MultiTrigger>
<!-- PointerOverSelected -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsSelected"
Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="BorderRectangle"
Property="Opacity"
Value="1" />
<Setter TargetName="BorderRectangle"
Property="BorderBrush"
Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryBrush"
Value="{DynamicResource SystemControlHighlightListAccentMediumBrush}" />
<Setter Property="ui:FocusVisualHelper.FocusVisualSecondaryThickness"
Value="2" />
</MultiTrigger>
<!-- Disabled -->
<Trigger Property="IsEnabled"
Value="False">
<Setter TargetName="ContentBorder"
Property="Opacity"
Value="{DynamicResource ListViewItemDisabledThemeOpacity}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -6,17 +6,16 @@
#include <common/utils/winapi_error.h> #include <common/utils/winapi_error.h>
#include <common/utils/logger_helper.h> #include <common/utils/logger_helper.h>
#include <common/utils/ProcessWaiter.h>
#include <common/utils/UnhandledExceptionHandler_x64.h> #include <common/utils/UnhandledExceptionHandler_x64.h>
#include <trace.h> #include <trace.h>
#include <KeyboardEventHandlers.h> #include <keyboardmanager/common/KeyboardEventHandlers.h>
#include <KeyboardManagerState.h>
#include <SettingsHelper.h>
#include <EditKeyboardWindow.h> #include <EditKeyboardWindow.h>
#include <EditShortcutsWindow.h> #include <EditShortcutsWindow.h>
#include <common/utils/ProcessWaiter.h> #include <KeyboardManagerState.h>
std::unique_ptr<KeyboardManagerEditor> editor = nullptr; std::unique_ptr<KeyboardManagerEditor> editor = nullptr;
const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEditor_InstanceMutex"; const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEditor_InstanceMutex";
@ -114,13 +113,13 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
KeyboardManagerEditor::KeyboardManagerEditor(HINSTANCE hInst) : KeyboardManagerEditor::KeyboardManagerEditor(HINSTANCE hInst) :
hInstance(hInst) hInstance(hInst)
{ {
bool loadedSuccessful = SettingsHelper::LoadSettings(keyboardManagerState); bool loadedSuccessful = mappingConfiguration.LoadSettings();
if (!loadedSuccessful) if (!loadedSuccessful)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
// retry once // retry once
SettingsHelper::LoadSettings(keyboardManagerState); mappingConfiguration.LoadSettings();
} }
StartLowLevelKeyboardHook(); StartLowLevelKeyboardHook();
@ -149,44 +148,44 @@ void KeyboardManagerEditor::OpenEditorWindow(KeyboardManagerEditorType type)
switch (type) switch (type)
{ {
case KeyboardManagerEditorType::KeyEditor: case KeyboardManagerEditorType::KeyEditor:
CreateEditKeyboardWindow(hInstance, keyboardManagerState); CreateEditKeyboardWindow(hInstance, keyboardManagerState, mappingConfiguration);
break; break;
case KeyboardManagerEditorType::ShortcutEditor: case KeyboardManagerEditorType::ShortcutEditor:
CreateEditShortcutsWindow(hInstance, keyboardManagerState); CreateEditShortcutsWindow(hInstance, keyboardManagerState, mappingConfiguration);
} }
} }
intptr_t KeyboardManagerEditor::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept intptr_t KeyboardManagerEditor::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept
{ {
// If the Detect Key Window is currently activated, then suppress the keyboard event // If the Detect Key Window is currently activated, then suppress the keyboard event
KeyboardManagerHelper::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data); Helpers::KeyboardHookDecision singleKeyRemapUIDetected = keyboardManagerState.DetectSingleRemapKeyUIBackend(data);
if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) if (singleKeyRemapUIDetected == Helpers::KeyboardHookDecision::Suppress)
{ {
return 1; return 1;
} }
else if (singleKeyRemapUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) else if (singleKeyRemapUIDetected == Helpers::KeyboardHookDecision::SkipHook)
{ {
return 0; return 0;
} }
// If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event // If the Detect Shortcut Window from Remap Keys is currently activated, then suppress the keyboard event
KeyboardManagerHelper::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true); Helpers::KeyboardHookDecision remapKeyShortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, true);
if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) if (remapKeyShortcutUIDetected == Helpers::KeyboardHookDecision::Suppress)
{ {
return 1; return 1;
} }
else if (remapKeyShortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) else if (remapKeyShortcutUIDetected == Helpers::KeyboardHookDecision::SkipHook)
{ {
return 0; return 0;
} }
// If the Detect Shortcut Window is currently activated, then suppress the keyboard event // If the Detect Shortcut Window is currently activated, then suppress the keyboard event
KeyboardManagerHelper::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false); Helpers::KeyboardHookDecision shortcutUIDetected = keyboardManagerState.DetectShortcutUIBackend(data, false);
if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::Suppress) if (shortcutUIDetected == Helpers::KeyboardHookDecision::Suppress)
{ {
return 1; return 1;
} }
else if (shortcutUIDetected == KeyboardManagerHelper::KeyboardHookDecision::SkipHook) else if (shortcutUIDetected == Helpers::KeyboardHookDecision::SkipHook)
{ {
return 0; return 0;
} }

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <keyboardmanager/common/Input.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include <KeyboardManagerState.h> #include <KeyboardManagerState.h>
#include <Input.h>
enum class KeyboardManagerEditorType enum class KeyboardManagerEditorType
{ {
@ -32,7 +34,8 @@ private:
inline static HHOOK hook; inline static HHOOK hook;
HINSTANCE hInstance; HINSTANCE hInstance;
KeyboardManagerState keyboardManagerState; KBMEditor::KeyboardManagerState keyboardManagerState;
MappingConfiguration mappingConfiguration;
// Object of class which implements InputInterface. Required for calling library functions while enabling testing // Object of class which implements InputInterface. Required for calling library functions while enabling testing
KeyboardManagerInput::Input inputHandler; KeyboardManagerInput::Input inputHandler;

View File

@ -109,7 +109,7 @@
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Display.lib;shcore.lib;Dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -118,7 +118,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\modules\KeyboardManager\KeyboardManagerEditorLibrary\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>

View File

@ -3,34 +3,12 @@
#include "targetver.h" #include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <unknwn.h>
#include <windows.h> #include <windows.h>
#include <shellapi.h> #include <shellapi.h>
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
#include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Text.h>
#pragma push_macro("GetCurrentTime")
#undef GetCurrentTime
#include <winrt/Windows.UI.Xaml.Automation.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <winrt/Windows.UI.Xaml.Interop.h>
#include <winrt/Windows.ui.xaml.media.h>
#pragma pop_macro("GetCurrentTime")
#include <common/logger/logger.h> #include <common/logger/logger.h>
#include <common/utils/resources.h> #include <common/utils/resources.h>
#include <Generated Files/resource.h> #include <Generated Files/resource.h>
using namespace winrt;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace Windows::UI::Xaml::Hosting;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;

View File

@ -2,17 +2,20 @@
#include "BufferValidationHelpers.h" #include "BufferValidationHelpers.h"
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <KeyboardManagerEditorStrings.h> #include "KeyboardManagerEditorStrings.h"
#include <KeyboardManagerConstants.h> #include "KeyDropDownControl.h"
#include <KeyDropDownControl.h> #include "UIHelpers.h"
#include "EditorHelpers.h"
#include "EditorConstants.h"
namespace BufferValidationHelpers namespace BufferValidationHelpers
{ {
// Function to validate and update an element of the key remap buffer when the selection has changed // Function to validate and update an element of the key remap buffer when the selection has changed
KeyboardManagerHelper::ErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer) ShortcutErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer)
{ {
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError; ShortcutErrorType errorType = ShortcutErrorType::NoError;
// Check if the element was not found or the index exceeds the known keys // Check if the element was not found or the index exceeds the known keys
if (selectedKeyCode != -1) if (selectedKeyCode != -1)
@ -22,13 +25,13 @@ namespace BufferValidationHelpers
{ {
if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == selectedKeyCode) if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == selectedKeyCode)
{ {
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey; errorType = ShortcutErrorType::MapToSameKey;
} }
} }
// If one column is shortcut and other is key no warning required // If one column is shortcut and other is key no warning required
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0) if (errorType == ShortcutErrorType::NoError && colIndex == 0)
{ {
// Check if the key is already remapped to something else // Check if the key is already remapped to something else
for (int i = 0; i < remapBuffer.size(); i++) for (int i = 0; i < remapBuffer.size(); i++)
@ -37,8 +40,8 @@ namespace BufferValidationHelpers
{ {
if (remapBuffer[i].first[colIndex].index() == 0) if (remapBuffer[i].first[colIndex].index() == 0)
{ {
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), selectedKeyCode); ShortcutErrorType result = EditorHelpers::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), selectedKeyCode);
if (result != KeyboardManagerHelper::ErrorType::NoError) if (result != ShortcutErrorType::NoError)
{ {
errorType = result; errorType = result;
break; break;
@ -51,7 +54,7 @@ namespace BufferValidationHelpers
} }
// If there is no error, set the buffer // If there is no error, set the buffer
if (errorType == KeyboardManagerHelper::ErrorType::NoError) if (errorType == ShortcutErrorType::NoError)
{ {
remapBuffer[rowIndex].first[colIndex] = selectedKeyCode; remapBuffer[rowIndex].first[colIndex] = selectedKeyCode;
} }
@ -70,32 +73,32 @@ namespace BufferValidationHelpers
} }
// Function to validate an element of the shortcut remap buffer when the selection has changed // Function to validate an element of the shortcut remap buffer when the selection has changed
std::pair<KeyboardManagerHelper::ErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound) std::pair<ShortcutErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound)
{ {
BufferValidationHelpers::DropDownAction dropDownAction = BufferValidationHelpers::DropDownAction::NoAction; BufferValidationHelpers::DropDownAction dropDownAction = BufferValidationHelpers::DropDownAction::NoAction;
KeyboardManagerHelper::ErrorType errorType = KeyboardManagerHelper::ErrorType::NoError; ShortcutErrorType errorType = ShortcutErrorType::NoError;
size_t dropDownCount = selectedCodes.size(); size_t dropDownCount = selectedCodes.size();
DWORD selectedKeyCode = dropDownFound ? selectedCodes[dropDownIndex] : -1; DWORD selectedKeyCode = dropDownFound ? selectedCodes[dropDownIndex] : -1;
if (selectedKeyCode != -1 && dropDownFound) if (selectedKeyCode != -1 && dropDownFound)
{ {
// If only 1 drop down and action key is chosen: Warn that a modifier must be chosen (if the drop down is not for a hybrid scenario) // If only 1 drop down and action key is chosen: Warn that a modifier must be chosen (if the drop down is not for a hybrid scenario)
if (dropDownCount == 1 && !KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && !isHybridControl) if (dropDownCount == 1 && !Helpers::IsModifierKey(selectedKeyCode) && !isHybridControl)
{ {
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier; errorType = ShortcutErrorType::ShortcutStartWithModifier;
} }
else if (dropDownIndex == dropDownCount - 1) else if (dropDownIndex == dropDownCount - 1)
{ {
// If it is the last drop down // If it is the last drop down
// If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced) // If last drop down and a modifier is selected: add a new drop down (max drop down count should be enforced)
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount < KeyboardManagerConstants::MaxShortcutSize) if (Helpers::IsModifierKey(selectedKeyCode) && dropDownCount < EditorConstants::MaxShortcutSize)
{ {
// If it matched any of the previous modifiers then reset that drop down // If it matched any of the previous modifiers then reset that drop down
if (KeyboardManagerHelper::CheckRepeatedModifier(selectedCodes, selectedKeyCode)) if (EditorHelpers::CheckRepeatedModifier(selectedCodes, selectedKeyCode))
{ {
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier; errorType = ShortcutErrorType::ShortcutCannotHaveRepeatedModifier;
} }
else else
{ {
@ -103,17 +106,17 @@ namespace BufferValidationHelpers
dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown; dropDownAction = BufferValidationHelpers::DropDownAction::AddDropDown;
} }
} }
else if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode) && dropDownCount >= KeyboardManagerConstants::MaxShortcutSize) else if (Helpers::IsModifierKey(selectedKeyCode) && dropDownCount >= EditorConstants::MaxShortcutSize)
{ {
// If last drop down and a modifier is selected but there are already max drop downs: warn the user // If last drop down and a modifier is selected but there are already max drop downs: warn the user
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey; errorType = ShortcutErrorType::ShortcutMaxShortcutSizeOneActionKey;
} }
else if (selectedKeyCode == 0) else if (selectedKeyCode == 0)
{ {
// If None is selected but it's the last index: warn // If None is selected but it's the last index: warn
// If it is a hybrid control and there are 2 drop downs then deletion is allowed // If it is a hybrid control and there are 2 drop downs then deletion is allowed
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize) if (isHybridControl && dropDownCount == EditorConstants::MinShortcutSize)
{ {
// set delete drop down flag // set delete drop down flag
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown; dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
@ -122,40 +125,40 @@ namespace BufferValidationHelpers
else else
{ {
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutOneActionKey; errorType = ShortcutErrorType::ShortcutOneActionKey;
} }
} }
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex) else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
{ {
// Disable can not be selected if one modifier key has already been selected // Disable can not be selected if one modifier key has already been selected
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey; errorType = ShortcutErrorType::ShortcutDisableAsActionKey;
} }
// If none of the above, then the action key will be set // If none of the above, then the action key will be set
} }
else else
{ {
// If it is not the last drop down // If it is not the last drop down
if (KeyboardManagerHelper::IsModifierKey(selectedKeyCode)) if (Helpers::IsModifierKey(selectedKeyCode))
{ {
// If it matched any of the previous modifiers then reset that drop down // If it matched any of the previous modifiers then reset that drop down
if (KeyboardManagerHelper::CheckRepeatedModifier(selectedCodes, selectedKeyCode)) if (EditorHelpers::CheckRepeatedModifier(selectedCodes, selectedKeyCode))
{ {
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier; errorType = ShortcutErrorType::ShortcutCannotHaveRepeatedModifier;
} }
// If not, the modifier key will be set // If not, the modifier key will be set
} }
else if (selectedKeyCode == 0 && dropDownCount > KeyboardManagerConstants::MinShortcutSize) else if (selectedKeyCode == 0 && dropDownCount > EditorConstants::MinShortcutSize)
{ {
// If None is selected and there are more than 2 drop downs // If None is selected and there are more than 2 drop downs
// set delete drop down flag // set delete drop down flag
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown; dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
// do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal // do not delete the drop down now since there may be some other error which would cause the drop down to be invalid after removal
} }
else if (selectedKeyCode == 0 && dropDownCount <= KeyboardManagerConstants::MinShortcutSize) else if (selectedKeyCode == 0 && dropDownCount <= EditorConstants::MinShortcutSize)
{ {
// If it is a hybrid control and there are 2 drop downs then deletion is allowed // If it is a hybrid control and there are 2 drop downs then deletion is allowed
if (isHybridControl && dropDownCount == KeyboardManagerConstants::MinShortcutSize) if (isHybridControl && dropDownCount == EditorConstants::MinShortcutSize)
{ {
// set delete drop down flag // set delete drop down flag
dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown; dropDownAction = BufferValidationHelpers::DropDownAction::DeleteDropDown;
@ -164,13 +167,13 @@ namespace BufferValidationHelpers
else else
{ {
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys; errorType = ShortcutErrorType::ShortcutAtleast2Keys;
} }
} }
else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex) else if (selectedKeyCode == CommonSharedConstants::VK_DISABLED && dropDownIndex)
{ {
// Allow selection of VK_DISABLE only in first dropdown // Allow selection of VK_DISABLE only in first dropdown
errorType = KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey; errorType = ShortcutErrorType::ShortcutDisableAsActionKey;
} }
else if (dropDownIndex != 0 || isHybridControl) else if (dropDownIndex != 0 || isHybridControl)
{ {
@ -193,20 +196,20 @@ namespace BufferValidationHelpers
else else
{ {
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey; errorType = ShortcutErrorType::ShortcutNotMoreThanOneActionKey;
} }
} }
else else
{ {
// If there an action key is chosen on the first drop down and there are more than one drop down menus // If there an action key is chosen on the first drop down and there are more than one drop down menus
// warn and reset the drop down // warn and reset the drop down
errorType = KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier; errorType = ShortcutErrorType::ShortcutStartWithModifier;
} }
} }
} }
// After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del // After validating the shortcut, now for errors like remap to same shortcut, remap shortcut more than once, Win L and Ctrl Alt Del
if (errorType == KeyboardManagerHelper::ErrorType::NoError) if (errorType == ShortcutErrorType::NoError)
{ {
KeyShortcutUnion tempShortcut; KeyShortcutUnion tempShortcut;
if (isHybridControl && KeyDropDownControl::GetNumberOfSelectedKeys(selectedCodes) == 1) if (isHybridControl && KeyDropDownControl::GetNumberOfSelectedKeys(selectedCodes) == 1)
@ -234,9 +237,10 @@ namespace BufferValidationHelpers
// If shortcut to shortcut // If shortcut to shortcut
if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1) if (remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)].index() == 1)
{ {
if (std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<Shortcut>(tempShortcut) && std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]).IsValidShortcut() && std::get<Shortcut>(tempShortcut).IsValidShortcut()) auto& shortcut = std::get<Shortcut>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]);
if (shortcut == std::get<Shortcut>(tempShortcut) && EditorHelpers::IsValidShortcut(shortcut) && EditorHelpers::IsValidShortcut(std::get<Shortcut>(tempShortcut)))
{ {
errorType = KeyboardManagerHelper::ErrorType::MapToSameShortcut; errorType = ShortcutErrorType::MapToSameShortcut;
} }
} }
@ -249,14 +253,14 @@ namespace BufferValidationHelpers
{ {
if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<DWORD>(tempShortcut) && std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) != NULL && std::get<DWORD>(tempShortcut) != NULL) if (std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) == std::get<DWORD>(tempShortcut) && std::get<DWORD>(remapBuffer[rowIndex].first[std::abs(int(colIndex) - 1)]) != NULL && std::get<DWORD>(tempShortcut) != NULL)
{ {
errorType = KeyboardManagerHelper::ErrorType::MapToSameKey; errorType = ShortcutErrorType::MapToSameKey;
} }
} }
// If one column is shortcut and other is key no warning required // If one column is shortcut and other is key no warning required
} }
if (errorType == KeyboardManagerHelper::ErrorType::NoError && colIndex == 0) if (errorType == ShortcutErrorType::NoError && colIndex == 0)
{ {
// Check if the key is already remapped to something else for the same target app // Check if the key is already remapped to something else for the same target app
for (int i = 0; i < remapBuffer.size(); i++) for (int i = 0; i < remapBuffer.size(); i++)
@ -266,10 +270,10 @@ namespace BufferValidationHelpers
if (i != rowIndex && currAppName == appName) if (i != rowIndex && currAppName == appName)
{ {
KeyboardManagerHelper::ErrorType result = KeyboardManagerHelper::ErrorType::NoError; ShortcutErrorType result = ShortcutErrorType::NoError;
if (!isHybridControl) if (!isHybridControl)
{ {
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut)); result = EditorHelpers::DoShortcutsOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
} }
else else
{ {
@ -277,19 +281,20 @@ namespace BufferValidationHelpers
{ {
if (std::get<DWORD>(tempShortcut) != NULL && std::get<DWORD>(remapBuffer[i].first[colIndex]) != NULL) if (std::get<DWORD>(tempShortcut) != NULL && std::get<DWORD>(remapBuffer[i].first[colIndex]) != NULL)
{ {
result = KeyboardManagerHelper::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut)); result = EditorHelpers::DoKeysOverlap(std::get<DWORD>(remapBuffer[i].first[colIndex]), std::get<DWORD>(tempShortcut));
} }
} }
else if (tempShortcut.index() == 1 && remapBuffer[i].first[colIndex].index() == 1) else if (tempShortcut.index() == 1 && remapBuffer[i].first[colIndex].index() == 1)
{ {
if (std::get<Shortcut>(tempShortcut).IsValidShortcut() && std::get<Shortcut>(remapBuffer[i].first[colIndex]).IsValidShortcut()) auto& shortcut = std::get<Shortcut>(remapBuffer[i].first[colIndex]);
if (EditorHelpers::IsValidShortcut(std::get<Shortcut>(tempShortcut)) && EditorHelpers::IsValidShortcut(shortcut))
{ {
result = Shortcut::DoKeysOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut)); result = EditorHelpers::DoShortcutsOverlap(std::get<Shortcut>(remapBuffer[i].first[colIndex]), std::get<Shortcut>(tempShortcut));
} }
} }
// Other scenarios not possible since key to shortcut is with key to key, and shortcut to key is with shortcut to shortcut // Other scenarios not possible since key to shortcut is with key to key, and shortcut to key is with shortcut to shortcut
} }
if (result != KeyboardManagerHelper::ErrorType::NoError) if (result != ShortcutErrorType::NoError)
{ {
errorType = result; errorType = result;
break; break;
@ -298,9 +303,9 @@ namespace BufferValidationHelpers
} }
} }
if (errorType == KeyboardManagerHelper::ErrorType::NoError && tempShortcut.index() == 1) if (errorType == ShortcutErrorType::NoError && tempShortcut.index() == 1)
{ {
errorType = std::get<Shortcut>(tempShortcut).IsShortcutIllegal(); errorType = EditorHelpers::IsShortcutIllegal(std::get<Shortcut>(tempShortcut));
} }
} }

View File

@ -2,6 +2,8 @@
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
#include "ShortcutErrorType.h"
namespace BufferValidationHelpers namespace BufferValidationHelpers
{ {
enum class DropDownAction enum class DropDownAction
@ -13,8 +15,8 @@ namespace BufferValidationHelpers
}; };
// Function to validate and update an element of the key remap buffer when the selection has changed // Function to validate and update an element of the key remap buffer when the selection has changed
KeyboardManagerHelper::ErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer); ShortcutErrorType ValidateAndUpdateKeyBufferElement(int rowIndex, int colIndex, int selectedKeyCode, RemapBuffer& remapBuffer);
// Function to validate an element of the shortcut remap buffer when the selection has changed // Function to validate an element of the shortcut remap buffer when the selection has changed
std::pair<KeyboardManagerHelper::ErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound); std::pair<ShortcutErrorType, DropDownAction> ValidateShortcutBufferElement(int rowIndex, int colIndex, uint32_t dropDownIndex, const std::vector<int32_t>& selectedCodes, std::wstring appName, bool isHybridControl, const RemapBuffer& remapBuffer, bool dropDownFound);
} }

View File

@ -6,12 +6,13 @@
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <common/themes/windows_colors.h> #include <common/themes/windows_colors.h>
#include <common/utils/EventLocker.h> #include <common/utils/EventLocker.h>
#include <common/utils/winapi_error.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include <KeyboardManagerConstants.h>
#include <KeyboardManagerState.h> #include <KeyboardManagerState.h>
#include "EditKeyboardWindow.h" #include "EditKeyboardWindow.h"
#include "ErrorTypes.h"
#include "SingleKeyRemapControl.h" #include "SingleKeyRemapControl.h"
#include "KeyDropDownControl.h" #include "KeyDropDownControl.h"
#include "XamlBridge.h" #include "XamlBridge.h"
@ -19,7 +20,8 @@
#include "Dialog.h" #include "Dialog.h"
#include "LoadingAndSavingRemappingHelper.h" #include "LoadingAndSavingRemappingHelper.h"
#include "UIHelpers.h" #include "UIHelpers.h"
#include <common/utils/winapi_error.h> #include "ShortcutErrorType.h"
#include "EditorConstants.h"
using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation;
@ -41,7 +43,7 @@ std::mutex editKeyboardWindowMutex;
static XamlBridge* xamlBridgePtr = nullptr; static XamlBridge* xamlBridgePtr = nullptr;
static IAsyncOperation<bool> OrphanKeysConfirmationDialog( static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
KeyboardManagerState& state, KBMEditor::KeyboardManagerState& state,
const std::vector<DWORD>& keys, const std::vector<DWORD>& keys,
XamlRoot root) XamlRoot root)
{ {
@ -73,11 +75,11 @@ static IAsyncOperation<bool> OrphanKeysConfirmationDialog(
co_return res == ContentDialogResult::Primary; co_return res == ContentDialogResult::Primary;
} }
static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings) static IAsyncAction OnClickAccept(KBMEditor::KeyboardManagerState& keyboardManagerState, XamlRoot root, std::function<void()> ApplyRemappings)
{ {
KeyboardManagerHelper::ErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer); ShortcutErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(SingleKeyRemapControl::singleKeyRemapBuffer);
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError) if (isSuccess != ShortcutErrorType::NoError)
{ {
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITKEYBOARD_PARTIALCONFIRMATIONDIALOGTITLE))) if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITKEYBOARD_PARTIALCONFIRMATIONDIALOGTITLE)))
{ {
@ -100,7 +102,7 @@ static IAsyncAction OnClickAccept(KeyboardManagerState& keyboardManagerState, Xa
} }
// Function to create the Edit Keyboard Window // Function to create the Edit Keyboard Window
inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{ {
Logger::trace("CreateEditKeyboardWindowImpl()"); Logger::trace("CreateEditKeyboardWindowImpl()");
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str()); auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
@ -142,8 +144,8 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect(); RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect();
// Calculate DPI dependent window size // Calculate DPI dependent window size
int windowWidth = KeyboardManagerConstants::DefaultEditKeyboardWindowWidth; int windowWidth = EditorConstants::DefaultEditKeyboardWindowWidth;
int windowHeight = KeyboardManagerConstants::DefaultEditKeyboardWindowHeight; int windowHeight = EditorConstants::DefaultEditKeyboardWindowHeight;
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight); DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
DPIAware::GetScreenDPIForCursor(g_currentDPI); DPIAware::GetScreenDPIForCursor(g_currentDPI);
@ -231,7 +233,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
TextBlock originalKeyRemapHeader; TextBlock originalKeyRemapHeader;
originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER)); originalKeyRemapHeader.Text(GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER));
originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold()); originalKeyRemapHeader.FontWeight(Text::FontWeights::Bold());
StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, KeyboardManagerConstants::RemapTableDropDownWidth + KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>(); StackPanel originalKeyHeaderContainer = UIHelpers::GetWrapped(originalKeyRemapHeader, EditorConstants::RemapTableDropDownWidth + EditorConstants::TableArrowColWidth).as<StackPanel>();
// Second header textblock in the header row of the keys remap table // Second header textblock in the header row of the keys remap table
TextBlock newKeyRemapHeader; TextBlock newKeyRemapHeader;
@ -250,6 +252,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
// Store keyboard manager state // Store keyboard manager state
SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState; SingleKeyRemapControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState; KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
// Clear the single key remap buffer // Clear the single key remap buffer
SingleKeyRemapControl::singleKeyRemapBuffer.clear(); SingleKeyRemapControl::singleKeyRemapBuffer.clear();
@ -258,10 +261,10 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects; std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>> keyboardRemapControlObjects;
// Set keyboard manager UI state so that remaps are not applied while on this window // Set keyboard manager UI state so that remaps are not applied while on this window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, _hWndEditKeyboardWindow);
// Load existing remaps into UI // Load existing remaps into UI
SingleKeyRemapTable singleKeyRemapCopy = keyboardManagerState.singleKeyReMap; SingleKeyRemapTable singleKeyRemapCopy = mappingConfiguration.singleKeyReMap;
LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyRemapCopy); LoadingAndSavingRemappingHelper::PreProcessRemapTable(singleKeyRemapCopy);
@ -274,14 +277,14 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
Button applyButton; Button applyButton;
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON))); applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
applyButton.Style(AccentButtonStyle()); applyButton.Style(AccentButtonStyle());
applyButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth); applyButton.MinWidth(EditorConstants::HeaderButtonWidth);
cancelButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth); cancelButton.MinWidth(EditorConstants::HeaderButtonWidth);
header.SetAlignRightWithPanel(cancelButton, true); header.SetAlignRightWithPanel(cancelButton, true);
header.SetLeftOf(applyButton, cancelButton); header.SetLeftOf(applyButton, cancelButton);
auto ApplyRemappings = [&keyboardManagerState, _hWndEditKeyboardWindow]() { auto ApplyRemappings = [&mappingConfiguration, _hWndEditKeyboardWindow]() {
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(keyboardManagerState, SingleKeyRemapControl::singleKeyRemapBuffer, true); LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(mappingConfiguration, SingleKeyRemapControl::singleKeyRemapBuffer, true);
bool saveResult = keyboardManagerState.SaveConfigToFile(); bool saveResult = mappingConfiguration.SaveSettingsToFile();
PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0); PostMessage(_hWndEditKeyboardWindow, WM_CLOSE, 0, 0);
}; };
@ -313,7 +316,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr); scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr);
// Set focus to the first Type Button in the newly added row // Set focus to the first Type Button in the newly added row
UIHelpers::SetFocusOnTypeButtonInLastRow(keyRemapTable, KeyboardManagerConstants::RemapTableColCount); UIHelpers::SetFocusOnTypeButtonInLastRow(keyRemapTable, EditorConstants::RemapTableColCount);
}); });
// Set accessible name for the addRemapKey button // Set accessible name for the addRemapKey button
@ -376,10 +379,10 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KeyboardManagerState&
xamlBridge.ClearXamlIslands(); xamlBridge.ClearXamlIslands();
} }
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) void CreateEditKeyboardWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{ {
// Move implementation into the separate method so resources get destroyed correctly // Move implementation into the separate method so resources get destroyed correctly
CreateEditKeyboardWindowImpl(hInst, keyboardManagerState); CreateEditKeyboardWindowImpl(hInst, keyboardManagerState, mappingConfiguration);
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent // Calling ClearXamlIslands() outside of the message loop is not enough to prevent
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906 // Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
@ -405,8 +408,8 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar
case WM_GETMINMAXINFO: case WM_GETMINMAXINFO:
{ {
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
int minWidth = KeyboardManagerConstants::MinimumEditKeyboardWindowWidth; int minWidth = EditorConstants::MinimumEditKeyboardWindowWidth;
int minHeight = KeyboardManagerConstants::MinimumEditKeyboardWindowHeight; int minHeight = EditorConstants::MinimumEditKeyboardWindowHeight;
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
mmi->ptMinTrackSize.x = minWidth; mmi->ptMinTrackSize.x = minWidth;
mmi->ptMinTrackSize.y = minHeight; mmi->ptMinTrackSize.y = minHeight;

View File

@ -1,8 +1,14 @@
#pragma once #pragma once
class KeyboardManagerState;
namespace KBMEditor
{
class KeyboardManagerState;
}
class MappingConfiguration;
// Function to create the Edit Keyboard Window // Function to create the Edit Keyboard Window
void CreateEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); void CreateEditKeyboardWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration);
// Function to check if there is already a window active if yes bring to foreground // Function to check if there is already a window active if yes bring to foreground
bool CheckEditKeyboardWindowActive(); bool CheckEditKeyboardWindowActive();

View File

@ -3,18 +3,19 @@
#include <common/Display/dpi_aware.h> #include <common/Display/dpi_aware.h>
#include <common/utils/EventLocker.h> #include <common/utils/EventLocker.h>
#include <KeyboardManagerState.h>
#include <Dialog.h>
#include <ErrorTypes.h>
#include <KeyDropDownControl.h>
#include <LoadingAndSavingRemappingHelper.h>
#include <ShortcutControl.h>
#include <Styles.h>
#include <UIHelpers.h>
#include <XamlBridge.h>
#include <common/utils/winapi_error.h> #include <common/utils/winapi_error.h>
#include <keyboardmanager/common/MappingConfiguration.h>
#include "KeyboardManagerState.h"
#include "Dialog.h"
#include "KeyDropDownControl.h"
#include "LoadingAndSavingRemappingHelper.h"
#include "ShortcutControl.h"
#include "Styles.h"
#include "UIHelpers.h"
#include "XamlBridge.h"
#include "ShortcutErrorType.h"
#include "EditorConstants.h"
using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation;
@ -36,13 +37,13 @@ std::mutex editShortcutsWindowMutex;
static XamlBridge* xamlBridgePtr = nullptr; static XamlBridge* xamlBridgePtr = nullptr;
static IAsyncAction OnClickAccept( static IAsyncAction OnClickAccept(
KeyboardManagerState& keyboardManagerState, KBMEditor::KeyboardManagerState& keyboardManagerState,
XamlRoot root, XamlRoot root,
std::function<void()> ApplyRemappings) std::function<void()> ApplyRemappings)
{ {
KeyboardManagerHelper::ErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer); ShortcutErrorType isSuccess = LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(ShortcutControl::shortcutRemapBuffer);
if (isSuccess != KeyboardManagerHelper::ErrorType::NoError) if (isSuccess != ShortcutErrorType::NoError)
{ {
if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_PARTIALCONFIRMATIONDIALOGTITLE))) if (!co_await Dialog::PartialRemappingConfirmationDialog(root, GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_PARTIALCONFIRMATIONDIALOGTITLE)))
{ {
@ -53,7 +54,7 @@ static IAsyncAction OnClickAccept(
} }
// Function to create the Edit Shortcuts Window // Function to create the Edit Shortcuts Window
inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{ {
Logger::trace("CreateEditShortcutsWindowImpl()"); Logger::trace("CreateEditShortcutsWindowImpl()");
auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str()); auto locker = EventLocker::Get(KeyboardManagerConstants::EditorWindowEventName.c_str());
@ -95,8 +96,8 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect(); RECT desktopRect = UIHelpers::GetForegroundWindowDesktopRect();
// Calculate DPI dependent window size // Calculate DPI dependent window size
int windowWidth = KeyboardManagerConstants::DefaultEditShortcutsWindowWidth; int windowWidth = EditorConstants::DefaultEditShortcutsWindowWidth;
int windowHeight = KeyboardManagerConstants::DefaultEditShortcutsWindowHeight; int windowHeight = EditorConstants::DefaultEditShortcutsWindowHeight;
DPIAware::ConvertByCursorPosition(windowWidth, windowHeight); DPIAware::ConvertByCursorPosition(windowWidth, windowHeight);
DPIAware::GetScreenDPIForCursor(g_currentDPI); DPIAware::GetScreenDPIForCursor(g_currentDPI);
@ -198,9 +199,9 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
StackPanel tableHeader = StackPanel(); StackPanel tableHeader = StackPanel();
tableHeader.Orientation(Orientation::Horizontal); tableHeader.Orientation(Orientation::Horizontal);
tableHeader.Margin({ 10, 0, 0, 10 }); tableHeader.Margin({ 10, 0, 0, 10 });
auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, KeyboardManagerConstants::ShortcutOriginColumnWidth + (double)KeyboardManagerConstants::ShortcutArrowColumnWidth); auto originalShortcutContainer = UIHelpers::GetWrapped(originalShortcutHeader, EditorConstants::ShortcutOriginColumnWidth + (double)EditorConstants::ShortcutArrowColumnWidth);
tableHeader.Children().Append(originalShortcutContainer.as<FrameworkElement>()); tableHeader.Children().Append(originalShortcutContainer.as<FrameworkElement>());
auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, KeyboardManagerConstants::ShortcutTargetColumnWidth); auto newShortcutHeaderContainer = UIHelpers::GetWrapped(newShortcutHeader, EditorConstants::ShortcutTargetColumnWidth);
tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>()); tableHeader.Children().Append(newShortcutHeaderContainer.as<FrameworkElement>());
tableHeader.Children().Append(targetAppHeader); tableHeader.Children().Append(targetAppHeader);
@ -210,6 +211,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
// Store keyboard manager state // Store keyboard manager state
ShortcutControl::keyboardManagerState = &keyboardManagerState; ShortcutControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::keyboardManagerState = &keyboardManagerState; KeyDropDownControl::keyboardManagerState = &keyboardManagerState;
KeyDropDownControl::mappingConfiguration = &mappingConfiguration;
// Clear the shortcut remap buffer // Clear the shortcut remap buffer
ShortcutControl::shortcutRemapBuffer.clear(); ShortcutControl::shortcutRemapBuffer.clear();
@ -218,11 +220,11 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects; std::vector<std::vector<std::unique_ptr<ShortcutControl>>> keyboardRemapControlObjects;
// Set keyboard manager UI state so that shortcut remaps are not applied while on this window // Set keyboard manager UI state so that shortcut remaps are not applied while on this window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, _hWndEditShortcutsWindow);
// Load existing os level shortcuts into UI // Load existing os level shortcuts into UI
// Create copy of the remaps to avoid concurrent access // Create copy of the remaps to avoid concurrent access
ShortcutRemapTable osLevelShortcutReMapCopy = keyboardManagerState.osLevelShortcutReMap; ShortcutRemapTable osLevelShortcutReMapCopy = mappingConfiguration.osLevelShortcutReMap;
for (const auto& it : osLevelShortcutReMapCopy) for (const auto& it : osLevelShortcutReMapCopy)
{ {
@ -231,7 +233,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
// Load existing app-specific shortcuts into UI // Load existing app-specific shortcuts into UI
// Create copy of the remaps to avoid concurrent access // Create copy of the remaps to avoid concurrent access
AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = keyboardManagerState.appSpecificShortcutReMap; AppSpecificShortcutRemapTable appSpecificShortcutReMapCopy = mappingConfiguration.appSpecificShortcutReMap;
// Iterate through all the apps // Iterate through all the apps
for (const auto& itApp : appSpecificShortcutReMapCopy) for (const auto& itApp : appSpecificShortcutReMapCopy)
@ -247,14 +249,14 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
Button applyButton; Button applyButton;
applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON))); applyButton.Content(winrt::box_value(GET_RESOURCE_STRING(IDS_OK_BUTTON)));
applyButton.Style(AccentButtonStyle()); applyButton.Style(AccentButtonStyle());
applyButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth); applyButton.MinWidth(EditorConstants::HeaderButtonWidth);
cancelButton.MinWidth(KeyboardManagerConstants::HeaderButtonWidth); cancelButton.MinWidth(EditorConstants::HeaderButtonWidth);
header.SetAlignRightWithPanel(cancelButton, true); header.SetAlignRightWithPanel(cancelButton, true);
header.SetLeftOf(applyButton, cancelButton); header.SetLeftOf(applyButton, cancelButton);
auto ApplyRemappings = [&keyboardManagerState, _hWndEditShortcutsWindow]() { auto ApplyRemappings = [&mappingConfiguration, _hWndEditShortcutsWindow]() {
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(keyboardManagerState, ShortcutControl::shortcutRemapBuffer, true); LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(mappingConfiguration, ShortcutControl::shortcutRemapBuffer, true);
bool saveResult = keyboardManagerState.SaveConfigToFile(); bool saveResult = mappingConfiguration.SaveSettingsToFile();
PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0); PostMessage(_hWndEditShortcutsWindow, WM_CLOSE, 0, 0);
}; };
@ -286,7 +288,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr); scrollViewer.ChangeView(nullptr, scrollViewer.ScrollableHeight(), nullptr);
// Set focus to the first Type Button in the newly added row // Set focus to the first Type Button in the newly added row
UIHelpers::SetFocusOnTypeButtonInLastRow(shortcutTable, KeyboardManagerConstants::ShortcutTableColCount); UIHelpers::SetFocusOnTypeButtonInLastRow(shortcutTable, EditorConstants::ShortcutTableColCount);
}); });
// Set accessible name for the add shortcut button // Set accessible name for the add shortcut button
@ -349,10 +351,10 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KeyboardManagerState&
xamlBridge.ClearXamlIslands(); xamlBridge.ClearXamlIslands();
} }
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration)
{ {
// Move implementation into the separate method so resources get destroyed correctly // Move implementation into the separate method so resources get destroyed correctly
CreateEditShortcutsWindowImpl(hInst, keyboardManagerState); CreateEditShortcutsWindowImpl(hInst, keyboardManagerState, mappingConfiguration);
// Calling ClearXamlIslands() outside of the message loop is not enough to prevent // Calling ClearXamlIslands() outside of the message loop is not enough to prevent
// Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906 // Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906
@ -378,8 +380,8 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa
case WM_GETMINMAXINFO: case WM_GETMINMAXINFO:
{ {
LPMINMAXINFO mmi = (LPMINMAXINFO)lParam; LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
int minWidth = KeyboardManagerConstants::MinimumEditShortcutsWindowWidth; int minWidth = EditorConstants::MinimumEditShortcutsWindowWidth;
int minHeight = KeyboardManagerConstants::MinimumEditShortcutsWindowHeight; int minHeight = EditorConstants::MinimumEditShortcutsWindowHeight;
DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight); DPIAware::Convert(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL), minWidth, minHeight);
mmi->ptMinTrackSize.x = minWidth; mmi->ptMinTrackSize.x = minWidth;
mmi->ptMinTrackSize.y = minHeight; mmi->ptMinTrackSize.y = minHeight;

View File

@ -1,8 +1,14 @@
#pragma once #pragma once
class KeyboardManagerState;
namespace KBMEditor
{
class KeyboardManagerState;
}
class MappingConfiguration;
// Function to create the Edit Shortcuts Window // Function to create the Edit Shortcuts Window
void CreateEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration);
// Function to check if there is already a window active if yes bring to foreground // Function to check if there is already a window active if yes bring to foreground
bool CheckEditShortcutsWindowActive(); bool CheckEditShortcutsWindowActive();

View File

@ -0,0 +1,53 @@
#pragma once
namespace EditorConstants
{
// Default window sizes
inline const int DefaultEditKeyboardWindowWidth = 800;
inline const int DefaultEditKeyboardWindowHeight = 600;
inline const int MinimumEditKeyboardWindowWidth = 500;
inline const int MinimumEditKeyboardWindowHeight = 450;
inline const int EditKeyboardTableMinWidth = 700;
inline const int DefaultEditShortcutsWindowWidth = 1050;
inline const int DefaultEditShortcutsWindowHeight = 600;
inline const int MinimumEditShortcutsWindowWidth = 500;
inline const int MinimumEditShortcutsWindowHeight = 500;
inline const int EditShortcutsTableMinWidth = 1000;
// Key Remap table constants
inline const long RemapTableColCount = 4;
inline const long RemapTableHeaderCount = 2;
inline const long RemapTableOriginalColIndex = 0;
inline const long RemapTableArrowColIndex = 1;
inline const long RemapTableNewColIndex = 2;
inline const long RemapTableRemoveColIndex = 3;
inline const DWORD64 RemapTableDropDownWidth = 110;
// Shortcut table constants
inline const long ShortcutTableColCount = 5;
inline const long ShortcutTableHeaderCount = 3;
inline const long ShortcutTableOriginalColIndex = 0;
inline const long ShortcutTableArrowColIndex = 1;
inline const long ShortcutTableNewColIndex = 2;
inline const long ShortcutTableTargetAppColIndex = 3;
inline const long ShortcutTableRemoveColIndex = 4;
inline const long ShortcutArrowColumnWidth = 90;
inline const DWORD64 ShortcutTableDropDownWidth = 110;
inline const DWORD64 ShortcutTableDropDownSpacing = 10;
inline const long ShortcutOriginColumnWidth = 3 * ShortcutTableDropDownWidth + 2 * ShortcutTableDropDownSpacing;
inline const long ShortcutTargetColumnWidth = 3 * ShortcutTableDropDownWidth + 2 * ShortcutTableDropDownSpacing + 25;
// Drop down height used for both Edit Keyboard and Edit Shortcuts
inline const DWORD64 TableDropDownHeight = 200;
inline const DWORD64 TableArrowColWidth = 230;
inline const DWORD64 TableRemoveColWidth = 20;
inline const DWORD64 TableWarningColWidth = 20;
inline const DWORD64 TableTargetAppColWidth = ShortcutTableDropDownWidth + TableRemoveColWidth * 2;
// Shared style constants for both Remap Table and Shortcut Table
inline const DWORD64 HeaderButtonWidth = 100;
// Minimum and maximum size of a shortcut
inline const long MinShortcutSize = 2;
inline const long MaxShortcutSize = 3;
}

View File

@ -0,0 +1,144 @@
#include "pch.h"
#include <common/interop/keyboard_layout.h>
#include <keyboardmanager/common/Helpers.h>
#include "ShortcutErrorType.h"
using Helpers::GetKeyType;
namespace EditorHelpers
{
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoKeysOverlap(DWORD first, DWORD second)
{
// If the keys are same
if (first == second)
{
return ShortcutErrorType::SameKeyPreviouslyMapped;
}
else if ((GetKeyType(first) == GetKeyType(second)) && GetKeyType(first) != Helpers::KeyType::Action)
{
// If the keys are of the same modifier type and overlapping, i.e. one is L/R and other is common
if (((first == VK_LWIN && second == VK_RWIN) || (first == VK_RWIN && second == VK_LWIN)) || ((first == VK_LCONTROL && second == VK_RCONTROL) || (first == VK_RCONTROL && second == VK_LCONTROL)) || ((first == VK_LMENU && second == VK_RMENU) || (first == VK_RMENU && second == VK_LMENU)) || ((first == VK_LSHIFT && second == VK_RSHIFT) || (first == VK_RSHIFT && second == VK_LSHIFT)))
{
return ShortcutErrorType::NoError;
}
else
{
return ShortcutErrorType::ConflictingModifierKey;
}
}
// If no overlap
else
{
return ShortcutErrorType::NoError;
}
}
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(const std::vector<int32_t>& currentKeys, int selectedKeyCode)
{
// Count the number of keys that are equal to 'selectedKeyCode'
int numberOfSameType = 0;
for (int i = 0; i < currentKeys.size(); i++)
{
numberOfSameType += Helpers::GetKeyType(selectedKeyCode) == Helpers::GetKeyType(currentKeys[i]);
}
// If we have at least two keys equal to 'selectedKeyCode' than modifier was repeated
return numberOfSameType > 1;
}
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
bool IsValidShortcut(Shortcut shortcut)
{
if (shortcut.actionKey != NULL)
{
if (shortcut.winKey != ModifierKey::Disabled || shortcut.ctrlKey != ModifierKey::Disabled || shortcut.altKey != ModifierKey::Disabled || shortcut.shiftKey != ModifierKey::Disabled)
{
return true;
}
}
return false;
}
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoShortcutsOverlap(const Shortcut& first, const Shortcut& second)
{
if (IsValidShortcut(first) && IsValidShortcut(second))
{
// If the shortcuts are equal
if (first == second)
{
return ShortcutErrorType::SameShortcutPreviouslyMapped;
}
// action keys match
else if (first.actionKey == second.actionKey)
{
// corresponding modifiers are either both disabled or both not disabled - this ensures that both match in types of modifiers i.e. Ctrl(l/r/c) Shift (l/r/c) A matches Ctrl(l/r/c) Shift (l/r/c) A
if (((first.winKey != ModifierKey::Disabled && second.winKey != ModifierKey::Disabled) || (first.winKey == ModifierKey::Disabled && second.winKey == ModifierKey::Disabled)) &&
((first.ctrlKey != ModifierKey::Disabled && second.ctrlKey != ModifierKey::Disabled) || (first.ctrlKey == ModifierKey::Disabled && second.ctrlKey == ModifierKey::Disabled)) &&
((first.altKey != ModifierKey::Disabled && second.altKey != ModifierKey::Disabled) || (first.altKey == ModifierKey::Disabled && second.altKey == ModifierKey::Disabled)) &&
((first.shiftKey != ModifierKey::Disabled && second.shiftKey != ModifierKey::Disabled) || (first.shiftKey == ModifierKey::Disabled && second.shiftKey == ModifierKey::Disabled)))
{
// If one of the modifier is common
if ((first.winKey == ModifierKey::Both || second.winKey == ModifierKey::Both) ||
(first.ctrlKey == ModifierKey::Both || second.ctrlKey == ModifierKey::Both) ||
(first.altKey == ModifierKey::Both || second.altKey == ModifierKey::Both) ||
(first.shiftKey == ModifierKey::Both || second.shiftKey == ModifierKey::Both))
{
return ShortcutErrorType::ConflictingModifierShortcut;
}
}
}
}
return ShortcutErrorType::NoError;
}
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> GetKeyVector(Shortcut shortcut, LayoutMap& keyboardMap)
{
std::vector<winrt::hstring> keys;
if (shortcut.winKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetWinKey(ModifierKey::Both)).c_str()));
}
if (shortcut.ctrlKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetCtrlKey()).c_str()));
}
if (shortcut.altKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetAltKey()).c_str()));
}
if (shortcut.shiftKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.GetShiftKey()).c_str()));
}
if (shortcut.actionKey != NULL)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(shortcut.actionKey).c_str()));
}
return keys;
}
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
ShortcutErrorType IsShortcutIllegal(Shortcut shortcut)
{
// Win+L
if (shortcut.winKey != ModifierKey::Disabled && shortcut.ctrlKey == ModifierKey::Disabled && shortcut.altKey == ModifierKey::Disabled && shortcut.shiftKey == ModifierKey::Disabled && shortcut.actionKey == 0x4C)
{
return ShortcutErrorType::WinL;
}
// Ctrl+Alt+Del
if (shortcut.winKey == ModifierKey::Disabled && shortcut.ctrlKey != ModifierKey::Disabled && shortcut.altKey != ModifierKey::Disabled && shortcut.shiftKey == ModifierKey::Disabled && shortcut.actionKey == VK_DELETE)
{
return ShortcutErrorType::CtrlAltDel;
}
return ShortcutErrorType::NoError;
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <keyboardmanager/common/Shortcut.h>
#include "ShortcutErrorType.h"
namespace EditorHelpers
{
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoKeysOverlap(DWORD first, DWORD second);
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(const std::vector<int32_t>& currentKeys, int selectedKeyCodes);
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
bool IsValidShortcut(Shortcut shortcut);
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
ShortcutErrorType DoShortcutsOverlap(const Shortcut& first, const Shortcut& second);
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> GetKeyVector(Shortcut shortcut, LayoutMap& keyboardMap);
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
ShortcutErrorType IsShortcutIllegal(Shortcut shortcut);
}

View File

@ -1,166 +1,166 @@
#include "pch.h" #include "pch.h"
#include "KeyDelay.h" #include "KeyDelay.h"
// NOTE: The destructor should never be called on the DelayThread, i.e. from any of shortPress, longPress or longPressReleased, as it will re-enter the mutex. Even if the mutex is removed it will deadlock because of the join statement // NOTE: The destructor should never be called on the DelayThread, i.e. from any of shortPress, longPress or longPressReleased, as it will re-enter the mutex. Even if the mutex is removed it will deadlock because of the join statement
KeyDelay::~KeyDelay() KeyDelay::~KeyDelay()
{ {
std::unique_lock<std::mutex> l(_queueMutex); std::unique_lock<std::mutex> l(_queueMutex);
_quit = true; _quit = true;
_cv.notify_all(); _cv.notify_all();
l.unlock(); l.unlock();
_delayThread.join(); _delayThread.join();
} }
void KeyDelay::KeyEvent(LowlevelKeyboardEvent* ev) void KeyDelay::KeyEvent(LowlevelKeyboardEvent* ev)
{ {
std::lock_guard guard(_queueMutex); std::lock_guard guard(_queueMutex);
_queue.push({ ev->lParam->time, ev->wParam }); _queue.push({ ev->lParam->time, ev->wParam });
_cv.notify_all(); _cv.notify_all();
} }
KeyTimedEvent KeyDelay::NextEvent() KeyTimedEvent KeyDelay::NextEvent()
{ {
auto ev = _queue.front(); auto ev = _queue.front();
_queue.pop(); _queue.pop();
return ev; return ev;
} }
bool KeyDelay::CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration) bool KeyDelay::CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration)
{ {
if (first < last && first <= first + duration) if (first < last && first <= first + duration)
{ {
return first + duration < last; return first + duration < last;
} }
else else
{ {
first += ULLONG_MAX / 2; first += ULLONG_MAX / 2;
last += ULLONG_MAX / 2; last += ULLONG_MAX / 2;
return first + duration < last; return first + duration < last;
} }
} }
bool KeyDelay::HasNextEvent() bool KeyDelay::HasNextEvent()
{ {
return !_queue.empty(); return !_queue.empty();
} }
bool KeyDelay::HandleRelease() bool KeyDelay::HandleRelease()
{ {
while (HasNextEvent()) while (HasNextEvent())
{ {
auto ev = NextEvent(); auto ev = NextEvent();
switch (ev.message) switch (ev.message)
{ {
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_SYSKEYDOWN: case WM_SYSKEYDOWN:
_state = KeyDelayState::ON_HOLD; _state = KeyDelayState::ON_HOLD;
_initialHoldKeyDown = ev.time; _initialHoldKeyDown = ev.time;
return false; return false;
case WM_KEYUP: case WM_KEYUP:
case WM_SYSKEYUP: case WM_SYSKEYUP:
break; break;
} }
} }
return true; return true;
} }
bool KeyDelay::HandleOnHold(std::unique_lock<std::mutex>& cvLock) bool KeyDelay::HandleOnHold(std::unique_lock<std::mutex>& cvLock)
{ {
while (HasNextEvent()) while (HasNextEvent())
{ {
auto ev = NextEvent(); auto ev = NextEvent();
switch (ev.message) switch (ev.message)
{ {
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_SYSKEYDOWN: case WM_SYSKEYDOWN:
break; break;
case WM_KEYUP: case WM_KEYUP:
case WM_SYSKEYUP: case WM_SYSKEYUP:
if (CheckIfMillisHaveElapsed(_initialHoldKeyDown, ev.time, LONG_PRESS_DELAY_MILLIS)) if (CheckIfMillisHaveElapsed(_initialHoldKeyDown, ev.time, LONG_PRESS_DELAY_MILLIS))
{ {
if (_onLongPressDetected != nullptr) if (_onLongPressDetected != nullptr)
{ {
_onLongPressDetected(_key); _onLongPressDetected(_key);
} }
if (_onLongPressReleased != nullptr) if (_onLongPressReleased != nullptr)
{ {
_onLongPressReleased(_key); _onLongPressReleased(_key);
} }
} }
else else
{ {
if (_onShortPress != nullptr) if (_onShortPress != nullptr)
{ {
_onShortPress(_key); _onShortPress(_key);
} }
} }
_state = KeyDelayState::RELEASED; _state = KeyDelayState::RELEASED;
return false; return false;
} }
} }
if (CheckIfMillisHaveElapsed(_initialHoldKeyDown, GetTickCount64(), LONG_PRESS_DELAY_MILLIS)) if (CheckIfMillisHaveElapsed(_initialHoldKeyDown, GetTickCount64(), LONG_PRESS_DELAY_MILLIS))
{ {
if (_onLongPressDetected != nullptr) if (_onLongPressDetected != nullptr)
{ {
_onLongPressDetected(_key); _onLongPressDetected(_key);
} }
_state = KeyDelayState::ON_HOLD_TIMEOUT; _state = KeyDelayState::ON_HOLD_TIMEOUT;
} }
else else
{ {
_cv.wait_for(cvLock, std::chrono::milliseconds(ON_HOLD_WAIT_TIMEOUT_MILLIS)); _cv.wait_for(cvLock, std::chrono::milliseconds(ON_HOLD_WAIT_TIMEOUT_MILLIS));
} }
return false; return false;
} }
bool KeyDelay::HandleOnHoldTimeout() bool KeyDelay::HandleOnHoldTimeout()
{ {
while (HasNextEvent()) while (HasNextEvent())
{ {
auto ev = NextEvent(); auto ev = NextEvent();
switch (ev.message) switch (ev.message)
{ {
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_SYSKEYDOWN: case WM_SYSKEYDOWN:
break; break;
case WM_KEYUP: case WM_KEYUP:
case WM_SYSKEYUP: case WM_SYSKEYUP:
if (_onLongPressReleased != nullptr) if (_onLongPressReleased != nullptr)
{ {
_onLongPressReleased(_key); _onLongPressReleased(_key);
} }
_state = KeyDelayState::RELEASED; _state = KeyDelayState::RELEASED;
return false; return false;
} }
} }
return true; return true;
} }
void KeyDelay::DelayThread() void KeyDelay::DelayThread()
{ {
std::unique_lock<std::mutex> qLock(_queueMutex); std::unique_lock<std::mutex> qLock(_queueMutex);
bool shouldWait = true; bool shouldWait = true;
while (!_quit) while (!_quit)
{ {
if (shouldWait) if (shouldWait)
{ {
_cv.wait(qLock); _cv.wait(qLock);
} }
switch (_state) switch (_state)
{ {
case KeyDelayState::RELEASED: case KeyDelayState::RELEASED:
shouldWait = HandleRelease(); shouldWait = HandleRelease();
break; break;
case KeyDelayState::ON_HOLD: case KeyDelayState::ON_HOLD:
shouldWait = HandleOnHold(qLock); shouldWait = HandleOnHold(qLock);
break; break;
case KeyDelayState::ON_HOLD_TIMEOUT: case KeyDelayState::ON_HOLD_TIMEOUT:
shouldWait = HandleOnHoldTimeout(); shouldWait = HandleOnHoldTimeout();
break; break;
} }
} }
} }

View File

@ -1,93 +1,93 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <thread> #include <thread>
#include <queue> #include <queue>
#include <mutex> #include <mutex>
#include <common/hooks/LowlevelKeyboardEvent.h> #include <common/hooks/LowlevelKeyboardEvent.h>
// Available states for the KeyDelay state machine. // Available states for the KeyDelay state machine.
enum class KeyDelayState enum class KeyDelayState
{ {
RELEASED, RELEASED,
ON_HOLD, ON_HOLD,
ON_HOLD_TIMEOUT, ON_HOLD_TIMEOUT,
}; };
// Virtual key + timestamp (in millis since Windows startup) // Virtual key + timestamp (in millis since Windows startup)
struct KeyTimedEvent struct KeyTimedEvent
{ {
DWORD64 time; DWORD64 time;
WPARAM message; WPARAM message;
}; };
// Handles delayed key inputs. // Handles delayed key inputs.
// Implemented as a state machine running on its own thread. // Implemented as a state machine running on its own thread.
// Thread stops on destruction. // Thread stops on destruction.
class KeyDelay class KeyDelay
{ {
public: public:
KeyDelay( KeyDelay(
DWORD key, DWORD key,
std::function<void(DWORD)> onShortPress, std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected, std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased) : std::function<void(DWORD)> onLongPressReleased) :
_quit(false), _quit(false),
_state(KeyDelayState::RELEASED), _state(KeyDelayState::RELEASED),
_initialHoldKeyDown(0), _initialHoldKeyDown(0),
_key(key), _key(key),
_onShortPress(onShortPress), _onShortPress(onShortPress),
_onLongPressDetected(onLongPressDetected), _onLongPressDetected(onLongPressDetected),
_onLongPressReleased(onLongPressReleased), _onLongPressReleased(onLongPressReleased),
_delayThread(&KeyDelay::DelayThread, this){}; _delayThread(&KeyDelay::DelayThread, this){};
// Enque new KeyTimedEvent and notify the condition variable. // Enque new KeyTimedEvent and notify the condition variable.
void KeyEvent(LowlevelKeyboardEvent* ev); void KeyEvent(LowlevelKeyboardEvent* ev);
~KeyDelay(); ~KeyDelay();
private: private:
// Runs the state machine, waits if there is no events to process. // Runs the state machine, waits if there is no events to process.
// Checks for _quit condition. // Checks for _quit condition.
void DelayThread(); void DelayThread();
// Manage state transitions and trigger callbacks on certain events. // Manage state transitions and trigger callbacks on certain events.
// Returns whether or not the thread should wait on new events. // Returns whether or not the thread should wait on new events.
bool HandleRelease(); bool HandleRelease();
bool HandleOnHold(std::unique_lock<std::mutex>& cvLock); bool HandleOnHold(std::unique_lock<std::mutex>& cvLock);
bool HandleOnHoldTimeout(); bool HandleOnHoldTimeout();
// Get next key event in queue. // Get next key event in queue.
KeyTimedEvent NextEvent(); KeyTimedEvent NextEvent();
bool HasNextEvent(); bool HasNextEvent();
// Check if <duration> milliseconds passed since <first> millisecond. // Check if <duration> milliseconds passed since <first> millisecond.
// Also checks for overflow conditions. // Also checks for overflow conditions.
bool CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration); bool CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration);
bool _quit; bool _quit;
KeyDelayState _state; KeyDelayState _state;
// Callback functions, the key provided in the constructor is passed as an argument. // Callback functions, the key provided in the constructor is passed as an argument.
std::function<void(DWORD)> _onLongPressDetected; std::function<void(DWORD)> _onLongPressDetected;
std::function<void(DWORD)> _onLongPressReleased; std::function<void(DWORD)> _onLongPressReleased;
std::function<void(DWORD)> _onShortPress; std::function<void(DWORD)> _onShortPress;
// Queue holding key events that are not processed yet. Should be kept synchronized // Queue holding key events that are not processed yet. Should be kept synchronized
// using _queueMutex // using _queueMutex
std::queue<KeyTimedEvent> _queue; std::queue<KeyTimedEvent> _queue;
std::mutex _queueMutex; std::mutex _queueMutex;
// DelayThread waits on this condition variable when there is no events to process. // DelayThread waits on this condition variable when there is no events to process.
std::condition_variable _cv; std::condition_variable _cv;
// Keeps track of the time at which the initial KEY_DOWN event happened. // Keeps track of the time at which the initial KEY_DOWN event happened.
DWORD64 _initialHoldKeyDown; DWORD64 _initialHoldKeyDown;
// Virtual Key provided in the constructor. Passed to callback functions. // Virtual Key provided in the constructor. Passed to callback functions.
DWORD _key; DWORD _key;
// Declare _delayThread after all other members so that it is the last to be initialized by the constructor // Declare _delayThread after all other members so that it is the last to be initialized by the constructor
std::thread _delayThread; std::thread _delayThread;
static const DWORD64 LONG_PRESS_DELAY_MILLIS = 900; static const DWORD64 LONG_PRESS_DELAY_MILLIS = 900;
static const DWORD64 ON_HOLD_WAIT_TIMEOUT_MILLIS = 50; static const DWORD64 ON_HOLD_WAIT_TIMEOUT_MILLIS = 50;
}; };

View File

@ -2,15 +2,20 @@
#include "KeyDropDownControl.h" #include "KeyDropDownControl.h"
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <KeyboardManagerState.h>
#include <BufferValidationHelpers.h> #include <keyboardmanager/common/MappingConfiguration.h>
#include <KeyboardManagerEditorStrings.h>
#include <ErrorTypes.h> #include "KeyboardManagerState.h"
#include <UIHelpers.h> #include "BufferValidationHelpers.h"
#include "KeyboardManagerEditorStrings.h"
#include "UIHelpers.h"
#include "EditorHelpers.h"
#include "ShortcutErrorType.h"
#include "EditorConstants.h"
// Initialized to null // Initialized to null
KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr; KBMEditor::KeyboardManagerState* KeyDropDownControl::keyboardManagerState = nullptr;
MappingConfiguration* KeyDropDownControl::mappingConfiguration = nullptr;
// Get selected value of dropdown or -1 if nothing is selected // Get selected value of dropdown or -1 if nothing is selected
DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox) DWORD KeyDropDownControl::GetSelectedValue(ComboBox comboBox)
@ -51,14 +56,14 @@ void KeyDropDownControl::SetDefaultProperties(bool isShortcut, bool renderDisabl
if (!isShortcut) if (!isShortcut)
{ {
dropDown.as<ComboBox>().Width(KeyboardManagerConstants::RemapTableDropDownWidth); dropDown.as<ComboBox>().Width(EditorConstants::RemapTableDropDownWidth);
} }
else else
{ {
dropDown.as<ComboBox>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth); dropDown.as<ComboBox>().Width(EditorConstants::ShortcutTableDropDownWidth);
} }
dropDown.as<ComboBox>().MaxDropDownHeight(KeyboardManagerConstants::TableDropDownHeight); dropDown.as<ComboBox>().MaxDropDownHeight(EditorConstants::TableDropDownHeight);
// Initialise layout attribute // Initialise layout attribute
previousLayout = GetKeyboardLayout(0); previousLayout = GetKeyboardLayout(0);
@ -121,10 +126,10 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
int selectedKeyCode = GetSelectedValue(currentDropDown); int selectedKeyCode = GetSelectedValue(currentDropDown);
// Validate current remap selection // Validate current remap selection
KeyboardManagerHelper::ErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer); ShortcutErrorType errorType = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(rowIndex, colIndex, selectedKeyCode, singleKeyRemapBuffer);
// If there is an error set the warning flyout // If there is an error set the warning flyout
if (errorType != KeyboardManagerHelper::ErrorType::NoError) if (errorType != ShortcutErrorType::NoError)
{ {
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(errorType)); SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(errorType));
} }
@ -145,12 +150,12 @@ void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row,
}); });
} }
std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow) std::pair<ShortcutErrorType, int> KeyDropDownControl::ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
{ {
ComboBox currentDropDown = dropDown.as<ComboBox>(); ComboBox currentDropDown = dropDown.as<ComboBox>();
uint32_t dropDownIndex = -1; uint32_t dropDownIndex = -1;
bool dropDownFound = parent.Children().IndexOf(currentDropDown, dropDownIndex); bool dropDownFound = parent.Children().IndexOf(currentDropDown, dropDownIndex);
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> validationResult = std::make_pair(KeyboardManagerHelper::ErrorType::NoError, BufferValidationHelpers::DropDownAction::NoAction); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> validationResult = std::make_pair(ShortcutErrorType::NoError, BufferValidationHelpers::DropDownAction::NoAction);
uint32_t rowIndex; uint32_t rowIndex;
bool controlIindexFound = table.Children().IndexOf(row, rowIndex); bool controlIindexFound = table.Children().IndexOf(row, rowIndex);
@ -185,13 +190,13 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
} }
// If ignore key to shortcut warning flag is true and it is a hybrid control in SingleKeyRemapControl, then skip MapToSameKey error // If ignore key to shortcut warning flag is true and it is a hybrid control in SingleKeyRemapControl, then skip MapToSameKey error
if (isHybridControl && isSingleKeyWindow && ignoreKeyToShortcutWarning && (validationResult.first == KeyboardManagerHelper::ErrorType::MapToSameKey)) if (isHybridControl && isSingleKeyWindow && ignoreKeyToShortcutWarning && (validationResult.first == ShortcutErrorType::MapToSameKey))
{ {
validationResult.first = KeyboardManagerHelper::ErrorType::NoError; validationResult.first = ShortcutErrorType::NoError;
} }
// If the remapping is invalid display an error message // If the remapping is invalid display an error message
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError) if (validationResult.first != ShortcutErrorType::NoError)
{ {
SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(validationResult.first)); SetDropDownError(currentDropDown, KeyboardManagerEditorStrings::GetErrorMessage(validationResult.first));
} }
@ -227,13 +232,13 @@ std::pair<KeyboardManagerHelper::ErrorType, int> KeyDropDownControl::ValidateSho
void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow) void KeyDropDownControl::SetSelectionHandler(StackPanel& table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow)
{ {
auto onSelectionChange = [&, table, row, colIndex, parent, targetApp, isHybridControl, isSingleKeyWindow](winrt::Windows::Foundation::IInspectable const& sender) { auto onSelectionChange = [&, table, row, colIndex, parent, targetApp, isHybridControl, isSingleKeyWindow](winrt::Windows::Foundation::IInspectable const& sender) {
std::pair<KeyboardManagerHelper::ErrorType, int> validationResult = ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); std::pair<ShortcutErrorType, int> validationResult = ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
// Check if the drop down row index was identified from the return value of validateSelection // Check if the drop down row index was identified from the return value of validateSelection
if (validationResult.second != -1) if (validationResult.second != -1)
{ {
// If an error occurred // If an error occurred
if (validationResult.first != KeyboardManagerHelper::ErrorType::NoError) if (validationResult.first != ShortcutErrorType::NoError)
{ {
// Validate all the drop downs // Validate all the drop downs
ValidateShortcutFromDropDownList(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); ValidateShortcutFromDropDownList(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
@ -362,7 +367,7 @@ void KeyDropDownControl::ValidateShortcutFromDropDownList(StackPanel table, Stac
} }
// If the key/shortcut is valid and that drop down is not empty // If the key/shortcut is valid and that drop down is not empty
if (((currentShortcut.index() == 0 && std::get<DWORD>(currentShortcut) != NULL) || (currentShortcut.index() == 1 && std::get<Shortcut>(currentShortcut).IsValidShortcut())) && GetSelectedValue(keyDropDownControlObjects[i]->GetComboBox()) != -1) if (((currentShortcut.index() == 0 && std::get<DWORD>(currentShortcut) != NULL) || (currentShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(currentShortcut)))) && GetSelectedValue(keyDropDownControlObjects[i]->GetComboBox()) != -1)
{ {
keyDropDownControlObjects[i]->ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow); keyDropDownControlObjects[i]->ValidateShortcutSelection(table, row, parent, colIndex, shortcutRemapBuffer, keyDropDownControlObjects, targetApp, isHybridControl, isSingleKeyWindow);
} }
@ -378,7 +383,7 @@ void KeyDropDownControl::SetDropDownError(ComboBox currentDropDown, hstring mess
} }
// Function to add a shortcut to the UI control as combo boxes // Function to add a shortcut to the UI control as combo boxes
void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow) void KeyDropDownControl::AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow)
{ {
// Delete the existing drop down menus // Delete the existing drop down menus
parent.Children().Clear(); parent.Children().Clear();

View File

@ -1,8 +1,13 @@
#pragma once #pragma once
#include <Shortcut.h> #include <keyboardmanager/common/Shortcut.h>
class KeyboardManagerState; namespace KBMEditor
{
class KeyboardManagerState;
}
class MappingConfiguration;
namespace winrt::Windows namespace winrt::Windows
{ {
@ -20,10 +25,7 @@ namespace winrt::Windows
} }
} }
namespace KeyboardManagerHelper enum class ShortcutErrorType;
{
enum class ErrorType;
}
// Wrapper class for the key drop down menu // Wrapper class for the key drop down menu
class KeyDropDownControl class KeyDropDownControl
@ -58,7 +60,8 @@ private:
public: public:
// Pointer to the keyboard manager state // Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState; static KBMEditor::KeyboardManagerState* keyboardManagerState;
static MappingConfiguration* mappingConfiguration;
// Constructor - the last default parameter should be passed as false only if it originates from Type shortcut or when an old shortcut is reloaded // Constructor - the last default parameter should be passed as false only if it originates from Type shortcut or when an old shortcut is reloaded
KeyDropDownControl(bool isShortcut, bool fromAddShortcutToControl = false, bool renderDisable = false) : KeyDropDownControl(bool isShortcut, bool fromAddShortcutToControl = false, bool renderDisable = false) :
@ -71,7 +74,7 @@ public:
void SetSelectionHandler(StackPanel& table, StackPanel row, int colIndex, RemapBuffer& singleKeyRemapBuffer); void SetSelectionHandler(StackPanel& table, StackPanel row, int colIndex, RemapBuffer& singleKeyRemapBuffer);
// Function for validating the selection of shortcuts for the drop down // Function for validating the selection of shortcuts for the drop down
std::pair<KeyboardManagerHelper::ErrorType, int> ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow); std::pair<ShortcutErrorType, int> ValidateShortcutSelection(StackPanel table, StackPanel row, StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
// Function to set selection handler for shortcut drop down. // Function to set selection handler for shortcut drop down.
void SetSelectionHandler(StackPanel& table, StackPanel row, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow); void SetSelectionHandler(StackPanel& table, StackPanel row, winrt::Windows::UI::Xaml::Controls::StackPanel parent, int colIndex, RemapBuffer& shortcutRemapBuffer, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, winrt::Windows::UI::Xaml::Controls::TextBox& targetApp, bool isHybridControl, bool isSingleKeyWindow);
@ -95,7 +98,7 @@ public:
void SetSelectedValue(std::wstring value); void SetSelectedValue(std::wstring value);
// Function to add a shortcut to the UI control as combo boxes // Function to add a shortcut to the UI control as combo boxes
static void AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow); static void AddShortcutToControl(Shortcut shortcut, StackPanel table, StackPanel parent, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, RemapBuffer& remapBuffer, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow);
// Get keys name list depending if Disable is in dropdown // Get keys name list depending if Disable is in dropdown
static std::vector<std::pair<DWORD,std::wstring>> GetKeyList(bool isShortcut, bool renderDisable); static std::vector<std::pair<DWORD,std::wstring>> GetKeyList(bool isShortcut, bool renderDisable);

View File

@ -27,7 +27,7 @@
<ItemDefinitionGroup> <ItemDefinitionGroup>
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;./../common;./../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>./;$(SolutionDir)src\modules\;$(SolutionDir)src\common\Display;$(SolutionDir)src\common\inc;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
@ -36,12 +36,17 @@
<ClInclude Include="BufferValidationHelpers.h" /> <ClInclude Include="BufferValidationHelpers.h" />
<ClInclude Include="Dialog.h" /> <ClInclude Include="Dialog.h" />
<ClInclude Include="EditKeyboardWindow.h" /> <ClInclude Include="EditKeyboardWindow.h" />
<ClInclude Include="EditorConstants.h" />
<ClInclude Include="EditorHelpers.h" />
<ClInclude Include="EditShortcutsWindow.h" /> <ClInclude Include="EditShortcutsWindow.h" />
<ClInclude Include="KeyboardManagerEditorStrings.h" /> <ClInclude Include="KeyboardManagerEditorStrings.h" />
<ClInclude Include="KeyboardManagerState.h" />
<ClInclude Include="KeyDelay.h" />
<ClInclude Include="KeyDropDownControl.h" /> <ClInclude Include="KeyDropDownControl.h" />
<ClInclude Include="LoadingAndSavingRemappingHelper.h" /> <ClInclude Include="LoadingAndSavingRemappingHelper.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="ShortcutControl.h" /> <ClInclude Include="ShortcutControl.h" />
<ClInclude Include="ShortcutErrorType.h" />
<ClInclude Include="SingleKeyRemapControl.h" /> <ClInclude Include="SingleKeyRemapControl.h" />
<ClInclude Include="Styles.h" /> <ClInclude Include="Styles.h" />
<ClInclude Include="targetver.h" /> <ClInclude Include="targetver.h" />
@ -53,8 +58,11 @@
<ClCompile Include="BufferValidationHelpers.cpp" /> <ClCompile Include="BufferValidationHelpers.cpp" />
<ClCompile Include="Dialog.cpp" /> <ClCompile Include="Dialog.cpp" />
<ClCompile Include="EditKeyboardWindow.cpp" /> <ClCompile Include="EditKeyboardWindow.cpp" />
<ClCompile Include="EditorHelpers.cpp" />
<ClCompile Include="EditShortcutsWindow.cpp" /> <ClCompile Include="EditShortcutsWindow.cpp" />
<ClCompile Include="KeyboardManagerEditorStrings.cpp" /> <ClCompile Include="KeyboardManagerEditorStrings.cpp" />
<ClCompile Include="KeyboardManagerState.cpp" />
<ClCompile Include="KeyDelay.cpp" />
<ClCompile Include="KeyDropDownControl.cpp" /> <ClCompile Include="KeyDropDownControl.cpp" />
<ClCompile Include="LoadingAndSavingRemappingHelper.cpp" /> <ClCompile Include="LoadingAndSavingRemappingHelper.cpp" />
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">

View File

@ -60,6 +60,21 @@
<ClInclude Include="trace.h"> <ClInclude Include="trace.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="KeyboardManagerState.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EditorHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutErrorType.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyDelay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EditorConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
@ -104,6 +119,15 @@
<ClCompile Include="trace.cpp"> <ClCompile Include="trace.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="KeyboardManagerState.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EditorHelpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KeyDelay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />

View File

@ -2,47 +2,45 @@
#include "KeyboardManagerEditorStrings.h" #include "KeyboardManagerEditorStrings.h"
// Function to return the error message // Function to return the error message
winrt::hstring KeyboardManagerEditorStrings::GetErrorMessage(KeyboardManagerHelper::ErrorType errorType) winrt::hstring KeyboardManagerEditorStrings::GetErrorMessage(ShortcutErrorType errorType)
{ {
using namespace KeyboardManagerHelper;
switch (errorType) switch (errorType)
{ {
case ErrorType::NoError: case ShortcutErrorType::NoError:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPSUCCESSFUL).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPSUCCESSFUL).c_str();
case ErrorType::SameKeyPreviouslyMapped: case ShortcutErrorType::SameKeyPreviouslyMapped:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMEKEYPREVIOUSLYMAPPED).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMEKEYPREVIOUSLYMAPPED).c_str();
case ErrorType::MapToSameKey: case ShortcutErrorType::MapToSameKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPPEDTOSAMEKEY).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPPEDTOSAMEKEY).c_str();
case ErrorType::ConflictingModifierKey: case ShortcutErrorType::ConflictingModifierKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERKEY).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERKEY).c_str();
case ErrorType::SameShortcutPreviouslyMapped: case ShortcutErrorType::SameShortcutPreviouslyMapped:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMESHORTCUTPREVIOUSLYMAPPED).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAMESHORTCUTPREVIOUSLYMAPPED).c_str();
case ErrorType::MapToSameShortcut: case ShortcutErrorType::MapToSameShortcut:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPTOSAMESHORTCUT).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAPTOSAMESHORTCUT).c_str();
case ErrorType::ConflictingModifierShortcut: case ShortcutErrorType::ConflictingModifierShortcut:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERSHORTCUT).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CONFLICTINGMODIFIERSHORTCUT).c_str();
case ErrorType::WinL: case ShortcutErrorType::WinL:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_WINL).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_WINL).c_str();
case ErrorType::CtrlAltDel: case ShortcutErrorType::CtrlAltDel:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CTRLALTDEL).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_CTRLALTDEL).c_str();
case ErrorType::RemapUnsuccessful: case ShortcutErrorType::RemapUnsuccessful:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPUNSUCCESSFUL).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_REMAPUNSUCCESSFUL).c_str();
case ErrorType::SaveFailed: case ShortcutErrorType::SaveFailed:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAVEFAILED).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SAVEFAILED).c_str();
case ErrorType::ShortcutStartWithModifier: case ShortcutErrorType::ShortcutStartWithModifier:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTSTARTWITHMODIFIER).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTSTARTWITHMODIFIER).c_str();
case ErrorType::ShortcutCannotHaveRepeatedModifier: case ShortcutErrorType::ShortcutCannotHaveRepeatedModifier:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTNOREPEATEDMODIFIER).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTNOREPEATEDMODIFIER).c_str();
case ErrorType::ShortcutAtleast2Keys: case ShortcutErrorType::ShortcutAtleast2Keys:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTATLEAST2KEYS).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTATLEAST2KEYS).c_str();
case ErrorType::ShortcutOneActionKey: case ShortcutErrorType::ShortcutOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTONEACTIONKEY).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTONEACTIONKEY).c_str();
case ErrorType::ShortcutNotMoreThanOneActionKey: case ShortcutErrorType::ShortcutNotMoreThanOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTMAXONEACTIONKEY).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_SHORTCUTMAXONEACTIONKEY).c_str();
case ErrorType::ShortcutMaxShortcutSizeOneActionKey: case ShortcutErrorType::ShortcutMaxShortcutSizeOneActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAXSHORTCUTSIZE).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_MAXSHORTCUTSIZE).c_str();
case ErrorType::ShortcutDisableAsActionKey: case ShortcutErrorType::ShortcutDisableAsActionKey:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DISABLEASACTIONKEY).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DISABLEASACTIONKEY).c_str();
default: default:
return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DEFAULT).c_str(); return GET_RESOURCE_STRING(IDS_ERRORMESSAGE_DEFAULT).c_str();

View File

@ -2,7 +2,7 @@
#include "pch.h" #include "pch.h"
#include <ErrorTypes.h> #include "ShortcutErrorType.h"
namespace KeyboardManagerEditorStrings namespace KeyboardManagerEditorStrings
{ {
@ -10,5 +10,5 @@ namespace KeyboardManagerEditorStrings
inline const std::wstring DefaultAppName = GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS); inline const std::wstring DefaultAppName = GET_RESOURCE_STRING(IDS_EDITSHORTCUTS_ALLAPPS);
// Function to return the error message // Function to return the error message
winrt::hstring GetErrorMessage(KeyboardManagerHelper::ErrorType errorType); winrt::hstring GetErrorMessage(ShortcutErrorType errorType);
} }

View File

@ -1,624 +1,363 @@
#include "pch.h" #include "pch.h"
#include "KeyboardManagerState.h" #include "KeyboardManagerState.h"
#include "Shortcut.h"
#include "RemapShortcut.h" #include <keyboardmanager/common/Helpers.h>
#include <common/SettingsAPI/settings_helpers.h>
#include "KeyDelay.h" #include "EditorHelpers.h"
#include "Helpers.h" #include "KeyDelay.h"
#include <common/logger/logger.h>
using namespace KBMEditor;
// Constructor
KeyboardManagerState::KeyboardManagerState() : // Constructor
uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutUI1(nullptr), currentShortcutUI2(nullptr), currentSingleKeyUI(nullptr), detectedRemapKey(NULL), remappingsEnabled(true) KeyboardManagerState::KeyboardManagerState() :
{ uiState(KeyboardManagerUIState::Deactivated), currentUIWindow(nullptr), currentShortcutUI1(nullptr), currentShortcutUI2(nullptr), currentSingleKeyUI(nullptr), detectedRemapKey(NULL)
} {
}
// Destructor
KeyboardManagerState::~KeyboardManagerState() // Destructor
{ KeyboardManagerState::~KeyboardManagerState()
} {
}
// Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus.
bool KeyboardManagerState::CheckUIState(KeyboardManagerUIState state) // Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus.
{ bool KeyboardManagerState::CheckUIState(KeyboardManagerUIState state)
std::lock_guard<std::mutex> lock(uiState_mutex); {
if (uiState == state) std::lock_guard<std::mutex> lock(uiState_mutex);
{ if (uiState == state)
std::unique_lock<std::mutex> lock(currentUIWindow_mutex); {
if (uiState == KeyboardManagerUIState::Deactivated || uiState == KeyboardManagerUIState::EditKeyboardWindowActivated || uiState == KeyboardManagerUIState::EditShortcutsWindowActivated) std::unique_lock<std::mutex> lock(currentUIWindow_mutex);
{ if (uiState == KeyboardManagerUIState::Deactivated || uiState == KeyboardManagerUIState::EditKeyboardWindowActivated || uiState == KeyboardManagerUIState::EditShortcutsWindowActivated)
return true; {
} return true;
// If the UI state is a detect window then we also have to ensure that the UI window is in focus. }
// GetForegroundWindow can be used here since we only need to check the main parent window and not the sub windows within the content dialog. Using GUIThreadInfo will give more specific sub-windows within the XAML window which is not needed. // If the UI state is a detect window then we also have to ensure that the UI window is in focus.
else if (currentUIWindow == GetForegroundWindow()) // GetForegroundWindow can be used here since we only need to check the main parent window and not the sub windows within the content dialog. Using GUIThreadInfo will give more specific sub-windows within the XAML window which is not needed.
{ else if (currentUIWindow == GetForegroundWindow())
return true; {
} return true;
} }
// If we are checking for EditKeyboardWindowActivated then it's possible the state could be DetectSingleKeyRemapWindowActivated but not in focus }
else if (state == KeyboardManagerUIState::EditKeyboardWindowActivated && uiState == KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated) // If we are checking for EditKeyboardWindowActivated then it's possible the state could be DetectSingleKeyRemapWindowActivated but not in focus
{ else if (state == KeyboardManagerUIState::EditKeyboardWindowActivated && uiState == KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated)
return true; {
} return true;
// If we are checking for EditShortcutsWindowActivated then it's possible the state could be DetectShortcutWindowActivated but not in focus }
else if (state == KeyboardManagerUIState::EditShortcutsWindowActivated && uiState == KeyboardManagerUIState::DetectShortcutWindowActivated) // If we are checking for EditShortcutsWindowActivated then it's possible the state could be DetectShortcutWindowActivated but not in focus
{ else if (state == KeyboardManagerUIState::EditShortcutsWindowActivated && uiState == KeyboardManagerUIState::DetectShortcutWindowActivated)
return true; {
} return true;
}
return false;
} return false;
}
// Function to set the window handle of the current UI window that is activated
void KeyboardManagerState::SetCurrentUIWindow(HWND windowHandle) // Function to set the window handle of the current UI window that is activated
{ void KeyboardManagerState::SetCurrentUIWindow(HWND windowHandle)
std::lock_guard<std::mutex> lock(currentUIWindow_mutex); {
currentUIWindow = windowHandle; std::lock_guard<std::mutex> lock(currentUIWindow_mutex);
} currentUIWindow = windowHandle;
}
// Function to set the UI state. When a window is activated, the handle to the window can be passed in the windowHandle argument.
void KeyboardManagerState::SetUIState(KeyboardManagerUIState state, HWND windowHandle) // Function to set the UI state. When a window is activated, the handle to the window can be passed in the windowHandle argument.
{ void KeyboardManagerState::SetUIState(KeyboardManagerUIState state, HWND windowHandle)
std::lock_guard<std::mutex> lock(uiState_mutex); {
uiState = state; std::lock_guard<std::mutex> lock(uiState_mutex);
SetCurrentUIWindow(windowHandle); uiState = state;
} SetCurrentUIWindow(windowHandle);
}
// Function to reset the UI state members
void KeyboardManagerState::ResetUIState() // Function to reset the UI state members
{ void KeyboardManagerState::ResetUIState()
SetUIState(KeyboardManagerUIState::Deactivated); {
SetUIState(KeyboardManagerUIState::Deactivated);
// Reset the shortcut UI stored variables
std::unique_lock<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex); // Reset the shortcut UI stored variables
currentShortcutUI1 = nullptr; std::unique_lock<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex);
currentShortcutUI2 = nullptr; currentShortcutUI1 = nullptr;
currentShortcutUI_lock.unlock(); currentShortcutUI2 = nullptr;
currentShortcutUI_lock.unlock();
std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
detectedShortcut.Reset(); std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
detectedShortcut_lock.unlock(); detectedShortcut.Reset();
detectedShortcut_lock.unlock();
std::unique_lock<std::mutex> currentShortcut_lock(currentShortcut_mutex);
currentShortcut.Reset(); std::unique_lock<std::mutex> currentShortcut_lock(currentShortcut_mutex);
currentShortcut_lock.unlock(); currentShortcut.Reset();
currentShortcut_lock.unlock();
// Reset all the single key remap UI stored variables.
std::unique_lock<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex); // Reset all the single key remap UI stored variables.
currentSingleKeyUI = nullptr; std::unique_lock<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex);
currentSingleKeyUI_lock.unlock(); currentSingleKeyUI = nullptr;
currentSingleKeyUI_lock.unlock();
std::unique_lock<std::mutex> detectedRemapKey_lock(detectedRemapKey_mutex);
detectedRemapKey = NULL; std::unique_lock<std::mutex> detectedRemapKey_lock(detectedRemapKey_mutex);
detectedRemapKey_lock.unlock(); detectedRemapKey = NULL;
} detectedRemapKey_lock.unlock();
}
// Function to clear the OS Level shortcut remapping table
void KeyboardManagerState::ClearOSLevelShortcuts() // Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
{ void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2)
osLevelShortcutReMap.clear(); {
osLevelShortcutReMapSortedKeys.clear(); std::lock_guard<std::mutex> lock(currentShortcutUI_mutex);
} currentShortcutUI1 = textBlock1.as<winrt::Windows::Foundation::IInspectable>();
currentShortcutUI2 = textBlock2.as<winrt::Windows::Foundation::IInspectable>();
// Function to clear the Keys remapping table. }
void KeyboardManagerState::ClearSingleKeyRemaps()
{ // Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
singleKeyReMap.clear(); void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const StackPanel& textBlock)
} {
std::lock_guard<std::mutex> lock(currentSingleKeyUI_mutex);
// Function to clear the App specific shortcut remapping table currentSingleKeyUI = textBlock.as<winrt::Windows::Foundation::IInspectable>();
void KeyboardManagerState::ClearAppSpecificShortcuts() }
{
appSpecificShortcutReMap.clear(); void KeyboardManagerState::AddKeyToLayout(const StackPanel& panel, const hstring& key)
appSpecificShortcutReMapSortedKeys.clear(); {
} // Textblock to display the detected key
TextBlock remapKey;
// Function to add a new OS level shortcut remapping Border border;
bool KeyboardManagerState::AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC)
{ border.Padding({ 20, 10, 20, 10 });
// Check if the shortcut is already remapped border.Margin({ 0, 0, 10, 0 });
auto it = osLevelShortcutReMap.find(originalSC); // Use the base low brush to be consistent with the theme
if (it != osLevelShortcutReMap.end()) border.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
{ remapKey.FontSize(20);
return false; border.HorizontalAlignment(HorizontalAlignment::Left);
} border.Child(remapKey);
osLevelShortcutReMap[originalSC] = RemapShortcut(newSC); remapKey.Text(key);
osLevelShortcutReMapSortedKeys.push_back(originalSC); panel.Children().Append(border);
KeyboardManagerHelper::SortShortcutVectorBasedOnSize(osLevelShortcutReMapSortedKeys); }
return true; // Function to update the detect shortcut UI based on the entered keys
} void KeyboardManagerState::UpdateDetectShortcutUI()
{
// Function to add a new single key to key/shortcut remapping std::lock_guard<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex);
bool KeyboardManagerState::AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey) if (currentShortcutUI1 == nullptr)
{ {
// Check if the key is already remapped return;
auto it = singleKeyReMap.find(originalKey); }
if (it != singleKeyReMap.end())
{ std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex);
return false; std::unique_lock<std::mutex> currentShortcut_lock(currentShortcut_mutex);
} // Save the latest displayed shortcut
currentShortcut = detectedShortcut;
singleKeyReMap[originalKey] = newRemapKey; auto detectedShortcutCopy = detectedShortcut;
return true; currentShortcut_lock.unlock();
} detectedShortcut_lock.unlock();
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used.
// Function to add a new App specific shortcut remapping currentShortcutUI1.as<StackPanel>().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this, detectedShortcutCopy]() {
bool KeyboardManagerState::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC) std::vector<hstring> shortcut = EditorHelpers::GetKeyVector(detectedShortcutCopy, keyboardMap);
{ currentShortcutUI1.as<StackPanel>().Children().Clear();
// Convert app name to lower case currentShortcutUI2.as<StackPanel>().Children().Clear();
std::wstring process_name;
process_name.resize(app.length()); // The second row should be hidden if there are 3 keys or lesser to avoid an extra margin
std::transform(app.begin(), app.end(), process_name.begin(), towlower); if (shortcut.size() > 3)
{
// Check if there are any app specific shortcuts for this app currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Visible);
auto appIt = appSpecificShortcutReMap.find(process_name); }
if (appIt != appSpecificShortcutReMap.end()) else
{ {
// Check if the shortcut is already remapped currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Collapsed);
auto shortcutIt = appSpecificShortcutReMap[process_name].find(originalSC); }
if (shortcutIt != appSpecificShortcutReMap[process_name].end())
{ for (int i = 0; i < shortcut.size(); i++)
return false; {
} if (i < 3)
} {
else AddKeyToLayout(currentShortcutUI1.as<StackPanel>(), shortcut[i]);
{ }
appSpecificShortcutReMapSortedKeys[process_name] = std::vector<Shortcut>(); else
} {
AddKeyToLayout(currentShortcutUI2.as<StackPanel>(), shortcut[i]);
appSpecificShortcutReMap[process_name][originalSC] = RemapShortcut(newSC); }
appSpecificShortcutReMapSortedKeys[process_name].push_back(originalSC); }
KeyboardManagerHelper::SortShortcutVectorBasedOnSize(appSpecificShortcutReMapSortedKeys[process_name]); currentShortcutUI1.as<StackPanel>().UpdateLayout();
return true; currentShortcutUI2.as<StackPanel>().UpdateLayout();
} });
}
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
std::optional<SingleKeyRemapTable::iterator> KeyboardManagerState::GetSingleKeyRemap(const DWORD& originalKey) // Function to update the detect remap key UI based on the entered key.
{ void KeyboardManagerState::UpdateDetectSingleKeyRemapUI()
auto it = singleKeyReMap.find(originalKey); {
if (it != singleKeyReMap.end()) std::lock_guard<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex);
{ if (currentSingleKeyUI == nullptr)
return it; {
} return;
}
return std::nullopt; // Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used.
} currentSingleKeyUI.as<StackPanel>().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() {
currentSingleKeyUI.as<StackPanel>().Children().Clear();
bool KeyboardManagerState::CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName) hstring key = winrt::to_hstring(keyboardMap.GetKeyName(detectedRemapKey).c_str());
{ AddKeyToLayout(currentSingleKeyUI.as<StackPanel>(), key);
// Assumes appName exists in the app-specific remap table currentSingleKeyUI.as<StackPanel>().UpdateLayout();
ShortcutRemapTable& currentRemapTable = appName ? appSpecificShortcutReMap[*appName] : osLevelShortcutReMap; });
for (auto& it : currentRemapTable) }
{
if (it.second.isShortcutInvoked) // Function to return the currently detected shortcut which is displayed on the UI
{ Shortcut KeyboardManagerState::GetDetectedShortcut()
return true; {
} std::lock_guard<std::mutex> lock(currentShortcut_mutex);
} return currentShortcut;
}
return false;
} // Function to return the currently detected remap key which is displayed on the UI
DWORD KeyboardManagerState::GetDetectedSingleRemapKey()
std::vector<Shortcut>& KeyboardManagerState::GetSortedShortcutRemapVector(const std::optional<std::wstring>& appName) {
{ std::lock_guard<std::mutex> lock(detectedRemapKey_mutex);
// Assumes appName exists in the app-specific remap table return detectedRemapKey;
return appName ? appSpecificShortcutReMapSortedKeys[*appName] : osLevelShortcutReMapSortedKeys; }
}
void KeyboardManagerState::SelectDetectedRemapKey(DWORD key)
// Function to get the source and target of a shortcut remap given the source shortcut. Returns nullopt if it isn't remapped {
ShortcutRemapTable& KeyboardManagerState::GetShortcutRemapTable(const std::optional<std::wstring>& appName) std::lock_guard<std::mutex> guard(detectedRemapKey_mutex);
{ detectedRemapKey = key;
if (appName) UpdateDetectSingleKeyRemapUI();
{ return;
auto itTable = appSpecificShortcutReMap.find(*appName); }
if (itTable != appSpecificShortcutReMap.end())
{ void KeyboardManagerState::SelectDetectedShortcut(DWORD key)
return itTable->second; {
} // Set the new key and store if a change occurred
} std::unique_lock<std::mutex> lock(detectedShortcut_mutex);
bool updateUI = detectedShortcut.SetKey(key);
return osLevelShortcutReMap; lock.unlock();
}
if (updateUI)
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook {
void KeyboardManagerState::ConfigureDetectShortcutUI(const StackPanel& textBlock1, const StackPanel& textBlock2) // Update the UI. This function is called here because it should store the set of keys pressed till the last key which was pressed down.
{ UpdateDetectShortcutUI();
std::lock_guard<std::mutex> lock(currentShortcutUI_mutex); }
currentShortcutUI1 = textBlock1.as<winrt::Windows::Foundation::IInspectable>(); return;
currentShortcutUI2 = textBlock2.as<winrt::Windows::Foundation::IInspectable>(); }
}
void KeyboardManagerState::ResetDetectedShortcutKey(DWORD key)
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook {
void KeyboardManagerState::ConfigureDetectSingleKeyRemapUI(const StackPanel& textBlock) std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
{ detectedShortcut.ResetKey(key);
std::lock_guard<std::mutex> lock(currentSingleKeyUI_mutex); }
currentSingleKeyUI = textBlock.as<winrt::Windows::Foundation::IInspectable>();
} // Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data)
void KeyboardManagerState::AddKeyToLayout(const StackPanel& panel, const hstring& key) {
{ // Check if the detect key UI window has been activated
// Textblock to display the detected key if (CheckUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated))
TextBlock remapKey; {
Border border; if (HandleKeyDelayEvent(data))
{
border.Padding({ 20, 10, 20, 10 }); return Helpers::KeyboardHookDecision::Suppress;
border.Margin({ 0, 0, 10, 0 }); }
// Use the base low brush to be consistent with the theme // detect the key if it is pressed down
border.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>()); if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
remapKey.FontSize(20); {
border.HorizontalAlignment(HorizontalAlignment::Left); SelectDetectedRemapKey(data->lParam->vkCode);
border.Child(remapKey); }
remapKey.Text(key); // Suppress the keyboard event
panel.Children().Append(border); return Helpers::KeyboardHookDecision::Suppress;
} }
// Function to update the detect shortcut UI based on the entered keys // If the settings window is up, remappings should not be applied, but we should not suppress events in the hook
void KeyboardManagerState::UpdateDetectShortcutUI() else if (CheckUIState(KeyboardManagerUIState::EditKeyboardWindowActivated))
{ {
std::lock_guard<std::mutex> currentShortcutUI_lock(currentShortcutUI_mutex); return Helpers::KeyboardHookDecision::SkipHook;
if (currentShortcutUI1 == nullptr) }
{
return; return Helpers::KeyboardHookDecision::ContinueExec;
} }
std::unique_lock<std::mutex> detectedShortcut_lock(detectedShortcut_mutex); // Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
std::unique_lock<std::mutex> currentShortcut_lock(currentShortcut_mutex); Helpers::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey)
// Save the latest displayed shortcut {
currentShortcut = detectedShortcut; // Check if the detect shortcut UI window has been activated
auto detectedShortcutCopy = detectedShortcut; if ((!isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) || (isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated)))
currentShortcut_lock.unlock(); {
detectedShortcut_lock.unlock(); if (HandleKeyDelayEvent(data))
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used. {
currentShortcutUI1.as<StackPanel>().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this, detectedShortcutCopy]() { return Helpers::KeyboardHookDecision::Suppress;
std::vector<hstring> shortcut = detectedShortcutCopy.GetKeyVector(keyboardMap); }
currentShortcutUI1.as<StackPanel>().Children().Clear();
currentShortcutUI2.as<StackPanel>().Children().Clear(); // Add the key if it is pressed down
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
// The second row should be hidden if there are 3 keys or lesser to avoid an extra margin {
if (shortcut.size() > 3) SelectDetectedShortcut(data->lParam->vkCode);
{ }
currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Visible); // Remove the key if it has been released
} else if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
else {
{ ResetDetectedShortcutKey(data->lParam->vkCode);
currentShortcutUI2.as<StackPanel>().Visibility(Visibility::Collapsed); }
}
// Suppress the keyboard event
for (int i = 0; i < shortcut.size(); i++) return Helpers::KeyboardHookDecision::Suppress;
{ }
if (i < 3)
{ // If the detect shortcut UI window is not activated, then clear the shortcut buffer if it isn't empty
AddKeyToLayout(currentShortcutUI1.as<StackPanel>(), shortcut[i]); else if (!CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated) && !CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
} {
else std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
{ if (!detectedShortcut.IsEmpty())
AddKeyToLayout(currentShortcutUI2.as<StackPanel>(), shortcut[i]); {
} detectedShortcut.Reset();
} }
currentShortcutUI1.as<StackPanel>().UpdateLayout(); }
currentShortcutUI2.as<StackPanel>().UpdateLayout();
}); // If the settings window is up, shortcut remappings should not be applied, but we should not suppress events in the hook
} if (!isRemapKey && (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated)) || (isRemapKey && uiState == KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
// Function to update the detect remap key UI based on the entered key. return Helpers::KeyboardHookDecision::SkipHook;
void KeyboardManagerState::UpdateDetectSingleKeyRemapUI() }
{
std::lock_guard<std::mutex> currentSingleKeyUI_lock(currentSingleKeyUI_mutex); return Helpers::KeyboardHookDecision::ContinueExec;
if (currentSingleKeyUI == nullptr) }
{
return; void KeyboardManagerState::RegisterKeyDelay(
} DWORD key,
// Since this function is invoked from the back-end thread, in order to update the UI the dispatcher must be used. std::function<void(DWORD)> onShortPress,
currentSingleKeyUI.as<StackPanel>().Dispatcher().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() { std::function<void(DWORD)> onLongPressDetected,
currentSingleKeyUI.as<StackPanel>().Children().Clear(); std::function<void(DWORD)> onLongPressReleased)
hstring key = winrt::to_hstring(keyboardMap.GetKeyName(detectedRemapKey).c_str()); {
AddKeyToLayout(currentSingleKeyUI.as<StackPanel>(), key); std::lock_guard l(keyDelays_mutex);
currentSingleKeyUI.as<StackPanel>().UpdateLayout();
}); if (keyDelays.find(key) != keyDelays.end())
} {
throw std::invalid_argument("This key was already registered.");
// Function to return the currently detected shortcut which is displayed on the UI }
Shortcut KeyboardManagerState::GetDetectedShortcut()
{ keyDelays[key] = std::make_unique<KeyDelay>(key, onShortPress, onLongPressDetected, onLongPressReleased);
std::lock_guard<std::mutex> lock(currentShortcut_mutex); }
return currentShortcut;
} void KeyboardManagerState::UnregisterKeyDelay(DWORD key)
{
// Function to return the currently detected remap key which is displayed on the UI std::lock_guard l(keyDelays_mutex);
DWORD KeyboardManagerState::GetDetectedSingleRemapKey()
{ auto deleted = keyDelays.erase(key);
std::lock_guard<std::mutex> lock(detectedRemapKey_mutex); if (deleted == 0)
return detectedRemapKey; {
} throw std::invalid_argument("The key was not previously registered.");
}
void KeyboardManagerState::SelectDetectedRemapKey(DWORD key) }
{
std::lock_guard<std::mutex> guard(detectedRemapKey_mutex); // Function to clear all the registered key delays
detectedRemapKey = key; void KeyboardManagerState::ClearRegisteredKeyDelays()
UpdateDetectSingleKeyRemapUI(); {
return; std::lock_guard l(keyDelays_mutex);
} keyDelays.clear();
}
void KeyboardManagerState::SelectDetectedShortcut(DWORD key)
{ bool KeyboardManagerState::HandleKeyDelayEvent(LowlevelKeyboardEvent* ev)
// Set the new key and store if a change occurred {
std::unique_lock<std::mutex> lock(detectedShortcut_mutex); if (currentUIWindow != GetForegroundWindow())
bool updateUI = detectedShortcut.SetKey(key); {
lock.unlock(); return false;
}
if (updateUI)
{ std::lock_guard l(keyDelays_mutex);
// Update the UI. This function is called here because it should store the set of keys pressed till the last key which was pressed down.
UpdateDetectShortcutUI(); if (keyDelays.find(ev->lParam->vkCode) == keyDelays.end())
} {
return; return false;
} }
void KeyboardManagerState::ResetDetectedShortcutKey(DWORD key) keyDelays[ev->lParam->vkCode]->KeyEvent(ev);
{ return true;
std::lock_guard<std::mutex> lock(detectedShortcut_mutex); }
detectedShortcut.ResetKey(key);
}
// Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data)
{
// Check if the detect key UI window has been activated
if (CheckUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated))
{
if (HandleKeyDelayEvent(data))
{
return KeyboardManagerHelper::KeyboardHookDecision::Suppress;
}
// detect the key if it is pressed down
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{
SelectDetectedRemapKey(data->lParam->vkCode);
}
// Suppress the keyboard event
return KeyboardManagerHelper::KeyboardHookDecision::Suppress;
}
// If the settings window is up, remappings should not be applied, but we should not suppress events in the hook
else if (CheckUIState(KeyboardManagerUIState::EditKeyboardWindowActivated))
{
return KeyboardManagerHelper::KeyboardHookDecision::SkipHook;
}
return KeyboardManagerHelper::KeyboardHookDecision::ContinueExec;
}
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
KeyboardManagerHelper::KeyboardHookDecision KeyboardManagerState::DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey)
{
// Check if the detect shortcut UI window has been activated
if ((!isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated)) || (isRemapKey && CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated)))
{
if (HandleKeyDelayEvent(data))
{
return KeyboardManagerHelper::KeyboardHookDecision::Suppress;
}
// Add the key if it is pressed down
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{
SelectDetectedShortcut(data->lParam->vkCode);
}
// Remove the key if it has been released
else if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{
ResetDetectedShortcutKey(data->lParam->vkCode);
}
// Suppress the keyboard event
return KeyboardManagerHelper::KeyboardHookDecision::Suppress;
}
// If the detect shortcut UI window is not activated, then clear the shortcut buffer if it isn't empty
else if (!CheckUIState(KeyboardManagerUIState::DetectShortcutWindowActivated) && !CheckUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
std::lock_guard<std::mutex> lock(detectedShortcut_mutex);
if (!detectedShortcut.IsEmpty())
{
detectedShortcut.Reset();
}
}
// If the settings window is up, shortcut remappings should not be applied, but we should not suppress events in the hook
if (!isRemapKey && (CheckUIState(KeyboardManagerUIState::EditShortcutsWindowActivated)) || (isRemapKey && uiState == KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated))
{
return KeyboardManagerHelper::KeyboardHookDecision::SkipHook;
}
return KeyboardManagerHelper::KeyboardHookDecision::ContinueExec;
}
void KeyboardManagerState::RegisterKeyDelay(
DWORD key,
std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased)
{
std::lock_guard l(keyDelays_mutex);
if (keyDelays.find(key) != keyDelays.end())
{
throw std::invalid_argument("This key was already registered.");
}
keyDelays[key] = std::make_unique<KeyDelay>(key, onShortPress, onLongPressDetected, onLongPressReleased);
}
void KeyboardManagerState::UnregisterKeyDelay(DWORD key)
{
std::lock_guard l(keyDelays_mutex);
auto deleted = keyDelays.erase(key);
if (deleted == 0)
{
throw std::invalid_argument("The key was not previously registered.");
}
}
// Function to clear all the registered key delays
void KeyboardManagerState::ClearRegisteredKeyDelays()
{
std::lock_guard l(keyDelays_mutex);
keyDelays.clear();
}
bool KeyboardManagerState::HandleKeyDelayEvent(LowlevelKeyboardEvent* ev)
{
if (currentUIWindow != GetForegroundWindow())
{
return false;
}
std::lock_guard l(keyDelays_mutex);
if (keyDelays.find(ev->lParam->vkCode) == keyDelays.end())
{
return false;
}
keyDelays[ev->lParam->vkCode]->KeyEvent(ev);
return true;
}
// Save the updated configuration.
bool KeyboardManagerState::SaveConfigToFile()
{
bool result = true;
json::JsonObject configJson;
json::JsonObject remapShortcuts;
json::JsonObject remapKeys;
json::JsonArray inProcessRemapKeysArray;
json::JsonArray appSpecificRemapShortcutsArray;
json::JsonArray globalRemapShortcutsArray;
for (const auto& it : singleKeyReMap)
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.first)));
// For key to key remapping
if (it.second.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second))));
}
// For key to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second).ToHstringVK()));
}
inProcessRemapKeysArray.Append(keys);
}
for (const auto& it : osLevelShortcutReMap)
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(it.first.ToHstringVK()));
// For shortcut to key remapping
if (it.second.targetShortcut.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second.targetShortcut))));
}
// For shortcut to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second.targetShortcut).ToHstringVK()));
}
globalRemapShortcutsArray.Append(keys);
}
for (const auto& itApp : appSpecificShortcutReMap)
{
// Iterate over apps
for (const auto& itKeys : itApp.second)
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(itKeys.first.ToHstringVK()));
// For shortcut to key remapping
if (itKeys.second.targetShortcut.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(itKeys.second.targetShortcut))));
}
// For shortcut to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(itKeys.second.targetShortcut).ToHstringVK()));
}
keys.SetNamedValue(KeyboardManagerConstants::TargetAppSettingName, json::value(itApp.first));
appSpecificRemapShortcutsArray.Append(keys);
}
}
remapShortcuts.SetNamedValue(KeyboardManagerConstants::GlobalRemapShortcutsSettingName, globalRemapShortcutsArray);
remapShortcuts.SetNamedValue(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName, appSpecificRemapShortcutsArray);
remapKeys.SetNamedValue(KeyboardManagerConstants::InProcessRemapKeysSettingName, inProcessRemapKeysArray);
configJson.SetNamedValue(KeyboardManagerConstants::RemapKeysSettingName, remapKeys);
configJson.SetNamedValue(KeyboardManagerConstants::RemapShortcutsSettingName, remapShortcuts);
try
{
json::to_file((PTSettingsHelper::get_module_save_folder_location(KeyboardManagerConstants::ModuleName) + L"\\" + GetCurrentConfigName() + L".json"), configJson);
}
catch (...)
{
result = false;
Logger::error(L"Failed to save the settings");
}
if (result)
{
auto hEvent = CreateEvent(nullptr, false, false, KeyboardManagerConstants::SettingsEventName.c_str());
if (hEvent)
{
SetEvent(hEvent);
Logger::trace(L"Signaled {} event", KeyboardManagerConstants::SettingsEventName);
}
else
{
Logger::error(L"Failed to signal {} event", KeyboardManagerConstants::SettingsEventName);
}
}
return result;
}
void KeyboardManagerState::SetCurrentConfigName(const std::wstring& configName)
{
std::lock_guard<std::mutex> lock(currentConfig_mutex);
currentConfig = configName;
}
std::wstring KeyboardManagerState::GetCurrentConfigName()
{
std::lock_guard<std::mutex> lock(currentConfig_mutex);
return currentConfig;
}
// Sets the activated target application in app-specific shortcut
void KeyboardManagerState::SetActivatedApp(const std::wstring& appName)
{
activatedAppSpecificShortcutTarget = appName;
}
// Gets the activated target application in app-specific shortcut
std::wstring KeyboardManagerState::GetActivatedApp()
{
return activatedAppSpecificShortcutTarget;
}

View File

@ -0,0 +1,161 @@
#pragma once
#include <common/hooks/LowlevelKeyboardEvent.h>
#include <common/interop/keyboard_layout.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/common/Shortcut.h>
class KeyDelay;
namespace Helpers
{
// Enum type to store possible decision for input in the low level hook
enum class KeyboardHookDecision
{
ContinueExec,
Suppress,
SkipHook
};
}
namespace winrt::Windows::UI::Xaml::Controls
{
struct StackPanel;
}
namespace KBMEditor
{
// Enum type to store different states of the UI
enum class KeyboardManagerUIState
{
// If set to this value then there is no keyboard manager window currently active that requires a hook
Deactivated,
// If set to this value then the detect key window is currently active and it requires a hook
DetectSingleKeyRemapWindowActivated,
// If set to this value then the detect shortcut window in edit keyboard window is currently active and it requires a hook
DetectShortcutWindowInEditKeyboardWindowActivated,
// If set to this value then the edit keyboard window is currently active and remaps should not be applied
EditKeyboardWindowActivated,
// If set to this value then the detect shortcut window is currently active and it requires a hook
DetectShortcutWindowActivated,
// If set to this value then the edit shortcuts window is currently active and remaps should not be applied
EditShortcutsWindowActivated
};
// Class to store the shared state of the keyboard manager between the UI and the hook
class KeyboardManagerState
{
private:
// State variable used to store which UI window is currently active that requires interaction with the hook
KeyboardManagerUIState uiState;
std::mutex uiState_mutex;
// Window handle for the current UI window which is active. Should be set to nullptr if UI is deactivated
HWND currentUIWindow;
std::mutex currentUIWindow_mutex;
// Object to store the shortcut detected in the detect shortcut UI window. Gets cleared on releasing keys. This is used in both the backend and the UI.
Shortcut detectedShortcut;
std::mutex detectedShortcut_mutex;
// Object to store the shortcut state displayed in the UI window. Always stores last displayed shortcut irrespective of releasing keys. This is used in both the backend and the UI.
Shortcut currentShortcut;
std::mutex currentShortcut_mutex;
// Store detected remap key in the remap UI window. This is used in both the backend and the UI.
DWORD detectedRemapKey;
std::mutex detectedRemapKey_mutex;
// Stores the UI element which is to be updated based on the remap key entered.
winrt::Windows::Foundation::IInspectable currentSingleKeyUI;
std::mutex currentSingleKeyUI_mutex;
// Stores the UI element which is to be updated based on the shortcut entered (each stackpanel represents a row of keys)
winrt::Windows::Foundation::IInspectable currentShortcutUI1;
winrt::Windows::Foundation::IInspectable currentShortcutUI2;
std::mutex currentShortcutUI_mutex;
// Registered KeyDelay objects, used to notify delayed key events.
std::map<DWORD, std::unique_ptr<KeyDelay>> keyDelays;
std::mutex keyDelays_mutex;
// Display a key by appending a border Control as a child of the panel.
void AddKeyToLayout(const winrt::Windows::UI::Xaml::Controls::StackPanel& panel, const winrt::hstring& key);
public:
// Stores the keyboard layout
LayoutMap keyboardMap;
// Constructor
KeyboardManagerState();
// Destructor
~KeyboardManagerState();
// Function to reset the UI state members
void ResetUIState();
// Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus.
bool CheckUIState(KeyboardManagerUIState state);
// Function to set the window handle of the current UI window that is activated
void SetCurrentUIWindow(HWND windowHandle);
// Function to set the UI state. When a window is activated, the handle to the window can be passed in the windowHandle argument.
void SetUIState(KeyboardManagerUIState state, HWND windowHandle = nullptr);
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
void ConfigureDetectShortcutUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock1, const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock2);
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
void ConfigureDetectSingleKeyRemapUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock);
// Function to update the detect shortcut UI based on the entered keys
void UpdateDetectShortcutUI();
// Function to update the detect remap key UI based on the entered key.
void UpdateDetectSingleKeyRemapUI();
// Function to return the currently detected shortcut which is displayed on the UI
Shortcut GetDetectedShortcut();
// Function to return the currently detected remap key which is displayed on the UI
DWORD GetDetectedSingleRemapKey();
// Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data);
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
Helpers::KeyboardHookDecision DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey);
// Add a KeyDelay object to get delayed key presses events for a given virtual key
// NOTE: this will throw an exception if a virtual key is registered twice.
// NOTE*: the virtual key should represent the original, unmapped virtual key.
void RegisterKeyDelay(
DWORD key,
std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased);
// Remove a KeyDelay.
// NOTE: this method will throw if the virtual key is not registered beforehand.
// NOTE*: the virtual key should represent the original, unmapped virtual key.
void UnregisterKeyDelay(DWORD key);
// Function to clear all the registered key delays
void ClearRegisteredKeyDelays();
// Handle a key event, for a delayed key.
bool HandleKeyDelayEvent(LowlevelKeyboardEvent* ev);
// Update the currently selected single key remap
void SelectDetectedRemapKey(DWORD key);
// Update the currently selected shortcut.
void SelectDetectedShortcut(DWORD key);
// Reset the shortcut (backend) state after releasing a key.
void ResetDetectedShortcutKey(DWORD key);
};
}

View File

@ -2,19 +2,20 @@
#include "LoadingAndSavingRemappingHelper.h" #include "LoadingAndSavingRemappingHelper.h"
#include <set> #include <set>
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <ErrorTypes.h> #include <keyboardmanager/common/MappingConfiguration.h>
#include <KeyboardManagerState.h>
#include <keyboardmanager/KeyboardManagerEditorLibrary/trace.h> #include "KeyboardManagerState.h"
#include "keyboardmanager/KeyboardManagerEditorLibrary/trace.h"
#include "EditorHelpers.h"
#include "ShortcutErrorType.h"
namespace LoadingAndSavingRemappingHelper namespace LoadingAndSavingRemappingHelper
{ {
// Function to check if the set of remappings in the buffer are valid // Function to check if the set of remappings in the buffer are valid
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings) ShortcutErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings)
{ {
KeyboardManagerHelper::ErrorType isSuccess = KeyboardManagerHelper::ErrorType::NoError; ShortcutErrorType isSuccess = ShortcutErrorType::NoError;
std::map<std::wstring, std::set<KeyShortcutUnion>> ogKeys; std::map<std::wstring, std::set<KeyShortcutUnion>> ogKeys;
for (int i = 0; i < remappings.size(); i++) for (int i = 0; i < remappings.size(); i++)
{ {
@ -22,8 +23,8 @@ namespace LoadingAndSavingRemappingHelper
KeyShortcutUnion newKey = remappings[i].first[1]; KeyShortcutUnion newKey = remappings[i].first[1];
std::wstring appName = remappings[i].second; std::wstring appName = remappings[i].second;
bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && std::get<Shortcut>(ogKey).IsValidShortcut()); bool ogKeyValidity = (ogKey.index() == 0 && std::get<DWORD>(ogKey) != NULL) || (ogKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(ogKey)));
bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut()); bool newKeyValidity = (newKey.index() == 0 && std::get<DWORD>(newKey) != NULL) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)));
// Add new set for a new target app name // Add new set for a new target app name
if (ogKeys.find(appName) == ogKeys.end()) if (ogKeys.find(appName) == ogKeys.end())
@ -37,11 +38,11 @@ namespace LoadingAndSavingRemappingHelper
} }
else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end()) else if (ogKeyValidity && newKeyValidity && ogKeys[appName].find(ogKey) != ogKeys[appName].end())
{ {
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful; isSuccess = ShortcutErrorType::RemapUnsuccessful;
} }
else else
{ {
isSuccess = KeyboardManagerHelper::ErrorType::RemapUnsuccessful; isSuccess = ShortcutErrorType::RemapUnsuccessful;
} }
} }
@ -59,7 +60,7 @@ namespace LoadingAndSavingRemappingHelper
DWORD ogKey = std::get<DWORD>(remappings[i].first[0]); DWORD ogKey = std::get<DWORD>(remappings[i].first[0]);
KeyShortcutUnion newKey = remappings[i].first[1]; KeyShortcutUnion newKey = remappings[i].first[1];
if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && std::get<Shortcut>(newKey).IsValidShortcut()))) if (ogKey != NULL && ((newKey.index() == 0 && std::get<DWORD>(newKey) != 0) || (newKey.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey)))))
{ {
ogKeys.insert(ogKey); ogKeys.insert(ogKey);
@ -105,10 +106,10 @@ namespace LoadingAndSavingRemappingHelper
} }
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable // Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired) void ApplySingleKeyRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired)
{ {
// Clear existing Key Remaps // Clear existing Key Remaps
keyboardManagerState.ClearSingleKeyRemaps(); mappingConfiguration.ClearSingleKeyRemaps();
DWORD successfulKeyToKeyRemapCount = 0; DWORD successfulKeyToKeyRemapCount = 0;
DWORD successfulKeyToShortcutRemapCount = 0; DWORD successfulKeyToShortcutRemapCount = 0;
for (int i = 0; i < remappings.size(); i++) for (int i = 0; i < remappings.size(); i++)
@ -116,7 +117,7 @@ namespace LoadingAndSavingRemappingHelper
DWORD originalKey = std::get<DWORD>(remappings[i].first[0]); DWORD originalKey = std::get<DWORD>(remappings[i].first[0]);
KeyShortcutUnion newKey = remappings[i].first[1]; KeyShortcutUnion newKey = remappings[i].first[1];
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut())) if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))))
{ {
// If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key // If Ctrl/Alt/Shift are added, add their L and R versions instead to the same key
bool result = false; bool result = false;
@ -124,27 +125,27 @@ namespace LoadingAndSavingRemappingHelper
switch (originalKey) switch (originalKey)
{ {
case VK_CONTROL: case VK_CONTROL:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LCONTROL, newKey); res1 = mappingConfiguration.AddSingleKeyRemap(VK_LCONTROL, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RCONTROL, newKey); res2 = mappingConfiguration.AddSingleKeyRemap(VK_RCONTROL, newKey);
result = res1 && res2; result = res1 && res2;
break; break;
case VK_MENU: case VK_MENU:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LMENU, newKey); res1 = mappingConfiguration.AddSingleKeyRemap(VK_LMENU, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RMENU, newKey); res2 = mappingConfiguration.AddSingleKeyRemap(VK_RMENU, newKey);
result = res1 && res2; result = res1 && res2;
break; break;
case VK_SHIFT: case VK_SHIFT:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LSHIFT, newKey); res1 = mappingConfiguration.AddSingleKeyRemap(VK_LSHIFT, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RSHIFT, newKey); res2 = mappingConfiguration.AddSingleKeyRemap(VK_RSHIFT, newKey);
result = res1 && res2; result = res1 && res2;
break; break;
case CommonSharedConstants::VK_WIN_BOTH: case CommonSharedConstants::VK_WIN_BOTH:
res1 = keyboardManagerState.AddSingleKeyRemap(VK_LWIN, newKey); res1 = mappingConfiguration.AddSingleKeyRemap(VK_LWIN, newKey);
res2 = keyboardManagerState.AddSingleKeyRemap(VK_RWIN, newKey); res2 = mappingConfiguration.AddSingleKeyRemap(VK_RWIN, newKey);
result = res1 && res2; result = res1 && res2;
break; break;
default: default:
result = keyboardManagerState.AddSingleKeyRemap(originalKey, newKey); result = mappingConfiguration.AddSingleKeyRemap(originalKey, newKey);
} }
if (result) if (result)
@ -169,11 +170,11 @@ namespace LoadingAndSavingRemappingHelper
} }
// Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable // Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable
void ApplyShortcutRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired) void ApplyShortcutRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired)
{ {
// Clear existing shortcuts // Clear existing shortcuts
keyboardManagerState.ClearOSLevelShortcuts(); mappingConfiguration.ClearOSLevelShortcuts();
keyboardManagerState.ClearAppSpecificShortcuts(); mappingConfiguration.ClearAppSpecificShortcuts();
DWORD successfulOSLevelShortcutToShortcutRemapCount = 0; DWORD successfulOSLevelShortcutToShortcutRemapCount = 0;
DWORD successfulOSLevelShortcutToKeyRemapCount = 0; DWORD successfulOSLevelShortcutToKeyRemapCount = 0;
DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0; DWORD successfulAppSpecificShortcutToShortcutRemapCount = 0;
@ -185,11 +186,11 @@ namespace LoadingAndSavingRemappingHelper
Shortcut originalShortcut = std::get<Shortcut>(remappings[i].first[0]); Shortcut originalShortcut = std::get<Shortcut>(remappings[i].first[0]);
KeyShortcutUnion newShortcut = remappings[i].first[1]; KeyShortcutUnion newShortcut = remappings[i].first[1];
if (originalShortcut.IsValidShortcut() && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && std::get<Shortcut>(newShortcut).IsValidShortcut()))) if (EditorHelpers::IsValidShortcut(originalShortcut) && ((newShortcut.index() == 0 && std::get<DWORD>(newShortcut) != NULL) || (newShortcut.index() == 1 && EditorHelpers::IsValidShortcut(std::get<Shortcut>(newShortcut)))))
{ {
if (remappings[i].second == L"") if (remappings[i].second == L"")
{ {
bool result = keyboardManagerState.AddOSLevelShortcut(originalShortcut, newShortcut); bool result = mappingConfiguration.AddOSLevelShortcut(originalShortcut, newShortcut);
if (result) if (result)
{ {
if (newShortcut.index() == 0) if (newShortcut.index() == 0)
@ -204,7 +205,7 @@ namespace LoadingAndSavingRemappingHelper
} }
else else
{ {
bool result = keyboardManagerState.AddAppSpecificShortcut(remappings[i].second, originalShortcut, newShortcut); bool result = mappingConfiguration.AddAppSpecificShortcut(remappings[i].second, originalShortcut, newShortcut);
if (result) if (result)
{ {
if (newShortcut.index() == 0) if (newShortcut.index() == 0)

View File

@ -2,12 +2,14 @@
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
class KeyboardManagerState; #include "ShortcutErrorType.h"
class MappingConfiguration;
namespace LoadingAndSavingRemappingHelper namespace LoadingAndSavingRemappingHelper
{ {
// Function to check if the set of remappings in the buffer are valid // Function to check if the set of remappings in the buffer are valid
KeyboardManagerHelper::ErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings); ShortcutErrorType CheckIfRemappingsAreValid(const RemapBuffer& remappings);
// Function to return the set of keys that have been orphaned from the remap buffer // Function to return the set of keys that have been orphaned from the remap buffer
std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings); std::vector<DWORD> GetOrphanedKeys(const RemapBuffer& remappings);
@ -19,8 +21,8 @@ namespace LoadingAndSavingRemappingHelper
void PreProcessRemapTable(std::unordered_map<DWORD, KeyShortcutUnion>& table); void PreProcessRemapTable(std::unordered_map<DWORD, KeyShortcutUnion>& table);
// Function to apply the single key remappings from the buffer to the KeyboardManagerState variable // Function to apply the single key remappings from the buffer to the KeyboardManagerState variable
void ApplySingleKeyRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired); void ApplySingleKeyRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired);
// Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable // Function to apply the shortcut remappings from the buffer to the KeyboardManagerState variable
void ApplyShortcutRemappings(KeyboardManagerState& keyboardManagerState, const RemapBuffer& remappings, bool isTelemetryRequired); void ApplyShortcutRemappings(MappingConfiguration& mappingConfiguration, const RemapBuffer& remappings, bool isTelemetryRequired);
} }

View File

@ -3,15 +3,16 @@
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <KeyboardManagerState.h> #include "KeyboardManagerState.h"
#include "KeyboardManagerEditorStrings.h"
#include <KeyboardManagerEditorStrings.h> #include "KeyDropDownControl.h"
#include <KeyDropDownControl.h> #include "UIHelpers.h"
#include <UIHelpers.h> #include "EditorHelpers.h"
#include "EditorConstants.h"
//Both static members are initialized to null //Both static members are initialized to null
HWND ShortcutControl::editShortcutsWindowHandle = nullptr; HWND ShortcutControl::editShortcutsWindowHandle = nullptr;
KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr; KBMEditor::KeyboardManagerState* ShortcutControl::keyboardManagerState = nullptr;
// Initialized as new vector // Initialized as new vector
RemapBuffer ShortcutControl::shortcutRemapBuffer; RemapBuffer ShortcutControl::shortcutRemapBuffer;
@ -22,13 +23,13 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
shortcutControlLayout = StackPanel(); shortcutControlLayout = StackPanel();
bool isHybridControl = colIndex == 1 ? true : false; bool isHybridControl = colIndex == 1 ? true : false;
shortcutDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing); shortcutDropDownStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal); shortcutDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON))); typeShortcut.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
typeShortcut.as<Button>().Width(KeyboardManagerConstants::ShortcutTableDropDownWidth); typeShortcut.as<Button>().Width(EditorConstants::ShortcutTableDropDownWidth);
typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) { typeShortcut.as<Button>().Click([&, table, row, colIndex, isHybridControl, targetApp](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle); keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectShortcutWindowActivated, editShortcutsWindowHandle);
// Using the XamlRoot of the typeShortcut to get the root of the XAML host // Using the XamlRoot of the typeShortcut to get the root of the XAML host
CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, editShortcutsWindowHandle, shortcutRemapBuffer); CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, targetApp, isHybridControl, false, editShortcutsWindowHandle, shortcutRemapBuffer);
}); });
@ -36,7 +37,7 @@ ShortcutControl::ShortcutControl(StackPanel table, StackPanel row, const int col
// Set an accessible name for the type shortcut button // Set an accessible name for the type shortcut button
typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON))); typeShortcut.as<Button>().SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
shortcutControlLayout.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing); shortcutControlLayout.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>()); shortcutControlLayout.as<StackPanel>().Children().Append(typeShortcut.as<Button>());
shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownStackPanel.as<StackPanel>()); shortcutControlLayout.as<StackPanel>().Children().Append(shortcutDropDownStackPanel.as<StackPanel>());
@ -90,7 +91,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
// ShortcutControl for the original shortcut // ShortcutControl for the original shortcut
auto origin = keyboardRemapControlObjects.back()[0]->GetShortcutControl(); auto origin = keyboardRemapControlObjects.back()[0]->GetShortcutControl();
origin.Width(KeyboardManagerConstants::ShortcutOriginColumnWidth); origin.Width(EditorConstants::ShortcutOriginColumnWidth);
row.Children().Append(origin); row.Children().Append(origin);
// Arrow icon // Arrow icon
@ -99,17 +100,17 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
arrowIcon.Glyph(L"\xE72A"); arrowIcon.Glyph(L"\xE72A");
arrowIcon.VerticalAlignment(VerticalAlignment::Center); arrowIcon.VerticalAlignment(VerticalAlignment::Center);
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center); arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::ShortcutArrowColumnWidth).as<StackPanel>(); auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, EditorConstants::ShortcutArrowColumnWidth).as<StackPanel>();
arrowIconContainer.Orientation(Orientation::Vertical); arrowIconContainer.Orientation(Orientation::Vertical);
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center); arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
row.Children().Append(arrowIconContainer); row.Children().Append(arrowIconContainer);
// ShortcutControl for the new shortcut // ShortcutControl for the new shortcut
auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl(); auto target = keyboardRemapControlObjects.back()[1]->GetShortcutControl();
target.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth); target.Width(EditorConstants::ShortcutTargetColumnWidth);
row.Children().Append(target); row.Children().Append(target);
targetAppTextBox.Width(KeyboardManagerConstants::ShortcutTableDropDownWidth); targetAppTextBox.Width(EditorConstants::ShortcutTableDropDownWidth);
targetAppTextBox.PlaceholderText(KeyboardManagerEditorStrings::DefaultAppName); targetAppTextBox.PlaceholderText(KeyboardManagerEditorStrings::DefaultAppName);
targetAppTextBox.Text(targetAppName); targetAppTextBox.Text(targetAppName);
@ -174,10 +175,10 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
}); });
// We need two containers in order to align it horizontally and vertically // We need two containers in order to align it horizontally and vertically
StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>(); StackPanel targetAppHorizontal = UIHelpers::GetWrapped(targetAppTextBox, EditorConstants::TableTargetAppColWidth).as<StackPanel>();
targetAppHorizontal.Orientation(Orientation::Horizontal); targetAppHorizontal.Orientation(Orientation::Horizontal);
targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left); targetAppHorizontal.HorizontalAlignment(HorizontalAlignment::Left);
StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, KeyboardManagerConstants::TableTargetAppColWidth).as<StackPanel>(); StackPanel targetAppContainer = UIHelpers::GetWrapped(targetAppHorizontal, EditorConstants::TableTargetAppColWidth).as<StackPanel>();
targetAppContainer.Orientation(Orientation::Vertical); targetAppContainer.Orientation(Orientation::Vertical);
targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom); targetAppContainer.VerticalAlignment(VerticalAlignment::Bottom);
row.Children().Append(targetAppContainer); row.Children().Append(targetAppContainer);
@ -240,7 +241,7 @@ void ShortcutControl::AddNewShortcutControlRow(StackPanel& parent, std::vector<s
UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size()); UpdateAccessibleNames(keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->GetShortcutControl(), keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->GetShortcutControl(), targetAppTextBox, deleteShortcut, (int)keyboardRemapControlObjects.size());
// Set the shortcut text if the two vectors are not empty (i.e. default args) // Set the shortcut text if the two vectors are not empty (i.e. default args)
if (originalKeys.IsValidShortcut() && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !std::get<Shortcut>(newKeys).IsValidShortcut())) if (EditorHelpers::IsValidShortcut(originalKeys) && !(newKeys.index() == 0 && std::get<DWORD>(newKeys) == NULL) && !(newKeys.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKeys))))
{ {
// change to load app name // change to load app name
shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName))); shortcutRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ Shortcut(), Shortcut() }, std::wstring(targetAppName)));
@ -269,7 +270,7 @@ StackPanel ShortcutControl::GetShortcutControl()
} }
// Function to create the detect shortcut UI window // Function to create the detect shortcut UI window
void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer) void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel row, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer)
{ {
// ContentDialog for detecting shortcuts. This is the parent UI element. // ContentDialog for detecting shortcuts. This is the parent UI element.
ContentDialog detectShortcutBox; ContentDialog detectShortcutBox;
@ -325,12 +326,12 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
if (isSingleKeyWindow) if (isSingleKeyWindow)
{ {
// Revert UI state back to Edit Keyboard window // Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
} }
else else
{ {
// Revert UI state back to Edit Shortcut window // Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
} }
unregisterKeys(); unregisterKeys();
@ -391,12 +392,12 @@ void ShortcutControl::CreateDetectShortcutWindow(winrt::Windows::Foundation::IIn
if (isSingleKeyWindow) if (isSingleKeyWindow)
{ {
// Revert UI state back to Edit Keyboard window // Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
} }
else else
{ {
// Revert UI state back to Edit Shortcut window // Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
} }
unregisterKeys(); unregisterKeys();
}; };

View File

@ -1,8 +1,12 @@
#pragma once #pragma once
#include <Shortcut.h> #include <keyboardmanager/common/Shortcut.h>
namespace KBMEditor
{
class KeyboardManagerState;
}
class KeyboardManagerState;
class KeyDropDownControl; class KeyDropDownControl;
namespace winrt::Windows::UI::Xaml namespace winrt::Windows::UI::Xaml
{ {
@ -38,7 +42,7 @@ public:
static HWND editShortcutsWindowHandle; static HWND editShortcutsWindowHandle;
// Pointer to the keyboard manager state // Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState; static KBMEditor::KeyboardManagerState* keyboardManagerState;
// Stores the current list of remappings // Stores the current list of remappings
static RemapBuffer shortcutRemapBuffer; static RemapBuffer shortcutRemapBuffer;
@ -56,5 +60,5 @@ public:
StackPanel GetShortcutControl(); StackPanel GetShortcutControl();
// Function to create the detect shortcut UI window // Function to create the detect shortcut UI window
static void CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer); static void CreateDetectShortcutWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState, const int colIndex, StackPanel table, std::vector<std::unique_ptr<KeyDropDownControl>>& keyDropDownControlObjects, StackPanel controlLayout, TextBox targetApp, bool isHybridControl, bool isSingleKeyWindow, HWND parentWindow, RemapBuffer& remapBuffer);
}; };

View File

@ -0,0 +1,24 @@
#pragma once
// Type to store codes for different errors
enum class ShortcutErrorType
{
NoError,
SameKeyPreviouslyMapped,
MapToSameKey,
ConflictingModifierKey,
SameShortcutPreviouslyMapped,
MapToSameShortcut,
ConflictingModifierShortcut,
WinL,
CtrlAltDel,
RemapUnsuccessful,
SaveFailed,
ShortcutStartWithModifier,
ShortcutCannotHaveRepeatedModifier,
ShortcutAtleast2Keys,
ShortcutOneActionKey,
ShortcutNotMoreThanOneActionKey,
ShortcutMaxShortcutSizeOneActionKey,
ShortcutDisableAsActionKey
};

View File

@ -1,21 +1,22 @@
#include "pch.h" #include "pch.h"
#include "SingleKeyRemapControl.h" #include "SingleKeyRemapControl.h"
#include <KeyboardManagerState.h> #include "KeyboardManagerState.h"
#include "ShortcutControl.h"
#include <ShortcutControl.h> #include "UIHelpers.h"
#include <UIHelpers.h> #include "EditorHelpers.h"
#include "EditorConstants.h"
//Both static members are initialized to null //Both static members are initialized to null
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr; HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr; KBMEditor::KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
// Initialized as new vector // Initialized as new vector
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer; RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex) SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
{ {
typeKey = Button(); typeKey = Button();
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth); typeKey.as<Button>().Width(EditorConstants::RemapTableDropDownWidth);
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON))); typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
singleKeyRemapControlLayout = StackPanel(); singleKeyRemapControlLayout = StackPanel();
@ -35,7 +36,7 @@ SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, c
else else
{ {
hybridDropDownStackPanel = StackPanel(); hybridDropDownStackPanel = StackPanel();
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing); hybridDropDownStackPanel.as<StackPanel>().Spacing(EditorConstants::ShortcutTableDropDownSpacing);
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal); hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true); KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>()); singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
@ -45,12 +46,12 @@ SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, c
// Using the XamlRoot of the typeKey to get the root of the XAML host // Using the XamlRoot of the typeKey to get the root of the XAML host
if (colIndex == 0) if (colIndex == 0)
{ {
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle); keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState); createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
} }
else else
{ {
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle); keyboardManagerState->SetUIState(KBMEditor::KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
ShortcutControl::CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer); ShortcutControl::CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
} }
}); });
@ -87,7 +88,7 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
// SingleKeyRemapControl for the original key. // SingleKeyRemapControl for the original key.
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(); auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth); originalElement.Width(EditorConstants::RemapTableDropDownWidth);
row.Children().Append(originalElement); row.Children().Append(originalElement);
// Arrow icon // Arrow icon
@ -96,18 +97,18 @@ void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::ve
arrowIcon.Glyph(L"\xE72A"); arrowIcon.Glyph(L"\xE72A");
arrowIcon.VerticalAlignment(VerticalAlignment::Center); arrowIcon.VerticalAlignment(VerticalAlignment::Center);
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center); arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>(); auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, EditorConstants::TableArrowColWidth).as<StackPanel>();
arrowIconContainer.Orientation(Orientation::Vertical); arrowIconContainer.Orientation(Orientation::Vertical);
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center); arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
row.Children().Append(arrowIconContainer); row.Children().Append(arrowIconContainer);
// SingleKeyRemapControl for the new remap key // SingleKeyRemapControl for the new remap key
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(); auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth); targetElement.Width(EditorConstants::ShortcutTargetColumnWidth);
row.Children().Append(targetElement); row.Children().Append(targetElement);
// Set the key text if the two keys are not null (i.e. default args) // Set the key text if the two keys are not null (i.e. default args)
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut())) if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !EditorHelpers::IsValidShortcut(std::get<Shortcut>(newKey))))
{ {
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L"")); singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey)); keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey));
@ -186,7 +187,7 @@ StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
} }
// Function to create the detect remap key UI window // Function to create the detect remap key UI window
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState) void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState)
{ {
// ContentDialog for detecting remap key. This is the parent UI element. // ContentDialog for detecting remap key. This is the parent UI element.
ContentDialog detectRemapKeyBox; ContentDialog detectRemapKeyBox;
@ -229,7 +230,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// Reset the keyboard manager UI state // Reset the keyboard manager UI state
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window // Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys(); unregisterKeys();
}; };
@ -253,7 +254,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread // NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay( keyboardManagerState.RegisterKeyDelay(
VK_RETURN, VK_RETURN,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1), std::bind(&KBMEditor::KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) { [primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync( detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal, Windows::UI::Core::CoreDispatcherPriority::Normal,
@ -283,7 +284,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
keyboardManagerState.ResetUIState(); keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window // Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle); keyboardManagerState.SetUIState(KBMEditor::KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys(); unregisterKeys();
}; };
@ -300,7 +301,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread // NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay( keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE, VK_ESCAPE,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1), std::bind(&KBMEditor::KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[onCancel, detectRemapKeyBox](DWORD) { [onCancel, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync( detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal, Windows::UI::Core::CoreDispatcherPriority::Normal,

View File

@ -1,10 +1,14 @@
#pragma once #pragma once
#include <Shortcut.h> #include <keyboardmanager/common/Shortcut.h>
#include <KeyDropDownControl.h> #include <KeyDropDownControl.h>
class KeyboardManagerState; namespace KBMEditor
{
class KeyboardManagerState;
}
namespace winrt::Windows::UI::Xaml namespace winrt::Windows::UI::Xaml
{ {
struct XamlRoot; struct XamlRoot;
@ -39,7 +43,7 @@ public:
static HWND EditKeyboardWindowHandle; static HWND EditKeyboardWindowHandle;
// Pointer to the keyboard manager state // Pointer to the keyboard manager state
static KeyboardManagerState* keyboardManagerState; static KBMEditor::KeyboardManagerState* keyboardManagerState;
// Stores the current list of remappings // Stores the current list of remappings
static RemapBuffer singleKeyRemapBuffer; static RemapBuffer singleKeyRemapBuffer;
@ -54,5 +58,5 @@ public:
winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl(); winrt::Windows::UI::Xaml::Controls::StackPanel getSingleKeyRemapControl();
// Function to create the detect remap keys UI window // Function to create the detect remap keys UI window
void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState); void createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KBMEditor::KeyboardManagerState& keyboardManagerState);
}; };

View File

@ -1,14 +1,12 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include <keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h> #include <keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.h>
#include "TestHelpers.h"
#include <common/interop/keyboard_layout.h> #include <common/interop/keyboard_layout.h>
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <functional> #include <functional>
#include <keyboardmanager/common/ErrorTypes.h> #include <keyboardmanager/KeyboardManagerEditorLibrary/ShortcutErrorType.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace TestHelpers;
namespace RemappingUITests namespace RemappingUITests
{ {
@ -61,10 +59,10 @@ namespace RemappingUITests
// Validate and update the element when -1 i.e. null selection is made on an empty row. // Validate and update the element when -1 i.e. null selection is made on an empty row.
ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, -1 }; ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, -1 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is validated and buffer is updated // Assert that the element is validated and buffer is updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, error == ShortcutErrorType::NoError);
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[0]));
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[1])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[1]));
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0]));
@ -81,10 +79,10 @@ namespace RemappingUITests
// Validate and update the element when selecting B on an empty row // Validate and update the element when selecting B on an empty row
ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x42 }; ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x42 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is validated and buffer is updated // Assert that the element is validated and buffer is updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, error == ShortcutErrorType::NoError);
Assert::AreEqual((DWORD)0x42, std::get<DWORD>(remapBuffer[0].first[0])); Assert::AreEqual((DWORD)0x42, std::get<DWORD>(remapBuffer[0].first[0]));
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[1])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[1]));
} }
@ -99,10 +97,10 @@ namespace RemappingUITests
// Validate and update the element when selecting B on a row // Validate and update the element when selecting B on a row
ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x42 }; ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x42 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is validated and buffer is updated // Assert that the element is validated and buffer is updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, error == ShortcutErrorType::NoError);
Assert::AreEqual((DWORD)0x42, std::get<DWORD>(remapBuffer[0].first[0])); Assert::AreEqual((DWORD)0x42, std::get<DWORD>(remapBuffer[0].first[0]));
Assert::AreEqual((DWORD)0x41, std::get<DWORD>(remapBuffer[0].first[1])); Assert::AreEqual((DWORD)0x41, std::get<DWORD>(remapBuffer[0].first[1]));
} }
@ -117,10 +115,10 @@ namespace RemappingUITests
// Validate and update the element when selecting B on a row // Validate and update the element when selecting B on a row
ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x42 }; ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x42 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is validated and buffer is updated // Assert that the element is validated and buffer is updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, error == ShortcutErrorType::NoError);
Assert::AreEqual((DWORD)0x42, std::get<DWORD>(remapBuffer[0].first[0])); Assert::AreEqual((DWORD)0x42, std::get<DWORD>(remapBuffer[0].first[0]));
Assert::AreEqual(true, Shortcut(std::vector<int32_t>{ VK_CONTROL, 0x41 }) == std::get<Shortcut>(remapBuffer[0].first[1])); Assert::AreEqual(true, Shortcut(std::vector<int32_t>{ VK_CONTROL, 0x41 }) == std::get<Shortcut>(remapBuffer[0].first[1]));
} }
@ -135,10 +133,10 @@ namespace RemappingUITests
// Validate and update the element when selecting A on a row // Validate and update the element when selecting A on a row
ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x41 }; ValidateAndUpdateKeyBufferElementArgs args = { 0, 0, 0x41 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is invalid and buffer is not updated // Assert that the element is invalid and buffer is not updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::MapToSameKey); Assert::AreEqual(true, error == ShortcutErrorType::MapToSameKey);
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[0].first[0]));
Assert::AreEqual((DWORD)0x41, std::get<DWORD>(remapBuffer[0].first[1])); Assert::AreEqual((DWORD)0x41, std::get<DWORD>(remapBuffer[0].first[1]));
} }
@ -154,10 +152,10 @@ namespace RemappingUITests
// Validate and update the element when selecting A on second row // Validate and update the element when selecting A on second row
ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, 0x41 }; ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, 0x41 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is invalid and buffer is not updated // Assert that the element is invalid and buffer is not updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::SameKeyPreviouslyMapped); Assert::AreEqual(true, error == ShortcutErrorType::SameKeyPreviouslyMapped);
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0]));
Assert::AreEqual((DWORD)0x43, std::get<DWORD>(remapBuffer[1].first[1])); Assert::AreEqual((DWORD)0x43, std::get<DWORD>(remapBuffer[1].first[1]));
} }
@ -173,10 +171,10 @@ namespace RemappingUITests
// Validate and update the element when selecting A on second row // Validate and update the element when selecting A on second row
ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, 0x41 }; ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, 0x41 };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is invalid and buffer is not updated // Assert that the element is invalid and buffer is not updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::SameKeyPreviouslyMapped); Assert::AreEqual(true, error == ShortcutErrorType::SameKeyPreviouslyMapped);
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0]));
Assert::AreEqual(true, Shortcut(std::vector<int32_t>{ VK_CONTROL, 0x41 }) == std::get<Shortcut>(remapBuffer[1].first[1])); Assert::AreEqual(true, Shortcut(std::vector<int32_t>{ VK_CONTROL, 0x41 }) == std::get<Shortcut>(remapBuffer[1].first[1]));
} }
@ -192,10 +190,10 @@ namespace RemappingUITests
// Validate and update the element when selecting LCtrl on second row // Validate and update the element when selecting LCtrl on second row
ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, VK_LCONTROL }; ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, VK_LCONTROL };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is invalid and buffer is not updated // Assert that the element is invalid and buffer is not updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::ConflictingModifierKey); Assert::AreEqual(true, error == ShortcutErrorType::ConflictingModifierKey);
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0]));
Assert::AreEqual((DWORD)0x43, std::get<DWORD>(remapBuffer[1].first[1])); Assert::AreEqual((DWORD)0x43, std::get<DWORD>(remapBuffer[1].first[1]));
} }
@ -211,10 +209,10 @@ namespace RemappingUITests
// Validate and update the element when selecting LCtrl on second row // Validate and update the element when selecting LCtrl on second row
ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, VK_LCONTROL }; ValidateAndUpdateKeyBufferElementArgs args = { 1, 0, VK_LCONTROL };
KeyboardManagerHelper::ErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer); ShortcutErrorType error = BufferValidationHelpers::ValidateAndUpdateKeyBufferElement(args.elementRowIndex, args.elementColIndex, args.selectedCodeFromDropDown, remapBuffer);
// Assert that the element is invalid and buffer is not updated // Assert that the element is invalid and buffer is not updated
Assert::AreEqual(true, error == KeyboardManagerHelper::ErrorType::ConflictingModifierKey); Assert::AreEqual(true, error == ShortcutErrorType::ConflictingModifierKey);
Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0])); Assert::AreEqual((DWORD)NULL, std::get<DWORD>(remapBuffer[1].first[0]));
Assert::AreEqual(true, Shortcut(std::vector<int32_t>{ VK_CONTROL, 0x41 }) == std::get<Shortcut>(remapBuffer[1].first[1])); Assert::AreEqual(true, Shortcut(std::vector<int32_t>{ VK_CONTROL, 0x41 }) == std::get<Shortcut>(remapBuffer[1].first[1]));
} }
@ -268,10 +266,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and no drop down action is required // Assert that the element is valid and no drop down action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -295,10 +293,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no drop down action is required // Assert that the element is invalid and no drop down action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutStartWithModifier);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -318,10 +316,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and no drop down action is required // Assert that the element is valid and no drop down action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -341,10 +339,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no drop down action is required // Assert that the element is invalid and no drop down action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutNotMoreThanOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -364,10 +362,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no drop down action is required // Assert that the element is invalid and no drop down action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutNotMoreThanOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutNotMoreThanOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -395,10 +393,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and no drop down action is required // Assert that the element is valid and no drop down action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -426,10 +424,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and ClearUnusedDropDowns action is required // Assert that the element is valid and ClearUnusedDropDowns action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::ClearUnusedDropDowns); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::ClearUnusedDropDowns);
}); });
} }
@ -447,10 +445,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and ClearUnusedDropDowns action is required // Assert that the element is valid and ClearUnusedDropDowns action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::ClearUnusedDropDowns); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::ClearUnusedDropDowns);
}); });
} }
@ -482,10 +480,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and AddDropDown action is required // Assert that the element is valid and AddDropDown action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::AddDropDown); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::AddDropDown);
}); });
} }
@ -509,10 +507,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutCannotHaveRepeatedModifier);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -546,10 +544,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutMaxShortcutSizeOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -577,10 +575,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutMaxShortcutSizeOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutMaxShortcutSizeOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -614,10 +612,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and no action is required // Assert that the element is valid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -651,10 +649,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutCannotHaveRepeatedModifier); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutCannotHaveRepeatedModifier);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -674,10 +672,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutStartWithModifier); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutStartWithModifier);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -695,10 +693,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -722,10 +720,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutAtleast2Keys); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutAtleast2Keys);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -749,10 +747,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -776,10 +774,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and DeleteDropDown action is required // Assert that the element is valid and DeleteDropDown action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::DeleteDropDown); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::DeleteDropDown);
}); });
} }
@ -819,10 +817,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid and DeleteDropDown action is required // Assert that the element is valid and DeleteDropDown action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::DeleteDropDown); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::DeleteDropDown);
}); });
} }
@ -850,10 +848,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid and no action is required // Assert that the element is invalid and no action is required
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutOneActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutOneActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
}); });
} }
@ -889,10 +887,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::WinL); Assert::AreEqual(true, result.first == ShortcutErrorType::WinL);
}); });
} }
@ -921,10 +919,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::WinL); Assert::AreEqual(true, result.first == ShortcutErrorType::WinL);
}); });
} }
@ -953,10 +951,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::CtrlAltDel); Assert::AreEqual(true, result.first == ShortcutErrorType::CtrlAltDel);
}); });
} }
@ -987,10 +985,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::MapToSameKey); Assert::AreEqual(true, result.first == ShortcutErrorType::MapToSameKey);
}); });
} }
@ -1025,10 +1023,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::MapToSameShortcut); Assert::AreEqual(true, result.first == ShortcutErrorType::MapToSameShortcut);
}); });
} }
@ -1103,10 +1101,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::MapToSameShortcut); Assert::AreEqual(true, result.first == ShortcutErrorType::MapToSameShortcut);
}); });
} }
@ -1129,10 +1127,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped); Assert::AreEqual(true, result.first == ShortcutErrorType::SameShortcutPreviouslyMapped);
}); });
} }
@ -1157,10 +1155,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid // Assert that the element is valid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
}); });
} }
@ -1183,10 +1181,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut); Assert::AreEqual(true, result.first == ShortcutErrorType::ConflictingModifierShortcut);
}); });
} }
@ -1209,10 +1207,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid // Assert that the element is valid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
}); });
} }
@ -1249,10 +1247,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped); Assert::AreEqual(true, result.first == ShortcutErrorType::SameShortcutPreviouslyMapped);
}); });
} }
@ -1289,10 +1287,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid // Assert that the element is valid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
}); });
} }
@ -1329,10 +1327,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is invalid // Assert that the element is invalid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut); Assert::AreEqual(true, result.first == ShortcutErrorType::ConflictingModifierShortcut);
}); });
} }
@ -1369,10 +1367,10 @@ namespace RemappingUITests
remapBuffer.push_back(testCase.bufferRow); remapBuffer.push_back(testCase.bufferRow);
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(testCase.elementRowIndex, testCase.elementColIndex, testCase.indexOfDropDownLastModified, testCase.selectedCodesOnDropDowns, testCase.targetAppNameInTextBox, testCase.isHybridColumn, remapBuffer, true);
// Assert that the element is valid // Assert that the element is valid
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::NoError); Assert::AreEqual(true, result.first == ShortcutErrorType::NoError);
}); });
} }
@ -1388,10 +1386,10 @@ namespace RemappingUITests
}; };
// Act // Act
std::pair<KeyboardManagerHelper::ErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(0, 1, 1, selectedCodes, testApp1, true, remapBuffer, true); std::pair<ShortcutErrorType, BufferValidationHelpers::DropDownAction> result = BufferValidationHelpers::ValidateShortcutBufferElement(0, 1, 1, selectedCodes, testApp1, true, remapBuffer, true);
// Assert // Assert
Assert::AreEqual(true, result.first == KeyboardManagerHelper::ErrorType::ShortcutDisableAsActionKey); Assert::AreEqual(true, result.first == ShortcutErrorType::ShortcutDisableAsActionKey);
Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction); Assert::AreEqual(true, result.second == BufferValidationHelpers::DropDownAction::NoAction);
} }
}; };

View File

@ -0,0 +1,295 @@
#include "pch.h"
#include "CppUnitTest.h"
#include <keyboardmanager/KeyboardManagerEditorLibrary/ShortcutErrorType.h>
#include <keyboardmanager/common/Helpers.h>
#include <common/interop/keyboard_layout.h>
#include <keyboardmanager/KeyboardManagerEditorLibrary/EditorHelpers.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace EditorHelpersTests
{
TEST_CLASS (EditorHelpersTests)
{
public:
// Test if the DoKeysOverlap method returns SameKeyPreviouslyMapped on passing the same key for both arguments
TEST_METHOD (DoKeysOverlap_ShouldReturnSameKeyPreviouslyMapped_OnPassingSameKeyForBothArguments)
{
// Arrange
DWORD key1 = 0x41;
DWORD key2 = key1;
// Act
auto result = EditorHelpers::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::SameKeyPreviouslyMapped);
}
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing left modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingLeftModifierAndCommonModifierOfSameType)
{
// Arrange
DWORD key1 = VK_LCONTROL;
DWORD key2 = VK_CONTROL;
// Act
auto result = EditorHelpers::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::ConflictingModifierKey);
}
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing right modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingRightModifierAndCommonModifierOfSameType)
{
// Arrange
DWORD key1 = VK_RCONTROL;
DWORD key2 = VK_CONTROL;
// Act
auto result = EditorHelpers::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::ConflictingModifierKey);
}
// Test if the DoKeysOverlap method returns NoError on passing left modifier and right modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingLeftModifierAndRightModifierOfSameType)
{
// Arrange
DWORD key1 = VK_LCONTROL;
DWORD key2 = VK_RCONTROL;
// Act
auto result = EditorHelpers::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
// Test if the DoKeysOverlap method returns NoError on passing keys of different types
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingKeysOfDifferentTypes)
{
// Arrange
DWORD key1 = VK_CONTROL;
DWORD key2 = VK_SHIFT;
// Act
auto result = EditorHelpers::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
// Test if the DoKeysOverlap method returns NoError on passing different action keys
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingDifferentActionKeys)
{
// Arrange
DWORD key1 = 0x41;
DWORD key2 = 0x42;
// Act
auto result = EditorHelpers::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
// Test if the CheckRepeatedModifier method returns true on passing vector with same modifier repeated
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingSameModifierRepeated)
{
// Arrange
std::vector<int32_t> keys = { VK_CONTROL, VK_CONTROL, 0x41 };
// Act
bool result = EditorHelpers::CheckRepeatedModifier(keys, VK_CONTROL);
// Assert
Assert::IsTrue(result);
}
// Test if the CheckRepeatedModifier method returns true on passing vector with conflicting modifier repeated
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingConflictingModifierRepeated)
{
// Arrange
std::vector<int32_t> keys = { VK_CONTROL, VK_LCONTROL, 0x41 };
// Act
bool result = EditorHelpers::CheckRepeatedModifier(keys, VK_LCONTROL);
// Assert
Assert::IsTrue(result);
}
// Test if the CheckRepeatedModifier method returns false on passing vector with different modifiers
TEST_METHOD (CheckRepeatedModifier_ShouldReturnFalse_OnPassingDifferentModifiers)
{
// Arrange
std::vector<int32_t> keys = { VK_CONTROL, VK_SHIFT, 0x41 };
// Act
bool result = EditorHelpers::CheckRepeatedModifier(keys, VK_SHIFT);
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns false on passing shortcut with null action key
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithNullActionKey)
{
// Arrange
Shortcut s;
s.SetKey(NULL);
// Act
bool result = EditorHelpers::IsValidShortcut(s);
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns false on passing shortcut with only action key
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyActionKey)
{
// Arrange
Shortcut s;
s.SetKey(0x41);
// Act
bool result = EditorHelpers::IsValidShortcut(s);
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns false on passing shortcut with only modifier keys
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyModifierKeys)
{
// Arrange
Shortcut s;
s.SetKey(VK_CONTROL);
s.SetKey(VK_SHIFT);
// Act
bool result = EditorHelpers::IsValidShortcut(s);
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns true on passing shortcut with modifier and action key
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithModifierAndActionKey)
{
// Arrange
Shortcut s;
s.SetKey(VK_CONTROL);
s.SetKey(0x41);
// Act
bool result = EditorHelpers::IsValidShortcut(s);
// Assert
Assert::IsTrue(result);
}
// Test if the DoKeysOverlap method returns NoError on passing invalid shortcut for one of the arguments
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingInvalidShortcutForOneOfTheArguments)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ NULL });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x41 });
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
// Test if the DoKeysOverlap method returns SameShortcutPreviouslyMapped on passing same shortcut for both arguments
TEST_METHOD (DoKeysOverlap_ShouldReturnSameShortcutPreviouslyMapped_OnPassingSameShortcutForBothArguments)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x41 });
Shortcut s2 = s1;
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::SameShortcutPreviouslyMapped);
}
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different action keys
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentActionKeys)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x41 });
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different modifiers
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentModifiers)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_SHIFT, 0x42 });
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndCommonModifierOfSameType)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_LCONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x42 });
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::ConflictingModifierShortcut);
}
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with right modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithRightModifierAndCommonModifierOfSameType)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_RCONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x42 });
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::ConflictingModifierShortcut);
}
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and right modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndRightModifierOfSameType)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_LCONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_RCONTROL, 0x42 });
// Act
auto result = EditorHelpers::DoShortcutsOverlap(s1, s2);
// Assert
Assert::IsTrue(result == ShortcutErrorType::NoError);
}
};
}

View File

@ -40,17 +40,14 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="BufferValidationTests.cpp" /> <ClCompile Include="BufferValidationTests.cpp" />
<ClCompile Include="LoadingAndSavingRemappingTests.cpp" /> <ClCompile Include="LoadingAndSavingRemappingTests.cpp" />
<ClCompile Include="MockedInput.cpp" />
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="TestHelpers.cpp" /> <ClCompile Include="EditorHelpersTests.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="MockedInput.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="resource.h" /> <ClInclude Include="resource.h" />
<ClInclude Include="TestHelpers.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj"> <ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">

View File

@ -24,10 +24,7 @@
<ClCompile Include="BufferValidationTests.cpp"> <ClCompile Include="BufferValidationTests.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="TestHelpers.cpp"> <ClCompile Include="EditorHelpersTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MockedInput.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
@ -35,15 +32,9 @@
<ClInclude Include="pch.h"> <ClInclude Include="pch.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="MockedInput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h"> <ClInclude Include="resource.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="TestHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="KeyboardManagerEditorTest.rc"> <ResourceCompile Include="KeyboardManagerEditorTest.rc">

View File

@ -1,10 +1,9 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include <keyboardmanager/common/KeyboardManagerState.h> #include <keyboardmanager/common/MappingConfiguration.h>
#include <keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h> #include <keyboardmanager/KeyboardManagerEditorLibrary/LoadingAndSavingRemappingHelper.h>
#include "TestHelpers.h"
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <keyboardmanager/common/ErrorTypes.h> #include <keyboardmanager/KeyboardManagerEditorLibrary/ShortcutErrorType.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@ -27,7 +26,7 @@ namespace RemappingUITests
RemapBuffer remapBuffer; RemapBuffer remapBuffer;
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -41,7 +40,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, 0x43 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, 0x43 }), std::wstring()));
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -61,7 +60,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, s2 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, s2 }), std::wstring()));
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -81,7 +80,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ s2, 0x42 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ s2, 0x42 }), std::wstring()));
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -107,7 +106,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest2 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest2 }), std::wstring()));
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -135,7 +134,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, dest2 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x42, dest2 }), std::wstring()));
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -148,7 +147,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, NULL }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, NULL }), std::wstring()));
// Assert that remapping set is invalid // Assert that remapping set is invalid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::RemapUnsuccessful);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -163,7 +162,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring()));
// Assert that remapping set is invalid // Assert that remapping set is invalid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::RemapUnsuccessful);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -180,7 +179,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x41, src1 }), std::wstring()));
// Assert that remapping set is invalid // Assert that remapping set is invalid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::RemapUnsuccessful);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -200,7 +199,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), std::wstring()));
// Assert that remapping set is invalid // Assert that remapping set is invalid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::RemapUnsuccessful);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -220,7 +219,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp1)); remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp1));
// Assert that remapping set is invalid // Assert that remapping set is invalid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::RemapUnsuccessful); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::RemapUnsuccessful);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -240,7 +239,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp2)); remapBuffer.push_back(std::make_pair(RemapBufferItem({ src1, dest1 }), testApp2));
// Assert that remapping set is valid // Assert that remapping set is valid
bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == KeyboardManagerHelper::ErrorType::NoError); bool isSuccess = (LoadingAndSavingRemappingHelper::CheckIfRemappingsAreValid(remapBuffer) == ShortcutErrorType::NoError);
Assert::AreEqual(true, isSuccess); Assert::AreEqual(true, isSuccess);
} }
@ -361,23 +360,23 @@ namespace RemappingUITests
// Test if the ApplySingleKeyRemappings method resets the keyboard manager state's single key remappings on passing an empty buffer // Test if the ApplySingleKeyRemappings method resets the keyboard manager state's single key remappings on passing an empty buffer
TEST_METHOD (ApplySingleKeyRemappings_ShouldResetSingleKeyRemappings_OnPassingEmptyBuffer) TEST_METHOD (ApplySingleKeyRemappings_ShouldResetSingleKeyRemappings_OnPassingEmptyBuffer)
{ {
KeyboardManagerState testState; MappingConfiguration testShortcuts;
RemapBuffer remapBuffer; RemapBuffer remapBuffer;
// Remap A to B // Remap A to B
testState.AddSingleKeyRemap(0x41, 0x42); testShortcuts.AddSingleKeyRemap(0x41, 0x42);
// Apply the single key remaps from the buffer to the keyboard manager state variable // Apply the single key remaps from the buffer to the keyboard manager state variable
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false); LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testShortcuts, remapBuffer, false);
// Assert that single key remapping in the kbm state variable is empty // Assert that single key remapping in the kbm state variable is empty
Assert::AreEqual((size_t)0, testState.singleKeyReMap.size()); Assert::AreEqual((size_t)0, testShortcuts.singleKeyReMap.size());
} }
// Test if the ApplySingleKeyRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid // Test if the ApplySingleKeyRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid
TEST_METHOD (ApplySingleKeyRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings) TEST_METHOD (ApplySingleKeyRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings)
{ {
KeyboardManagerState testState; MappingConfiguration testShortcuts;
RemapBuffer remapBuffer; RemapBuffer remapBuffer;
// Add A->B, B->Ctrl+V, C to incomplete shortcut and D to incomplete key remappings to the buffer // Add A->B, B->Ctrl+V, C to incomplete shortcut and D to incomplete key remappings to the buffer
@ -392,21 +391,21 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x44, s2 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ 0x44, s2 }), std::wstring()));
// Apply the single key remaps from the buffer to the keyboard manager state variable // Apply the single key remaps from the buffer to the keyboard manager state variable
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false); LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testShortcuts, remapBuffer, false);
// Expected A remapped to B, B remapped to Ctrl+V // Expected A remapped to B, B remapped to Ctrl+V
SingleKeyRemapTable expectedTable; SingleKeyRemapTable expectedTable;
expectedTable[0x41] = 0x42; expectedTable[0x41] = 0x42;
expectedTable[0x42] = s1; expectedTable[0x42] = s1;
bool areTablesEqual = (expectedTable == testState.singleKeyReMap); bool areTablesEqual = (expectedTable == testShortcuts.singleKeyReMap);
Assert::AreEqual(true, areTablesEqual); Assert::AreEqual(true, areTablesEqual);
} }
// Test if the ApplySingleKeyRemappings method splits common modifiers to their left and right version when copying to the keyboard manager state variable if remappings from common modifiers are passed // Test if the ApplySingleKeyRemappings method splits common modifiers to their left and right version when copying to the keyboard manager state variable if remappings from common modifiers are passed
TEST_METHOD (ApplySingleKeyRemappings_ShouldSplitRemappingsFromCommonModifiers_OnPassingBufferWithSomeMappingsFromCommonModifiers) TEST_METHOD (ApplySingleKeyRemappings_ShouldSplitRemappingsFromCommonModifiers_OnPassingBufferWithSomeMappingsFromCommonModifiers)
{ {
KeyboardManagerState testState; MappingConfiguration testShortcuts;
RemapBuffer remapBuffer; RemapBuffer remapBuffer;
// Add Ctrl->A, Alt->B, Shift->C and Win->D remappings to the buffer // Add Ctrl->A, Alt->B, Shift->C and Win->D remappings to the buffer
@ -416,7 +415,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ CommonSharedConstants::VK_WIN_BOTH, 0x44 }), std::wstring())); remapBuffer.push_back(std::make_pair(RemapBufferItem({ CommonSharedConstants::VK_WIN_BOTH, 0x44 }), std::wstring()));
// Apply the single key remaps from the buffer to the keyboard manager state variable // Apply the single key remaps from the buffer to the keyboard manager state variable
LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testState, remapBuffer, false); LoadingAndSavingRemappingHelper::ApplySingleKeyRemappings(testShortcuts, remapBuffer, false);
// Expected LCtrl/RCtrl remapped to A, LAlt/RAlt to B, LShift/RShift to C, LWin/RWin to D // Expected LCtrl/RCtrl remapped to A, LAlt/RAlt to B, LShift/RShift to C, LWin/RWin to D
SingleKeyRemapTable expectedTable; SingleKeyRemapTable expectedTable;
@ -429,14 +428,14 @@ namespace RemappingUITests
expectedTable[VK_LWIN] = 0x44; expectedTable[VK_LWIN] = 0x44;
expectedTable[VK_RWIN] = 0x44; expectedTable[VK_RWIN] = 0x44;
bool areTablesEqual = (expectedTable == testState.singleKeyReMap); bool areTablesEqual = (expectedTable == testShortcuts.singleKeyReMap);
Assert::AreEqual(true, areTablesEqual); Assert::AreEqual(true, areTablesEqual);
} }
// Test if the ApplyShortcutRemappings method resets the keyboard manager state's os level and app specific shortcut remappings on passing an empty buffer // Test if the ApplyShortcutRemappings method resets the keyboard manager state's os level and app specific shortcut remappings on passing an empty buffer
TEST_METHOD (ApplyShortcutRemappings_ShouldResetShortcutRemappings_OnPassingEmptyBuffer) TEST_METHOD (ApplyShortcutRemappings_ShouldResetShortcutRemappings_OnPassingEmptyBuffer)
{ {
KeyboardManagerState testState; MappingConfiguration testShortcuts;
RemapBuffer remapBuffer; RemapBuffer remapBuffer;
// Remap Ctrl+A to Ctrl+B for all apps and Ctrl+C to Alt+V for testApp1 // Remap Ctrl+A to Ctrl+B for all apps and Ctrl+C to Alt+V for testApp1
@ -452,21 +451,21 @@ namespace RemappingUITests
Shortcut dest2; Shortcut dest2;
dest2.SetKey(VK_MENU); dest2.SetKey(VK_MENU);
dest2.SetKey(0x56); dest2.SetKey(0x56);
testState.AddOSLevelShortcut(src1, dest1); testShortcuts.AddOSLevelShortcut(src1, dest1);
testState.AddAppSpecificShortcut(testApp1, src1, dest1); testShortcuts.AddAppSpecificShortcut(testApp1, src1, dest1);
// Apply the shortcut remaps from the buffer to the keyboard manager state variable // Apply the shortcut remaps from the buffer to the keyboard manager state variable
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testState, remapBuffer, false); LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testShortcuts, remapBuffer, false);
// Assert that shortcut remappings in the kbm state variable is empty // Assert that shortcut remappings in the kbm state variable is empty
Assert::AreEqual((size_t)0, testState.osLevelShortcutReMap.size()); Assert::AreEqual((size_t)0, testShortcuts.osLevelShortcutReMap.size());
Assert::AreEqual((size_t)0, testState.appSpecificShortcutReMap.size()); Assert::AreEqual((size_t)0, testShortcuts.appSpecificShortcutReMap.size());
} }
// Test if the ApplyShortcutRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid // Test if the ApplyShortcutRemappings method copies only the valid remappings to the keyboard manager state variable when some of the remappings are invalid
TEST_METHOD (ApplyShortcutRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings) TEST_METHOD (ApplyShortcutRemappings_ShouldCopyOnlyValidRemappings_OnPassingBufferWithSomeInvalidRemappings)
{ {
KeyboardManagerState testState; MappingConfiguration testShortcuts;
RemapBuffer remapBuffer; RemapBuffer remapBuffer;
// Add Ctrl+A->Ctrl+B, Ctrl+C->Alt+V, Ctrl+F->incomplete shortcut and Ctrl+G->incomplete key os level remappings to buffer // Add Ctrl+A->Ctrl+B, Ctrl+C->Alt+V, Ctrl+F->incomplete shortcut and Ctrl+G->incomplete key os level remappings to buffer
@ -501,7 +500,7 @@ namespace RemappingUITests
remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest4 }), testApp1)); remapBuffer.push_back(std::make_pair(RemapBufferItem({ src2, dest4 }), testApp1));
// Apply the shortcut remaps from the buffer to the keyboard manager state variable // Apply the shortcut remaps from the buffer to the keyboard manager state variable
LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testState, remapBuffer, false); LoadingAndSavingRemappingHelper::ApplyShortcutRemappings(testShortcuts, remapBuffer, false);
// Ctrl+A->Ctrl+B and Ctrl+C->Alt+V // Ctrl+A->Ctrl+B and Ctrl+C->Alt+V
ShortcutRemapTable expectedOSLevelTable; ShortcutRemapTable expectedOSLevelTable;
@ -513,8 +512,8 @@ namespace RemappingUITests
expectedAppSpecificLevelTable[testApp1][src3] = RemapShortcut(dest2); expectedAppSpecificLevelTable[testApp1][src3] = RemapShortcut(dest2);
expectedAppSpecificLevelTable[testApp1][src4] = RemapShortcut(dest1); expectedAppSpecificLevelTable[testApp1][src4] = RemapShortcut(dest1);
bool areOSLevelTablesEqual = (expectedOSLevelTable == testState.osLevelShortcutReMap); bool areOSLevelTablesEqual = (expectedOSLevelTable == testShortcuts.osLevelShortcutReMap);
bool areAppSpecificTablesEqual = (expectedAppSpecificLevelTable == testState.appSpecificShortcutReMap); bool areAppSpecificTablesEqual = (expectedAppSpecificLevelTable == testShortcuts.appSpecificShortcutReMap);
Assert::AreEqual(true, areOSLevelTablesEqual); Assert::AreEqual(true, areOSLevelTablesEqual);
Assert::AreEqual(true, areAppSpecificTablesEqual); Assert::AreEqual(true, areAppSpecificTablesEqual);
} }

View File

@ -1,161 +0,0 @@
#include "pch.h"
#include "MockedInput.h"
// Set the keyboard hook procedure to be tested
void MockedInput::SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure)
{
hookProc = hookProcedure;
}
// Function to simulate keyboard input - arguments and return value based on SendInput function (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)
UINT MockedInput::SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize)
{
// Iterate over inputs
for (UINT i = 0; i < cInputs; i++)
{
LowlevelKeyboardEvent keyEvent;
// Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-syskeydown
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
{
if (keyboardState[VK_MENU] == true)
{
keyEvent.wParam = WM_SYSKEYUP;
}
else
{
keyEvent.wParam = WM_KEYUP;
}
}
else
{
if (pInputs[i].ki.wVk == VK_F10 || keyboardState[VK_MENU] == true)
{
keyEvent.wParam = WM_SYSKEYDOWN;
}
else
{
keyEvent.wParam = WM_KEYDOWN;
}
}
KBDLLHOOKSTRUCT lParam = {};
// Set only vkCode and dwExtraInfo since other values are unused
lParam.vkCode = pInputs[i].ki.wVk;
lParam.dwExtraInfo = pInputs[i].ki.dwExtraInfo;
keyEvent.lParam = &lParam;
// If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
if (sendVirtualInputCallCondition == nullptr || sendVirtualInputCallCondition(&keyEvent))
{
sendVirtualInputCallCount++;
}
// Call low level hook handler
intptr_t result = MockedKeyboardHook(&keyEvent);
// Set keyboard state if the hook does not suppress the input
if (result == 0)
{
// If key up flag is set, then set keyboard state to false
keyboardState[pInputs[i].ki.wVk] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
// Handling modifier key codes
switch (pInputs[i].ki.wVk)
{
case VK_CONTROL:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
{
keyboardState[VK_LCONTROL] = false;
keyboardState[VK_RCONTROL] = false;
}
break;
case VK_LCONTROL:
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
case VK_RCONTROL:
keyboardState[VK_CONTROL] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
case VK_MENU:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
{
keyboardState[VK_LMENU] = false;
keyboardState[VK_RMENU] = false;
}
break;
case VK_LMENU:
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
case VK_RMENU:
keyboardState[VK_MENU] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
case VK_SHIFT:
if (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP)
{
keyboardState[VK_LSHIFT] = false;
keyboardState[VK_RSHIFT] = false;
}
break;
case VK_LSHIFT:
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
case VK_RSHIFT:
keyboardState[VK_SHIFT] = (pInputs[i].ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
break;
}
}
}
return cInputs;
}
// Function to simulate keyboard hook behavior
intptr_t MockedInput::MockedKeyboardHook(LowlevelKeyboardEvent* data)
{
// If the hookProc is set to null, then skip the hook
if (hookProc != nullptr)
{
return hookProc(data);
}
else
{
return 0;
}
}
// Function to get the state of a particular key
bool MockedInput::GetVirtualKeyState(int key)
{
return keyboardState[key];
}
// Function to reset the mocked keyboard state
void MockedInput::ResetKeyboardState()
{
std::fill(keyboardState.begin(), keyboardState.end(), false);
}
// Function to set SendVirtualInput call count condition
void MockedInput::SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition)
{
sendVirtualInputCallCount = 0;
sendVirtualInputCallCondition = condition;
}
// Function to get SendVirtualInput call count
int MockedInput::GetSendVirtualInputCallCount()
{
return sendVirtualInputCallCount;
}
// Function to get the foreground process name
void MockedInput::SetForegroundProcess(std::wstring process)
{
currentProcess = process;
}
// Function to get the foreground process name
void MockedInput::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
{
foregroundProcess = currentProcess;
}

View File

@ -1,57 +0,0 @@
#pragma once
#include <keyboardmanager/common/InputInterface.h>
#include <vector>
#include <functional>
#include <common/hooks/LowlevelKeyboardEvent.h>
// Class for mocked keyboard input
class MockedInput :
public KeyboardManagerInput::InputInterface
{
private:
// Stores the states for all the keys - false for key up, and true for key down
std::vector<bool> keyboardState;
// Function to be executed as a low level hook. By default it is nullptr so the hook is skipped
std::function<intptr_t(LowlevelKeyboardEvent*)> hookProc;
// Stores the count of sendVirtualInput calls given if the condition sendVirtualInputCallCondition is satisfied
int sendVirtualInputCallCount = 0;
std::function<bool(LowlevelKeyboardEvent*)> sendVirtualInputCallCondition;
std::wstring currentProcess;
public:
MockedInput()
{
keyboardState.resize(256, false);
}
// Set the keyboard hook procedure to be tested
void SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure);
// Function to simulate keyboard input
UINT SendVirtualInput(UINT cInputs, LPINPUT pInputs, int cbSize);
// Function to simulate keyboard hook behavior
intptr_t MockedKeyboardHook(LowlevelKeyboardEvent* data);
// Function to get the state of a particular key
bool GetVirtualKeyState(int key);
// Function to reset the mocked keyboard state
void ResetKeyboardState();
// Function to set SendVirtualInput call count condition
void SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition);
// Function to get SendVirtualInput call count
int GetSendVirtualInputCallCount();
// Function to get the foreground process name
void SetForegroundProcess(std::wstring process);
// Function to get the foreground process name
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess);
};

View File

@ -1,25 +0,0 @@
#include "pch.h"
#include "TestHelpers.h"
#include "MockedInput.h"
#include "keyboardmanager/common/KeyboardManagerState.h"
namespace TestHelpers
{
// Function to reset the environment variables for tests
void ResetTestEnv(MockedInput& input, KeyboardManagerState& state)
{
input.ResetKeyboardState();
input.SetHookProc(nullptr);
input.SetSendVirtualInputTestHandler(nullptr);
input.SetForegroundProcess(L"");
state.ClearSingleKeyRemaps();
state.ClearOSLevelShortcuts();
state.ClearAppSpecificShortcuts();
// Allocate memory for the keyboardManagerState activatedApp member to avoid CRT assert errors
std::wstring maxLengthString;
maxLengthString.resize(MAX_PATH);
state.SetActivatedApp(maxLengthString);
state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
}
}

View File

@ -1,12 +0,0 @@
#pragma once
class MockedInput;
class KeyboardManagerState;
namespace TestHelpers
{
// Function to reset the environment variables for tests
void ResetTestEnv(MockedInput& input, KeyboardManagerState& state);
// Function to return the index of the given key code from the drop down key list
int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList);
}

View File

@ -69,9 +69,6 @@
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj"> <ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project> <Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
</ProjectReference>
<ProjectReference Include="..\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj"> <ProjectReference Include="..\KeyboardManagerEngineLibrary\KeyboardManagerEngineLibrary.vcxproj">
<Project>{e496b7fc-1e99-4bab-849b-0e8367040b02}</Project> <Project>{e496b7fc-1e99-4bab-849b-0e8367040b02}</Project>
</ProjectReference> </ProjectReference>

View File

@ -3,10 +3,10 @@
#include <common/utils/ProcessWaiter.h> #include <common/utils/ProcessWaiter.h>
#include <common/utils/winapi_error.h> #include <common/utils/winapi_error.h>
#include <common/utils/logger_helper.h> #include <common/utils/logger_helper.h>
#include <common/utils/UnhandledExceptionHandler_x64.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h> #include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h>
#include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
#include <common/utils/UnhandledExceptionHandler_x64.h>
const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEngine_InstanceMutex"; const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEngine_InstanceMutex";

View File

@ -3,7 +3,6 @@
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <keyboardmanager/common/KeyboardManagerState.h>
#include <keyboardmanager/common/InputInterface.h> #include <keyboardmanager/common/InputInterface.h>
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
#include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/trace.h>
@ -11,12 +10,12 @@
namespace KeyboardEventHandlers namespace KeyboardEventHandlers
{ {
// Function to a handle a single key remap // Function to a handle a single key remap
intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept
{ {
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
{ {
const auto remapping = keyboardManagerState.GetSingleKeyRemap(data->lParam->vkCode); const auto remapping = state.GetSingleKeyRemap(data->lParam->vkCode);
if (remapping) if (remapping)
{ {
auto it = remapping.value(); auto it = remapping.value();
@ -50,11 +49,11 @@ namespace KeyboardEventHandlers
DWORD target; DWORD target;
if (remapToKey) if (remapToKey)
{ {
target = KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second)); target = Helpers::FilterArtificialKeys(std::get<DWORD>(it->second));
} }
else else
{ {
target = KeyboardManagerHelper::FilterArtificialKeys(std::get<Shortcut>(it->second).GetActionKey()); target = Helpers::FilterArtificialKeys(std::get<Shortcut>(it->second).GetActionKey());
} }
// If Ctrl/Alt/Shift is being remapped to Caps Lock, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397 // If Ctrl/Alt/Shift is being remapped to Caps Lock, then reset the modifier key state to fix issues in certain IME keyboards where the IME shortcut gets invoked since it detects that the modifier and Caps Lock is pressed even though it is suppressed by the hook - More information at the GitHub issue https://github.com/microsoft/PowerToys/issues/3397
@ -67,11 +66,11 @@ namespace KeyboardEventHandlers
{ {
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
} }
else else
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)target, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
} }
} }
else else
@ -80,16 +79,16 @@ namespace KeyboardEventHandlers
Shortcut targetShortcut = std::get<Shortcut>(it->second); Shortcut targetShortcut = std::get<Shortcut>(it->second);
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
i++; i++;
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, 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 // 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 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 // 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
KeyboardManagerHelper::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetModifierKeyEvents(targetShortcut, ModifierKey::Disabled, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)targetShortcut.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
i++; i++;
} }
} }
@ -127,22 +126,22 @@ namespace KeyboardEventHandlers
/* This feature has not been enabled (code from proof of concept stage) /* This feature has not been enabled (code from proof of concept stage)
* *
// Function to a change a key's behavior from toggle to modifier // Function to a change a key's behavior from toggle to modifier
__declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept __declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, State& State) noexcept
{ {
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG)) if (!(data->lParam->dwExtraInfo & CommonSharedConstants::KEYBOARDMANAGER_INJECTED_FLAG))
{ {
// The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837 // The mutex should be unlocked before SendInput is called to avoid re-entry into the same mutex. More details can be found at https://github.com/microsoft/PowerToys/pull/1789#issuecomment-607555837
std::unique_lock<std::mutex> lock(keyboardManagerState.singleKeyToggleToMod_mutex); std::unique_lock<std::mutex> lock(State.singleKeyToggleToMod_mutex);
auto it = keyboardManagerState.singleKeyToggleToMod.find(data->lParam->vkCode); auto it = State.singleKeyToggleToMod.find(data->lParam->vkCode);
if (it != keyboardManagerState.singleKeyToggleToMod.end()) if (it != State.singleKeyToggleToMod.end())
{ {
// To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off // To avoid long presses (which leads to continuous keydown messages) from toggling the key on and off
if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN) if (data->wParam == WM_KEYDOWN || data->wParam == WM_SYSKEYDOWN)
{ {
if (it->second == false) if (it->second == false)
{ {
keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = true; State.singleKeyToggleToMod[data->lParam->vkCode] = true;
} }
else else
{ {
@ -153,8 +152,8 @@ namespace KeyboardEventHandlers
int key_count = 2; int key_count = 2;
LPINPUT keyEventList = new INPUT[size_t(key_count)](); LPINPUT keyEventList = new INPUT[size_t(key_count)]();
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG); Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SINGLEKEY_FLAG);
lock.unlock(); lock.unlock();
UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput(key_count, keyEventList, sizeof(INPUT));
@ -164,7 +163,7 @@ namespace KeyboardEventHandlers
if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP) if (data->wParam == WM_KEYUP || data->wParam == WM_SYSKEYUP)
{ {
lock.lock(); lock.lock();
keyboardManagerState.singleKeyToggleToMod[data->lParam->vkCode] = false; State.singleKeyToggleToMod[data->lParam->vkCode] = false;
lock.unlock(); lock.unlock();
} }
@ -177,16 +176,16 @@ namespace KeyboardEventHandlers
*/ */
// Function to a handle a shortcut remap // Function to a handle a shortcut remap
intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp) noexcept intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state, const std::optional<std::wstring>& activatedApp) noexcept
{ {
// Check if any shortcut is currently in the invoked state // Check if any shortcut is currently in the invoked state
bool isShortcutInvoked = keyboardManagerState.CheckShortcutRemapInvoked(activatedApp); bool isShortcutInvoked = state.CheckShortcutRemapInvoked(activatedApp);
// Get shortcut table for given activatedApp // Get shortcut table for given activatedApp
ShortcutRemapTable& reMap = keyboardManagerState.GetShortcutRemapTable(activatedApp); ShortcutRemapTable& reMap = state.GetShortcutRemapTable(activatedApp);
// Iterate through the shortcut remaps and apply whichever has been pressed // Iterate through the shortcut remaps and apply whichever has been pressed
for (auto& itShortcut : keyboardManagerState.GetSortedShortcutRemapVector(activatedApp)) for (auto& itShortcut : state.GetSortedShortcutRemapVector(activatedApp))
{ {
const auto it = reMap.find(itShortcut); const auto it = reMap.find(itShortcut);
@ -239,8 +238,8 @@ namespace KeyboardEventHandlers
keyEventList = new INPUT[key_count](); keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
int i = 0; int i = 0;
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
else else
@ -252,14 +251,14 @@ namespace KeyboardEventHandlers
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->Ctrl+V, press Win+A, since Win will be released here we need to send a dummy event before it
int i = 0; int i = 0;
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Release original shortcut state (release in reverse order of shortcut to be accurate) // Release original shortcut state (release in reverse order of shortcut to be accurate)
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut)); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut));
// Set new shortcut key down state // Set new shortcut key down state
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
@ -291,22 +290,22 @@ namespace KeyboardEventHandlers
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+A->V, press Win+A, since Win will be released here we need to send a dummy event before it
int i = 0; int i = 0;
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Release original shortcut state (release in reverse order of shortcut to be accurate) // Release original shortcut state (release in reverse order of shortcut to be accurate)
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Set target key down state // Set target key down state
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED) if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
// Modifier state reset might be required for this key depending on the shortcut's action and target modifier - ex: Win+Caps -> Ctrl // 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() == NULL && it->first.GetAltKey() == NULL && it->first.GetShiftKey() == NULL)
{ {
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), data->lParam->vkCode); ResetIfModifierKeyForLowerLevelKeyHandlers(ii, (WORD)Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), data->lParam->vkCode);
} }
} }
@ -314,7 +313,7 @@ namespace KeyboardEventHandlers
// If app specific shortcut is invoked, store the target application // If app specific shortcut is invoked, store the target application
if (activatedApp) if (activatedApp)
{ {
keyboardManagerState.SetActivatedApp(*activatedApp); state.SetActivatedApp(*activatedApp);
} }
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
@ -375,16 +374,16 @@ namespace KeyboardEventHandlers
int i = 0; int i = 0;
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first, data->lParam->vkCode);
// 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 it's own key message // 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 it's own key message
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut), data->lParam->vkCode);
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->Ctrl+V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else else
{ {
@ -397,7 +396,7 @@ namespace KeyboardEventHandlers
{ {
key_count--; key_count--;
} }
else if (ii.GetVirtualKeyState(KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)))) else if (ii.GetVirtualKeyState(Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))))
{ {
isTargetKeyPressed = true; isTargetKeyPressed = true;
} }
@ -414,15 +413,15 @@ namespace KeyboardEventHandlers
int i = 0; int i = 0;
if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed) if (std::get<DWORD>(it->second.targetShortcut) != CommonSharedConstants::VK_DISABLED && isTargetKeyPressed)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
// 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 it's own key message // 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 it's own key message
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, Shortcut(), data->lParam->vkCode);
// Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it // Send a dummy key event to prevent modifier press+release from being triggered. Example: Win+Ctrl+A->V, press Win+Ctrl+A and release A then Ctrl, since Win will be pressed here we need to send a dummy event after it
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
// Reset the remap state // Reset the remap state
@ -433,7 +432,7 @@ namespace KeyboardEventHandlers
// If app specific shortcut has finished invoking, reset the target application // If app specific shortcut has finished invoking, reset the target application
if (activatedApp) if (activatedApp)
{ {
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
// key count can be 0 if both shortcuts have same modifiers and the action key is not held down. delete will throw an error if keyEventList is empty // key count can be 0 if both shortcuts have same modifiers and the action key is not held down. delete will throw an error if keyEventList is empty
@ -464,11 +463,11 @@ namespace KeyboardEventHandlers
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
if (remapToShortcut) if (remapToShortcut)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else else
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
@ -485,7 +484,7 @@ namespace KeyboardEventHandlers
{ {
keyEventList = new INPUT[key_count](); keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED) else if (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED)
{ {
@ -497,14 +496,14 @@ namespace KeyboardEventHandlers
else else
{ {
// Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key) // Check if the keyboard state is clear apart from the target remap key (by creating a temp Shortcut object with the target key)
bool isKeyboardStateClear = Shortcut(std::vector<int32_t>({ KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii); bool isKeyboardStateClear = Shortcut(std::vector<int32_t>({ Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)) })).IsKeyboardStateClearExceptShortcut(ii);
// If the keyboard state is clear, we release the target key but do not reset the remap state // If the keyboard state is clear, we release the target key but do not reset the remap state
if (isKeyboardStateClear) if (isKeyboardStateClear)
{ {
keyEventList = new INPUT[key_count](); keyEventList = new INPUT[key_count]();
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
} }
else else
{ {
@ -519,14 +518,14 @@ namespace KeyboardEventHandlers
// Release new key state // Release new key state
int i = 0; int i = 0;
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
// Set original shortcut key down state except the action key // Set original shortcut key down state except the action key
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, 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 // 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
KeyboardManagerHelper::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetDummyKeyEvent(keyEventList, i, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
// Reset the remap state // Reset the remap state
it->second.isShortcutInvoked = false; it->second.isShortcutInvoked = false;
@ -536,7 +535,7 @@ namespace KeyboardEventHandlers
// If app specific shortcut has finished invoking, reset the target application // If app specific shortcut has finished invoking, reset the target application
if (activatedApp != KeyboardManagerConstants::NoActivatedApp) if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
{ {
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
} }
} }
@ -561,7 +560,7 @@ namespace KeyboardEventHandlers
{ {
// If it is not remapped to Disable // If it is not remapped to Disable
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps // Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))); ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
} }
// Suppress the modifier as it is already physically pressed // Suppress the modifier as it is already physically pressed
@ -601,20 +600,20 @@ namespace KeyboardEventHandlers
int i = 0; int i = 0;
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
// key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
i++; i++;
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu
@ -639,23 +638,23 @@ namespace KeyboardEventHandlers
int i = 0; int i = 0;
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)std::get<Shortcut>(it->second.targetShortcut).GetActionKey(), KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
KeyboardManagerHelper::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first); Helpers::SetModifierKeyEvents(std::get<Shortcut>(it->second.targetShortcut), it->second.winKeyInvoked, keyEventList, i, false, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, it->first);
// Set old shortcut key down state // Set old shortcut key down state
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG, std::get<Shortcut>(it->second.targetShortcut)); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, 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 // key down for original shortcut action key with shortcut flag so that we don't invoke the same shortcut remap again
if (isActionKeyPressed) if (isActionKeyPressed)
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
i++; i++;
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after shortcut to shortcut is released to open start menu
@ -669,7 +668,7 @@ namespace KeyboardEventHandlers
// If app specific shortcut has finished invoking, reset the target application // If app specific shortcut has finished invoking, reset the target application
if (activatedApp) if (activatedApp)
{ {
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
@ -681,7 +680,7 @@ namespace KeyboardEventHandlers
// For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A // For remap to key, if the original action key is not currently pressed, we should revert the keyboard state to the physical keys. If it is pressed we should not suppress the event so that shortcut to key remaps can be pressed with other keys. Example use-case: Alt+D->Win, allows Alt+D+A to perform Win+A
// Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together // Modifier state reset might be required for this key depending on the target key - ex: Ctrl+A -> Caps, Shift is pressed. System should not see Shift and Caps pressed together
ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))); ResetIfModifierKeyForLowerLevelKeyHandlers(ii, data->lParam->vkCode, Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)));
// If the shortcut is remapped to Disable then we have to revert the keyboard state to the physical keys // If the shortcut is remapped to Disable then we have to revert the keyboard state to the physical keys
bool isRemapToDisable = (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED); bool isRemapToDisable = (std::get<DWORD>(it->second.targetShortcut) == CommonSharedConstants::VK_DISABLED);
@ -690,7 +689,7 @@ namespace KeyboardEventHandlers
if (!isRemapToDisable) if (!isRemapToDisable)
{ {
// If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys // If the remap target key is currently pressed, then we do not have to revert the keyboard state to the physical keys
if (ii.GetVirtualKeyState((KeyboardManagerHelper::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut))))) if (ii.GetVirtualKeyState((Helpers::FilterArtificialKeys(std::get<DWORD>(it->second.targetShortcut)))))
{ {
isOriginalActionKeyPressed = true; isOriginalActionKeyPressed = true;
} }
@ -710,13 +709,13 @@ namespace KeyboardEventHandlers
// Set original shortcut key down state // Set original shortcut key down state
int i = 0; int i = 0;
KeyboardManagerHelper::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, true, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetModifierKeyEvents(it->first, it->second.winKeyInvoked, keyEventList, i, 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 // 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
if (isRemapToDisable && isOriginalActionKeyPressed) if (isRemapToDisable && isOriginalActionKeyPressed)
{ {
// Set original action key // Set original action key
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)it->first.GetActionKey(), 0, KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG);
i++; i++;
} }
else else
@ -725,7 +724,7 @@ namespace KeyboardEventHandlers
} }
// Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut // Send current key pressed without shortcut flag so that it can be reprocessed in case the physical keys pressed are a different remapped shortcut
KeyboardManagerHelper::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0); Helpers::SetKeyEvent(keyEventList, i, INPUT_KEYBOARD, (WORD)data->lParam->vkCode, 0, 0);
i++; i++;
// Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu // Do not send a dummy key as we want the current key press to behave as normal i.e. it can do press+release functionality if required. Required to allow a shortcut to Win key remap invoked directly after another shortcut to key remap is released to open start menu
@ -738,7 +737,7 @@ namespace KeyboardEventHandlers
// If app specific shortcut has finished invoking, reset the target application // If app specific shortcut has finished invoking, reset the target application
if (activatedApp != KeyboardManagerConstants::NoActivatedApp) if (activatedApp != KeyboardManagerConstants::NoActivatedApp)
{ {
keyboardManagerState.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp); state.SetActivatedApp(KeyboardManagerConstants::NoActivatedApp);
} }
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
@ -760,12 +759,12 @@ namespace KeyboardEventHandlers
} }
// Function to a handle an os-level shortcut remap // Function to a handle an os-level shortcut remap
intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept
{ {
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG) if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
{ {
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState); bool result = HandleShortcutRemapEvent(ii, data, state);
return result; return result;
} }
@ -773,7 +772,7 @@ namespace KeyboardEventHandlers
} }
// Function to a handle an app-specific shortcut remap // Function to a handle an app-specific shortcut remap
intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept
{ {
// Check if the key event was generated by KeyboardManager to avoid remapping events generated by us. // Check if the key event was generated by KeyboardManager to avoid remapping events generated by us.
if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG) if (data->lParam->dwExtraInfo != KeyboardManagerConstants::KEYBOARDMANAGER_SHORTCUT_FLAG)
@ -800,29 +799,29 @@ namespace KeyboardEventHandlers
AppSpecificShortcutRemapTable::iterator it; AppSpecificShortcutRemapTable::iterator it;
// Check if an app-specific shortcut is already activated // Check if an app-specific shortcut is already activated
if (keyboardManagerState.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp) if (state.GetActivatedApp() == KeyboardManagerConstants::NoActivatedApp)
{ {
query_string = process_name; query_string = process_name;
it = keyboardManagerState.appSpecificShortcutReMap.find(query_string); it = state.appSpecificShortcutReMap.find(query_string);
// If no entry is found, search for the process name without it's file extension // If no entry is found, search for the process name without it's file extension
if (it == keyboardManagerState.appSpecificShortcutReMap.end()) if (it == state.appSpecificShortcutReMap.end())
{ {
// Find index of the file extension // Find index of the file extension
size_t extensionIndex = process_name.find_last_of(L"."); size_t extensionIndex = process_name.find_last_of(L".");
query_string = process_name.substr(0, extensionIndex); query_string = process_name.substr(0, extensionIndex);
it = keyboardManagerState.appSpecificShortcutReMap.find(query_string); it = state.appSpecificShortcutReMap.find(query_string);
} }
} }
else else
{ {
query_string = keyboardManagerState.GetActivatedApp(); query_string = state.GetActivatedApp();
it = keyboardManagerState.appSpecificShortcutReMap.find(query_string); it = state.appSpecificShortcutReMap.find(query_string);
} }
if (it != keyboardManagerState.appSpecificShortcutReMap.end()) if (it != state.appSpecificShortcutReMap.end())
{ {
bool result = HandleShortcutRemapEvent(ii, data, keyboardManagerState, query_string); bool result = HandleShortcutRemapEvent(ii, data, state, query_string);
return result; return result;
} }
} }
@ -837,14 +836,14 @@ namespace KeyboardEventHandlers
if (target == VK_CAPITAL) if (target == VK_CAPITAL)
{ {
// If the argument is either of the Ctrl/Shift/Alt modifier key codes // If the argument is either of the Ctrl/Shift/Alt modifier key codes
if (KeyboardManagerHelper::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH)) if (Helpers::IsModifierKey(key) && !(key == VK_LWIN || key == VK_RWIN || key == CommonSharedConstants::VK_WIN_BOTH))
{ {
int key_count = 1; int key_count = 1;
LPINPUT keyEventList = new INPUT[size_t(key_count)](); LPINPUT keyEventList = new INPUT[size_t(key_count)]();
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
// Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)key, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, (WORD)key, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
delete[] keyEventList; delete[] keyEventList;
} }

View File

@ -1,32 +1,31 @@
#pragma once #pragma once
#include <common/hooks/LowlevelKeyboardEvent.h> #include <common/hooks/LowlevelKeyboardEvent.h>
#include "State.h"
namespace KeyboardManagerInput namespace KeyboardManagerInput
{ {
class InputInterface; class InputInterface;
} }
class KeyboardManagerState;
namespace KeyboardEventHandlers namespace KeyboardEventHandlers
{ {
// Function to a handle a single key remap // Function to a handle a single key remap
intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; intptr_t HandleSingleKeyRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept;
/* This feature has not been enabled (code from proof of concept stage) /* This feature has not been enabled (code from proof of concept stage)
// Function to a change a key's behavior from toggle to modifier // Function to a change a key's behavior from toggle to modifier
__declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; __declspec(dllexport) intptr_t HandleSingleKeyToggleToModEvent(InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept;
*/ */
// Function to a handle a shortcut remap // Function to a handle a shortcut remap
intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState, const std::optional<std::wstring>& activatedApp = std::nullopt) noexcept; intptr_t HandleShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state, const std::optional<std::wstring>& activatedApp = std::nullopt) noexcept;
// Function to a handle an os-level shortcut remap // Function to a handle an os-level shortcut remap
intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; intptr_t HandleOSLevelShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept;
// Function to a handle an app-specific shortcut remap // Function to a handle an app-specific shortcut remap
intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, KeyboardManagerState& keyboardManagerState) noexcept; intptr_t HandleAppSpecificShortcutRemapEvent(KeyboardManagerInput::InputInterface& ii, LowlevelKeyboardEvent* data, State& state) noexcept;
// Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required // Function to ensure Ctrl/Shift/Alt modifier key state is not detected as pressed down by applications which detect keys at a lower level than hooks when it is remapped for scenarios where its required
void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target); void ResetIfModifierKeyForLowerLevelKeyHandlers(KeyboardManagerInput::InputInterface& ii, DWORD key, DWORD target);

View File

@ -12,7 +12,6 @@
#include <keyboardmanager/common/KeyboardManagerConstants.h> #include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
#include <keyboardmanager/common/KeyboardEventHandlers.h> #include <keyboardmanager/common/KeyboardEventHandlers.h>
#include <keyboardmanager/common/SettingsHelper.h>
#include <ctime> #include <ctime>
#include "KeyboardEventHandlers.h" #include "KeyboardEventHandlers.h"
@ -56,13 +55,13 @@ KeyboardManager::KeyboardManager()
void KeyboardManager::LoadSettings() void KeyboardManager::LoadSettings()
{ {
bool loadedSuccessful = SettingsHelper::LoadSettings(keyboardManagerState); bool loadedSuccessful = state.LoadSettings();
if (!loadedSuccessful) if (!loadedSuccessful)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
// retry once // retry once
SettingsHelper::LoadSettings(keyboardManagerState); state.LoadSettings();
} }
} }
@ -140,7 +139,7 @@ intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) n
} }
// Remap a key // Remap a key
intptr_t SingleKeyRemapResult = KeyboardEventHandlers::HandleSingleKeyRemapEvent(inputHandler, data, keyboardManagerState); intptr_t SingleKeyRemapResult = KeyboardEventHandlers::HandleSingleKeyRemapEvent(inputHandler, data, state);
// Single key remaps have priority. If a key is remapped, only the remapped version should be visible to the shortcuts and hence the event should be suppressed here. // Single key remaps have priority. If a key is remapped, only the remapped version should be visible to the shortcuts and hence the event should be suppressed here.
if (SingleKeyRemapResult == 1) if (SingleKeyRemapResult == 1)
@ -154,7 +153,7 @@ intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) n
*/ */
// Handle an app-specific shortcut remapping // Handle an app-specific shortcut remapping
intptr_t AppSpecificShortcutRemapResult = KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent(inputHandler, data, keyboardManagerState); intptr_t AppSpecificShortcutRemapResult = KeyboardEventHandlers::HandleAppSpecificShortcutRemapEvent(inputHandler, data, state);
// If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed. // If an app-specific shortcut is remapped then the os-level shortcut remapping should be suppressed.
if (AppSpecificShortcutRemapResult == 1) if (AppSpecificShortcutRemapResult == 1)
@ -163,5 +162,5 @@ intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) n
} }
// Handle an os-level shortcut remapping // Handle an os-level shortcut remapping
return KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, keyboardManagerState); return KeyboardEventHandlers::HandleOSLevelShortcutRemapEvent(inputHandler, data, state);
} }

View File

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <common/hooks/LowlevelKeyboardEvent.h>
#include <common/utils/EventWaiter.h> #include <common/utils/EventWaiter.h>
#include <keyboardmanager/common/KeyboardManagerState.h>
#include <keyboardmanager/common/Input.h> #include <keyboardmanager/common/Input.h>
#include "State.h"
class KeyboardManager class KeyboardManager
{ {
@ -27,7 +28,7 @@ private:
static KeyboardManager* keyboardManagerObjectPtr; static KeyboardManager* keyboardManagerObjectPtr;
// Variable which stores all the state information to be shared between the UI and back-end // Variable which stores all the state information to be shared between the UI and back-end
KeyboardManagerState keyboardManagerState; State state;
// Object of class which implements InputInterface. Required for calling library functions while enabling testing // Object of class which implements InputInterface. Required for calling library functions while enabling testing
KeyboardManagerInput::Input inputHandler; KeyboardManagerInput::Input inputHandler;

View File

@ -36,6 +36,7 @@
<ClInclude Include="KeyboardEventHandlers.h" /> <ClInclude Include="KeyboardEventHandlers.h" />
<ClInclude Include="KeyboardManager.h" /> <ClInclude Include="KeyboardManager.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="State.h" />
<ClInclude Include="trace.h" /> <ClInclude Include="trace.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -44,11 +45,17 @@
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="State.cpp" />
<ClCompile Include="trace.cpp" /> <ClCompile Include="trace.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\KeyboardManagerCommon.vcxproj">
<Project>{8affa899-0b73-49ec-8c50-0fadda57b2fc}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" /> <Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -27,6 +27,9 @@
<ClInclude Include="KeyboardEventHandlers.h"> <ClInclude Include="KeyboardEventHandlers.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="State.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
@ -41,6 +44,9 @@
<ClCompile Include="KeyboardEventHandlers.cpp"> <ClCompile Include="KeyboardEventHandlers.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="State.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />

View File

@ -0,0 +1,63 @@
#include "pch.h"
#include "State.h"
#include <optional>
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
std::optional<SingleKeyRemapTable::iterator> State::GetSingleKeyRemap(const DWORD& originalKey)
{
auto it = singleKeyReMap.find(originalKey);
if (it != singleKeyReMap.end())
{
return it;
}
return std::nullopt;
}
bool State::CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName)
{
// Assumes appName exists in the app-specific remap table
ShortcutRemapTable& currentRemapTable = appName ? appSpecificShortcutReMap[*appName] : osLevelShortcutReMap;
for (auto& it : currentRemapTable)
{
if (it.second.isShortcutInvoked)
{
return true;
}
}
return false;
}
// Function to get the source and target of a shortcut remap given the source shortcut. Returns nullopt if it isn't remapped
ShortcutRemapTable& State::GetShortcutRemapTable(const std::optional<std::wstring>& appName)
{
if (appName)
{
auto itTable = appSpecificShortcutReMap.find(*appName);
if (itTable != appSpecificShortcutReMap.end())
{
return itTable->second;
}
}
return osLevelShortcutReMap;
}
std::vector<Shortcut>& State::GetSortedShortcutRemapVector(const std::optional<std::wstring>& appName)
{
// Assumes appName exists in the app-specific remap table
return appName ? appSpecificShortcutReMapSortedKeys[*appName] : osLevelShortcutReMapSortedKeys;
}
// Sets the activated target application in app-specific shortcut
void State::SetActivatedApp(const std::wstring& appName)
{
activatedAppSpecificShortcutTarget = appName;
}
// Gets the activated target application in app-specific shortcut
std::wstring State::GetActivatedApp()
{
return activatedAppSpecificShortcutTarget;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <keyboardmanager/common/MappingConfiguration.h>
class State : public MappingConfiguration
{
private:
// Stores the activated target application in app-specific shortcut
std::wstring activatedAppSpecificShortcutTarget;
public:
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
std::optional<SingleKeyRemapTable::iterator> GetSingleKeyRemap(const DWORD& originalKey);
bool CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName);
// Function to get the source and target of a shortcut remap given the source shortcut. Returns nullopt if it isn't remapped
ShortcutRemapTable& GetShortcutRemapTable(const std::optional<std::wstring>& appName);
std::vector<Shortcut>& GetSortedShortcutRemapVector(const std::optional<std::wstring>& appName);
// Sets the activated target application in app-specific shortcut
void SetActivatedApp(const std::wstring& appName);
// Gets the activated target application in app-specific shortcut
std::wstring GetActivatedApp();
};

View File

@ -1,7 +1,7 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include "MockedInput.h" #include "MockedInput.h"
#include <keyboardmanager/common/KeyboardManagerState.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h>
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
#include "TestHelpers.h" #include "TestHelpers.h"
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
@ -15,7 +15,7 @@ namespace RemappingLogicTests
{ {
private: private:
KeyboardManagerInput::MockedInput mockedInputHandler; KeyboardManagerInput::MockedInput mockedInputHandler;
KeyboardManagerState testState; State testState;
std::wstring testApp1 = L"testtrocess1.exe"; std::wstring testApp1 = L"testtrocess1.exe";
std::wstring testApp2 = L"testprocess2.exe"; std::wstring testApp2 = L"testprocess2.exe";

View File

@ -1,143 +0,0 @@
#include "pch.h"
#include "CppUnitTest.h"
#include <keyboardmanager/common/ErrorTypes.h>
#include <keyboardmanager/common/Helpers.h>
#include "TestHelpers.h"
#include <common/interop/keyboard_layout.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace KeyboardManagerCommonTests
{
// Tests for methods in the KeyboardManagerHelper namespace
TEST_CLASS (KeyboardManagerHelperTests)
{
public:
TEST_METHOD_INITIALIZE(InitializeTestEnv)
{
}
// Test if the DoKeysOverlap method returns SameKeyPreviouslyMapped on passing the same key for both arguments
TEST_METHOD (DoKeysOverlap_ShouldReturnSameKeyPreviouslyMapped_OnPassingSameKeyForBothArguments)
{
// Arrange
DWORD key1 = 0x41;
DWORD key2 = key1;
// Act
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::SameKeyPreviouslyMapped);
}
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing left modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingLeftModifierAndCommonModifierOfSameType)
{
// Arrange
DWORD key1 = VK_LCONTROL;
DWORD key2 = VK_CONTROL;
// Act
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierKey);
}
// Test if the DoKeysOverlap method returns ConflictingModifierKey on passing right modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierKey_OnPassingRightModifierAndCommonModifierOfSameType)
{
// Arrange
DWORD key1 = VK_RCONTROL;
DWORD key2 = VK_CONTROL;
// Act
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierKey);
}
// Test if the DoKeysOverlap method returns NoError on passing left modifier and right modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingLeftModifierAndRightModifierOfSameType)
{
// Arrange
DWORD key1 = VK_LCONTROL;
DWORD key2 = VK_RCONTROL;
// Act
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
// Test if the DoKeysOverlap method returns NoError on passing keys of different types
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingKeysOfDifferentTypes)
{
// Arrange
DWORD key1 = VK_CONTROL;
DWORD key2 = VK_SHIFT;
// Act
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
// Test if the DoKeysOverlap method returns NoError on passing different action keys
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingDifferentActionKeys)
{
// Arrange
DWORD key1 = 0x41;
DWORD key2 = 0x42;
// Act
auto result = KeyboardManagerHelper::DoKeysOverlap(key1, key2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
// Test if the CheckRepeatedModifier method returns true on passing vector with same modifier repeated
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingSameModifierRepeated)
{
// Arrange
std::vector<int32_t> keys = { VK_CONTROL, VK_CONTROL, 0x41 };
// Act
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, VK_CONTROL);
// Assert
Assert::IsTrue(result);
}
// Test if the CheckRepeatedModifier method returns true on passing vector with conflicting modifier repeated
TEST_METHOD (CheckRepeatedModifier_ShouldReturnTrue_OnPassingConflictingModifierRepeated)
{
// Arrange
std::vector<int32_t> keys = { VK_CONTROL, VK_LCONTROL, 0x41 };
// Act
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, VK_LCONTROL);
// Assert
Assert::IsTrue(result);
}
// Test if the CheckRepeatedModifier method returns false on passing vector with different modifiers
TEST_METHOD (CheckRepeatedModifier_ShouldReturnFalse_OnPassingDifferentModifiers)
{
// Arrange
std::vector<int32_t> keys = { VK_CONTROL, VK_SHIFT, 0x41 };
// Act
bool result = KeyboardManagerHelper::CheckRepeatedModifier(keys, VK_SHIFT);
// Assert
Assert::IsFalse(result);
}
};
}

View File

@ -47,9 +47,7 @@
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="ShortcutTests.cpp" />
<ClCompile Include="SingleKeyRemappingTests.cpp" /> <ClCompile Include="SingleKeyRemappingTests.cpp" />
<ClCompile Include="HelperTests.cpp" />
<ClCompile Include="TestHelpers.cpp" /> <ClCompile Include="TestHelpers.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -39,12 +39,6 @@
<ClCompile Include="AppSpecificShortcutRemappingTests.cpp"> <ClCompile Include="AppSpecificShortcutRemappingTests.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="HelperTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ShortcutTests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="pch.h"> <ClInclude Include="pch.h">

View File

@ -1,7 +1,7 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include "MockedInput.h" #include "MockedInput.h"
#include <keyboardmanager/common/KeyboardManagerState.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h>
#include <keyboardmanager/common/KeyboardEventHandlers.h> #include <keyboardmanager/common/KeyboardEventHandlers.h>
#include "TestHelpers.h" #include "TestHelpers.h"
@ -14,7 +14,7 @@ namespace RemappingLogicTests
{ {
private: private:
KeyboardManagerInput::MockedInput mockedInputHandler; KeyboardManagerInput::MockedInput mockedInputHandler;
KeyboardManagerState testState; State testState;
public: public:
TEST_METHOD_INITIALIZE(InitializeTestEnv) TEST_METHOD_INITIALIZE(InitializeTestEnv)

View File

@ -1,7 +1,7 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include "MockedInput.h" #include "MockedInput.h"
#include <keyboardmanager/common/KeyboardManagerState.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h>
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
#include "TestHelpers.h" #include "TestHelpers.h"
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
@ -15,7 +15,7 @@ namespace RemappingLogicTests
{ {
private: private:
KeyboardManagerInput::MockedInput mockedInputHandler; KeyboardManagerInput::MockedInput mockedInputHandler;
KeyboardManagerState testState; State testState;
public: public:
TEST_METHOD_INITIALIZE(InitializeTestEnv) TEST_METHOD_INITIALIZE(InitializeTestEnv)

View File

@ -1,7 +1,7 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include "MockedInput.h" #include "MockedInput.h"
#include <keyboardmanager/common/KeyboardManagerState.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h>
#include <keyboardmanager/common/KeyboardEventHandlers.h> #include <keyboardmanager/common/KeyboardEventHandlers.h>
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
#include "TestHelpers.h" #include "TestHelpers.h"
@ -15,7 +15,7 @@ namespace RemappingLogicTests
{ {
private: private:
KeyboardManagerInput::MockedInput mockedInputHandler; KeyboardManagerInput::MockedInput mockedInputHandler;
KeyboardManagerState testState; State testState;
public: public:
TEST_METHOD_INITIALIZE(InitializeTestEnv) TEST_METHOD_INITIALIZE(InitializeTestEnv)
@ -36,7 +36,7 @@ namespace RemappingLogicTests
for (int i = 0; i < nInputs; i++) for (int i = 0; i < nInputs; i++)
{ {
// Set key events for all the extended keys // Set key events for all the extended keys
KeyboardManagerHelper::SetKeyEvent(input, i, INPUT_KEYBOARD, keyCodes[i], 0, 0); Helpers::SetKeyEvent(input, i, INPUT_KEYBOARD, keyCodes[i], 0, 0);
// Extended key flag should be set // Extended key flag should be set
Assert::AreEqual(true, bool(input[i].ki.dwFlags & KEYEVENTF_EXTENDEDKEY)); Assert::AreEqual(true, bool(input[i].ki.dwFlags & KEYEVENTF_EXTENDEDKEY));
} }
@ -49,7 +49,7 @@ namespace RemappingLogicTests
INPUT input[nInputs] = {}; INPUT input[nInputs] = {};
int index = 0; int index = 0;
KeyboardManagerHelper::SetDummyKeyEvent(input, index, 0); Helpers::SetDummyKeyEvent(input, index, 0);
// Assert that wScan for both inputs is 0 // Assert that wScan for both inputs is 0
Assert::AreEqual<unsigned int>(0, input[0].ki.wScan); Assert::AreEqual<unsigned int>(0, input[0].ki.wScan);

View File

@ -1,177 +0,0 @@
#include "pch.h"
#include "CppUnitTest.h"
#include <keyboardmanager/common/ErrorTypes.h>
#include <keyboardmanager/common/Shortcut.h>
#include <keyboardmanager/common/Helpers.h>
#include "TestHelpers.h"
#include <common/interop/keyboard_layout.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace KeyboardManagerCommonTests
{
// Tests for methods in the Shortcut class
TEST_CLASS (KeyboardManagerHelperTests)
{
public:
TEST_METHOD_INITIALIZE(InitializeTestEnv)
{
}
// Test if the IsValidShortcut method returns false on passing shortcut with null action key
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithNullActionKey)
{
// Arrange
Shortcut s;
s.SetKey(NULL);
// Act
bool result = s.IsValidShortcut();
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns false on passing shortcut with only action key
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyActionKey)
{
// Arrange
Shortcut s;
s.SetKey(0x41);
// Act
bool result = s.IsValidShortcut();
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns false on passing shortcut with only modifier keys
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithOnlyModifierKeys)
{
// Arrange
Shortcut s;
s.SetKey(VK_CONTROL);
s.SetKey(VK_SHIFT);
// Act
bool result = s.IsValidShortcut();
// Assert
Assert::IsFalse(result);
}
// Test if the IsValidShortcut method returns true on passing shortcut with modifier and action key
TEST_METHOD (IsValidShortcut_ShouldReturnFalse_OnPassingShortcutWithModifierAndActionKey)
{
// Arrange
Shortcut s;
s.SetKey(VK_CONTROL);
s.SetKey(0x41);
// Act
bool result = s.IsValidShortcut();
// Assert
Assert::IsTrue(result);
}
// Test if the DoKeysOverlap method returns NoError on passing invalid shortcut for one of the arguments
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingInvalidShortcutForOneOfTheArguments)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ NULL });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x41 });
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
// Test if the DoKeysOverlap method returns SameShortcutPreviouslyMapped on passing same shortcut for both arguments
TEST_METHOD (DoKeysOverlap_ShouldReturnSameShortcutPreviouslyMapped_OnPassingSameShortcutForBothArguments)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x41 });
Shortcut s2 = s1;
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped);
}
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different action keys
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentActionKeys)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x41 });
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
// Test if the DoKeysOverlap method returns NoError on passing shortcuts with different modifiers
TEST_METHOD (DoKeysOverlap_ShouldReturnNoError_OnPassingShortcutsWithDifferentModifiers)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_CONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_SHIFT, 0x42 });
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndCommonModifierOfSameType)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_LCONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x42 });
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut);
}
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with right modifier and common modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithRightModifierAndCommonModifierOfSameType)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_RCONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_CONTROL, 0x42 });
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut);
}
// Test if the DoKeysOverlap method returns ConflictingModifierShortcut on passing shortcuts with left modifier and right modifier
TEST_METHOD (DoKeysOverlap_ShouldReturnConflictingModifierShortcut_OnPassingShortcutsWithLeftModifierAndRightModifierOfSameType)
{
// Arrange
Shortcut s1(std::vector<int32_t>{ VK_LCONTROL, 0x42 });
Shortcut s2(std::vector<int32_t>{ VK_RCONTROL, 0x42 });
// Act
auto result = Shortcut::DoKeysOverlap(s1, s2);
// Assert
Assert::IsTrue(result == KeyboardManagerHelper::ErrorType::NoError);
}
};
}

View File

@ -1,7 +1,7 @@
#include "pch.h" #include "pch.h"
#include "CppUnitTest.h" #include "CppUnitTest.h"
#include "MockedInput.h" #include "MockedInput.h"
#include <keyboardmanager/common/KeyboardManagerState.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h>
#include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h> #include <keyboardmanager/KeyboardManagerEngineLibrary/KeyboardEventHandlers.h>
#include "TestHelpers.h" #include "TestHelpers.h"
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
@ -15,7 +15,7 @@ namespace RemappingLogicTests
{ {
private: private:
KeyboardManagerInput::MockedInput mockedInputHandler; KeyboardManagerInput::MockedInput mockedInputHandler;
KeyboardManagerState testState; State testState;
public: public:
TEST_METHOD_INITIALIZE(InitializeTestEnv) TEST_METHOD_INITIALIZE(InitializeTestEnv)

View File

@ -1,12 +1,12 @@
#include "pch.h" #include "pch.h"
#include "TestHelpers.h" #include "TestHelpers.h"
#include "MockedInput.h" #include "MockedInput.h"
#include "keyboardmanager/common/KeyboardManagerState.h" #include <keyboardmanager/KeyboardManagerEngineLibrary/State.h>
namespace TestHelpers namespace TestHelpers
{ {
// Function to reset the environment variables for tests // Function to reset the environment variables for tests
void ResetTestEnv(KeyboardManagerInput::MockedInput& input, KeyboardManagerState& state) void ResetTestEnv(KeyboardManagerInput::MockedInput& input, State& state)
{ {
input.ResetKeyboardState(); input.ResetKeyboardState();
input.SetHookProc(nullptr); input.SetHookProc(nullptr);

View File

@ -4,12 +4,12 @@ namespace KeyboardManagerInput
{ {
class MockedInput; class MockedInput;
} }
class KeyboardManagerState; class State;
namespace TestHelpers namespace TestHelpers
{ {
// Function to reset the environment variables for tests // Function to reset the environment variables for tests
void ResetTestEnv(KeyboardManagerInput::MockedInput& input, KeyboardManagerState& state); void ResetTestEnv(KeyboardManagerInput::MockedInput& input, State& state);
// Function to return the index of the given key code from the drop down key list // Function to return the index of the given key code from the drop down key list
int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList); int GetDropDownIndexFromDropDownList(DWORD key, const std::vector<DWORD>& keyList);

View File

@ -1,27 +0,0 @@
#pragma once
namespace KeyboardManagerHelper
{
// Type to store codes for different errors
enum class ErrorType
{
NoError,
SameKeyPreviouslyMapped,
MapToSameKey,
ConflictingModifierKey,
SameShortcutPreviouslyMapped,
MapToSameShortcut,
ConflictingModifierShortcut,
WinL,
CtrlAltDel,
RemapUnsuccessful,
SaveFailed,
ShortcutStartWithModifier,
ShortcutCannotHaveRepeatedModifier,
ShortcutAtleast2Keys,
ShortcutOneActionKey,
ShortcutNotMoreThanOneActionKey,
ShortcutMaxShortcutSizeOneActionKey,
ShortcutDisableAsActionKey
};
}

View File

@ -5,27 +5,10 @@
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include <common/utils/process_path.h> #include <common/utils/process_path.h>
#include "ErrorTypes.h"
#include "KeyboardManagerConstants.h" #include "KeyboardManagerConstants.h"
using namespace winrt::Windows::Foundation; namespace Helpers
namespace KeyboardManagerHelper
{ {
// Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter)
{
std::wstringstream ss(input);
std::wstring item;
std::vector<std::wstring> splittedStrings;
while (std::getline(ss, item, delimiter))
{
splittedStrings.push_back(item);
}
return splittedStrings;
}
// Function to check if the key is a modifier key // Function to check if the key is a modifier key
bool IsModifierKey(DWORD key) bool IsModifierKey(DWORD key)
{ {
@ -85,33 +68,6 @@ namespace KeyboardManagerHelper
} }
} }
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ErrorType DoKeysOverlap(DWORD first, DWORD second)
{
// If the keys are same
if (first == second)
{
return ErrorType::SameKeyPreviouslyMapped;
}
else if ((GetKeyType(first) == GetKeyType(second)) && GetKeyType(first) != KeyType::Action)
{
// If the keys are of the same modifier type and overlapping, i.e. one is L/R and other is common
if (((first == VK_LWIN && second == VK_RWIN) || (first == VK_RWIN && second == VK_LWIN)) || ((first == VK_LCONTROL && second == VK_RCONTROL) || (first == VK_RCONTROL && second == VK_LCONTROL)) || ((first == VK_LMENU && second == VK_RMENU) || (first == VK_RMENU && second == VK_LMENU)) || ((first == VK_LSHIFT && second == VK_RSHIFT) || (first == VK_RSHIFT && second == VK_LSHIFT)))
{
return ErrorType::NoError;
}
else
{
return ErrorType::ConflictingModifierKey;
}
}
// If no overlap
else
{
return ErrorType::NoError;
}
}
// Function to set the value of a key event based on the arguments // Function to set the value of a key event based on the arguments
void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo) void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo)
{ {
@ -207,22 +163,22 @@ namespace KeyboardManagerHelper
// 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 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(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked)) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckWinKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), 0, extraInfoFlag);
index++; index++;
} }
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), 0, extraInfoFlag);
index++; index++;
} }
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased))) if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckAltKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), 0, extraInfoFlag);
index++; index++;
} }
if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased))) if (shortcutToBeSent.GetShiftKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey()) && (keyToBeReleased == NULL || !shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), 0, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), 0, extraInfoFlag);
index++; index++;
} }
} }
@ -233,22 +189,22 @@ namespace KeyboardManagerHelper
// 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 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() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetShiftKey() != shortcutToCompare.GetShiftKey() || shortcutToBeSent.CheckShiftKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetShiftKey(), KEYEVENTF_KEYUP, extraInfoFlag);
index++; index++;
} }
if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased))) if (shortcutToBeSent.GetAltKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetAltKey() != shortcutToCompare.GetAltKey() || shortcutToBeSent.CheckAltKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetAltKey(), KEYEVENTF_KEYUP, extraInfoFlag);
index++; index++;
} }
if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased))) if (shortcutToBeSent.GetCtrlKey() != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetCtrlKey() != shortcutToCompare.GetCtrlKey() || shortcutToBeSent.CheckCtrlKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetCtrlKey(), KEYEVENTF_KEYUP, extraInfoFlag);
index++; index++;
} }
if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased))) if (shortcutToBeSent.GetWinKey(winKeyInvoked) != NULL && (shortcutToCompare.IsEmpty() || shortcutToBeSent.GetWinKey(winKeyInvoked) != shortcutToCompare.GetWinKey(winKeyInvoked) || shortcutToBeSent.CheckWinKey(keyToBeReleased)))
{ {
KeyboardManagerHelper::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), KEYEVENTF_KEYUP, extraInfoFlag); Helpers::SetKeyEvent(keyEventArray, index, INPUT_KEYBOARD, (WORD)shortcutToBeSent.GetWinKey(winKeyInvoked), KEYEVENTF_KEYUP, extraInfoFlag);
index++; index++;
} }
} }
@ -274,18 +230,4 @@ namespace KeyboardManagerHelper
return first.Size() > second.Size(); return first.Size() > second.Size();
}); });
} }
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(const std::vector<int32_t>& currentKeys, int selectedKeyCode)
{
// Count the number of keys that are equal to 'selectedKeyCode'
int numberOfSameType = 0;
for (int i = 0; i < currentKeys.size(); i++)
{
numberOfSameType += KeyboardManagerHelper::GetKeyType(selectedKeyCode) == KeyboardManagerHelper::GetKeyType(currentKeys[i]);
}
// If we have at least two keys equal to 'selectedKeyCode' than modifier was repeated
return numberOfSameType > 1;
}
} }

View File

@ -3,7 +3,7 @@
class LayoutMap; class LayoutMap;
namespace KeyboardManagerHelper namespace Helpers
{ {
// Type to distinguish between keys // Type to distinguish between keys
enum class KeyType enum class KeyType
@ -15,29 +15,12 @@ namespace KeyboardManagerHelper
Action Action
}; };
// Enum type to store possible decision for input in the low level hook
enum class KeyboardHookDecision
{
ContinueExec,
Suppress,
SkipHook
};
// Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter);
// Function to return if the key is an extended key which requires the use of the extended key flag
bool IsExtendedKey(DWORD key);
// Function to check if the key is a modifier key // Function to check if the key is a modifier key
bool IsModifierKey(DWORD key); bool IsModifierKey(DWORD key);
// Function to get the type of the key // Function to get the type of the key
KeyType GetKeyType(DWORD key); KeyType GetKeyType(DWORD key);
// Function to check if two keys are equal or cover the same set of keys. Return value depends on type of overlap
ErrorType DoKeysOverlap(DWORD first, DWORD second);
// Function to set the value of a key event based on the arguments // Function to set the value of a key event based on the arguments
void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo); void SetKeyEvent(LPINPUT keyEventArray, int index, DWORD inputType, WORD keyCode, DWORD flags, ULONG_PTR extraInfo);
@ -58,7 +41,4 @@ namespace KeyboardManagerHelper
// Function to sort a vector of shortcuts based on it's size // Function to sort a vector of shortcuts based on it's size
void SortShortcutVectorBasedOnSize(std::vector<Shortcut>& shortcutVector); void SortShortcutVectorBasedOnSize(std::vector<Shortcut>& shortcutVector);
// Function to check if a modifier has been repeated in the previous drop downs
bool CheckRepeatedModifier(const std::vector<int32_t>& currentKeys, int selectedKeyCodes);
} }

View File

@ -24,7 +24,7 @@ namespace KeyboardManagerInput
// Function to get the foreground process name // Function to get the foreground process name
void GetForegroundProcess(_Out_ std::wstring& foregroundProcess) void GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
{ {
foregroundProcess = KeyboardManagerHelper::GetCurrentApplication(false); foregroundProcess = Helpers::GetCurrentApplication(false);
} }
}; };
} }

View File

@ -1,8 +1,8 @@
#include "pch.h" #include "pch.h"
#include "KeyboardEventHandlers.h" #include "KeyboardEventHandlers.h"
#include <keyboardmanager/common/KeyboardManagerState.h>
#include <keyboardmanager/common/InputInterface.h> #include <keyboardmanager/common/InputInterface.h>
#include <keyboardmanager/common/Helpers.h> #include <keyboardmanager/common/Helpers.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
namespace KeyboardEventHandlers namespace KeyboardEventHandlers
{ {
@ -16,8 +16,8 @@ namespace KeyboardEventHandlers
memset(keyEventList, 0, sizeof(keyEventList)); memset(keyEventList, 0, sizeof(keyEventList));
// Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts // Use the suppress flag to ensure these are not intercepted by any remapped keys or shortcuts
KeyboardManagerHelper::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, 0, INPUT_KEYBOARD, VK_NUMLOCK, KEYEVENTF_KEYUP, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
KeyboardManagerHelper::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG); Helpers::SetKeyEvent(keyEventList, 1, INPUT_KEYBOARD, VK_NUMLOCK, 0, KeyboardManagerConstants::KEYBOARDMANAGER_SUPPRESS_FLAG);
UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT)); UINT res = ii.SendVirtualInput((UINT)key_count, keyEventList, sizeof(INPUT));
delete[] keyEventList; delete[] keyEventList;
} }

View File

@ -46,36 +46,28 @@
<ClCompile Include="..\..\..\common\interop\keyboard_layout.cpp" /> <ClCompile Include="..\..\..\common\interop\keyboard_layout.cpp" />
<ClCompile Include="Helpers.cpp" /> <ClCompile Include="Helpers.cpp" />
<ClCompile Include="KeyboardEventHandlers.cpp" /> <ClCompile Include="KeyboardEventHandlers.cpp" />
<ClCompile Include="KeyboardManagerState.cpp" /> <ClCompile Include="MappingConfiguration.cpp" />
<ClCompile Include="KeyDelay.cpp" />
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="SettingsHelper.cpp" />
<ClCompile Include="Shortcut.cpp" /> <ClCompile Include="Shortcut.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ErrorTypes.h" />
<ClInclude Include="Input.h" /> <ClInclude Include="Input.h" />
<ClInclude Include="KeyboardEventHandlers.h" /> <ClInclude Include="KeyboardEventHandlers.h" />
<ClInclude Include="MappingConfiguration.h" />
<ClInclude Include="ModifierKey.h" /> <ClInclude Include="ModifierKey.h" />
<ClInclude Include="InputInterface.h" /> <ClInclude Include="InputInterface.h" />
<ClInclude Include="Helpers.h" /> <ClInclude Include="Helpers.h" />
<ClInclude Include="KeyboardManagerConstants.h" /> <ClInclude Include="KeyboardManagerConstants.h" />
<ClInclude Include="KeyboardManagerState.h" />
<ClInclude Include="KeyDelay.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="RemapShortcut.h" /> <ClInclude Include="RemapShortcut.h" />
<ClInclude Include="SettingsHelper.h" />
<ClInclude Include="Shortcut.h" /> <ClInclude Include="Shortcut.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\common\COMUtils\COMUtils.vcxproj"> <ProjectReference Include="..\..\..\common\COMUtils\COMUtils.vcxproj">
<Project>{7319089e-46d6-4400-bc65-e39bdf1416ee}</Project> <Project>{7319089e-46d6-4400-bc65-e39bdf1416ee}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj"> <ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project> <Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference> </ProjectReference>

View File

@ -15,9 +15,6 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="KeyboardManagerState.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Helpers.cpp"> <ClCompile Include="Helpers.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -27,23 +24,17 @@
<ClCompile Include="Shortcut.cpp"> <ClCompile Include="Shortcut.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="KeyDelay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\common\interop\keyboard_layout.cpp"> <ClCompile Include="..\..\..\common\interop\keyboard_layout.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="SettingsHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KeyboardEventHandlers.cpp"> <ClCompile Include="KeyboardEventHandlers.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="MappingConfiguration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="KeyboardManagerState.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Helpers.h"> <ClInclude Include="Helpers.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -56,9 +47,6 @@
<ClInclude Include="RemapShortcut.h"> <ClInclude Include="RemapShortcut.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="KeyDelay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyboardManagerConstants.h"> <ClInclude Include="KeyboardManagerConstants.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -68,16 +56,13 @@
<ClInclude Include="ModifierKey.h"> <ClInclude Include="ModifierKey.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="SettingsHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KeyboardEventHandlers.h"> <ClInclude Include="KeyboardEventHandlers.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Input.h"> <ClInclude Include="Input.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ErrorTypes.h"> <ClInclude Include="MappingConfiguration.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>

View File

@ -40,67 +40,11 @@ namespace KeyboardManagerConstants
// Name of the default configuration. // Name of the default configuration.
inline const std::wstring DefaultConfiguration = L"default"; inline const std::wstring DefaultConfiguration = L"default";
// Name of the dummy update file.
inline const std::wstring DummyUpdateFileName = L"settings-updated.json";
// Minimum and maximum size of a shortcut
inline const long MinShortcutSize = 2;
inline const long MaxShortcutSize = 3;
// Default window sizes
inline const int DefaultEditKeyboardWindowWidth = 800;
inline const int DefaultEditKeyboardWindowHeight = 600;
// Increasing the min size can cause issues when moving the window between
// monitors with different DPI scaling factor // monitors with different DPI scaling factor
inline const int MinimumEditKeyboardWindowWidth = 200; inline const int MinimumEditKeyboardWindowWidth = 200;
inline const int MinimumEditKeyboardWindowHeight = 200; inline const int MinimumEditKeyboardWindowHeight = 200;
inline const int EditKeyboardTableMinWidth = 700;
inline const int DefaultEditShortcutsWindowWidth = 1050;
inline const int DefaultEditShortcutsWindowHeight = 600;
// Increasing the min size can cause issues when moving the window between
// monitors with different DPI scaling factor
inline const int MinimumEditShortcutsWindowWidth = 200;
inline const int MinimumEditShortcutsWindowHeight = 200;
inline const int EditShortcutsTableMinWidth = 1000;
// Key Remap table constants
inline const long RemapTableColCount = 4;
inline const long RemapTableHeaderCount = 2;
inline const long RemapTableOriginalColIndex = 0;
inline const long RemapTableArrowColIndex = 1;
inline const long RemapTableNewColIndex = 2;
inline const long RemapTableRemoveColIndex = 3;
inline const DWORD64 RemapTableDropDownWidth = 110;
// Shortcut table constants
inline const long ShortcutTableColCount = 5;
inline const long ShortcutTableHeaderCount = 3;
inline const long ShortcutTableOriginalColIndex = 0;
inline const long ShortcutTableArrowColIndex = 1;
inline const long ShortcutTableNewColIndex = 2;
inline const long ShortcutTableTargetAppColIndex = 3;
inline const long ShortcutTableRemoveColIndex = 4;
inline const long ShortcutArrowColumnWidth = 90;
inline const DWORD64 ShortcutTableDropDownWidth = 110;
inline const DWORD64 ShortcutTableDropDownSpacing = 10;
inline const long ShortcutOriginColumnWidth = 3 * ShortcutTableDropDownWidth + 2 * ShortcutTableDropDownSpacing;
inline const long ShortcutTargetColumnWidth = 3 * ShortcutTableDropDownWidth + 2 * ShortcutTableDropDownSpacing + 25;
// Drop down height used for both Edit Keyboard and Edit Shortcuts
inline const DWORD64 TableDropDownHeight = 200;
inline const DWORD64 TableArrowColWidth = 230;
inline const DWORD64 TableRemoveColWidth = 20;
inline const DWORD64 TableWarningColWidth = 20;
inline const DWORD64 TableTargetAppColWidth = ShortcutTableDropDownWidth + TableRemoveColWidth * 2;
// Shared style constants for both Remap Table and Shortcut Table
inline const DWORD64 HeaderButtonWidth = 100;
// Flags used for distinguishing key events sent by Keyboard Manager // Flags used for distinguishing key events sent by Keyboard Manager
inline const ULONG_PTR KEYBOARDMANAGER_SINGLEKEY_FLAG = 0x11; // Single key remaps inline const ULONG_PTR KEYBOARDMANAGER_SINGLEKEY_FLAG = 0x11; // Single key remaps
inline const ULONG_PTR KEYBOARDMANAGER_SHORTCUT_FLAG = 0x101; // Shortcut remaps inline const ULONG_PTR KEYBOARDMANAGER_SHORTCUT_FLAG = 0x101; // Shortcut remaps

View File

@ -1,230 +0,0 @@
#pragma once
#include <mutex>
#include "KeyboardManagerConstants.h"
#include <common/interop/keyboard_layout.h>
#include "../common/hooks/LowlevelKeyboardEvent.h"
#include <functional>
#include <variant>
#include "Shortcut.h"
#include "RemapShortcut.h"
class KeyDelay;
namespace KeyboardManagerHelper
{
enum class KeyboardHookDecision;
}
namespace winrt::Windows::UI::Xaml::Controls
{
struct StackPanel;
}
using SingleKeyRemapTable = std::unordered_map<DWORD, KeyShortcutUnion>;
using ShortcutRemapTable = std::map<Shortcut, RemapShortcut>;
using AppSpecificShortcutRemapTable = std::map<std::wstring, ShortcutRemapTable>;
// Enum type to store different states of the UI
enum class KeyboardManagerUIState
{
// If set to this value then there is no keyboard manager window currently active that requires a hook
Deactivated,
// If set to this value then the detect key window is currently active and it requires a hook
DetectSingleKeyRemapWindowActivated,
// If set to this value then the detect shortcut window in edit keyboard window is currently active and it requires a hook
DetectShortcutWindowInEditKeyboardWindowActivated,
// If set to this value then the edit keyboard window is currently active and remaps should not be applied
EditKeyboardWindowActivated,
// If set to this value then the detect shortcut window is currently active and it requires a hook
DetectShortcutWindowActivated,
// If set to this value then the edit shortcuts window is currently active and remaps should not be applied
EditShortcutsWindowActivated
};
// Class to store the shared state of the keyboard manager between the UI and the hook
class KeyboardManagerState
{
private:
// State variable used to store which UI window is currently active that requires interaction with the hook
KeyboardManagerUIState uiState;
std::mutex uiState_mutex;
// Window handle for the current UI window which is active. Should be set to nullptr if UI is deactivated
HWND currentUIWindow;
std::mutex currentUIWindow_mutex;
// Object to store the shortcut detected in the detect shortcut UI window. Gets cleared on releasing keys. This is used in both the backend and the UI.
Shortcut detectedShortcut;
std::mutex detectedShortcut_mutex;
// Object to store the shortcut state displayed in the UI window. Always stores last displayed shortcut irrespective of releasing keys. This is used in both the backend and the UI.
Shortcut currentShortcut;
std::mutex currentShortcut_mutex;
// Store detected remap key in the remap UI window. This is used in both the backend and the UI.
DWORD detectedRemapKey;
std::mutex detectedRemapKey_mutex;
// Stores the UI element which is to be updated based on the remap key entered.
winrt::Windows::Foundation::IInspectable currentSingleKeyUI;
std::mutex currentSingleKeyUI_mutex;
// Stores the UI element which is to be updated based on the shortcut entered (each stackpanel represents a row of keys)
winrt::Windows::Foundation::IInspectable currentShortcutUI1;
winrt::Windows::Foundation::IInspectable currentShortcutUI2;
std::mutex currentShortcutUI_mutex;
// Stores the current configuration name.
std::wstring currentConfig = KeyboardManagerConstants::DefaultConfiguration;
std::mutex currentConfig_mutex;
// Registered KeyDelay objects, used to notify delayed key events.
std::map<DWORD, std::unique_ptr<KeyDelay>> keyDelays;
std::mutex keyDelays_mutex;
// Stores the activated target application in app-specific shortcut
std::wstring activatedAppSpecificShortcutTarget;
// Thread safe boolean value to check if remappings are currently enabled. This is used to disable remappings while the remap tables are being updated by the UI thread
std::atomic_bool remappingsEnabled;
// Display a key by appending a border Control as a child of the panel.
void AddKeyToLayout(const winrt::Windows::UI::Xaml::Controls::StackPanel& panel, const winrt::hstring& key);
public:
// The map members and their mutexes are left as public since the maps are used extensively in dllmain.cpp.
// Maps which store the remappings for each of the features. The bool fields should be initialized to false. They are used to check the current state of the shortcut (i.e is that particular shortcut currently pressed down or not).
// Stores single key remappings
std::unordered_map<DWORD, KeyShortcutUnion> singleKeyReMap;
/* This feature has not been enabled (code from proof of concept stage)
*
// Stores keys which need to be changed from toggle behavior to modifier behavior. Eg. Caps Lock
std::unordered_map<DWORD, bool> singleKeyToggleToMod;
*/
// Stores the os level shortcut remappings
ShortcutRemapTable osLevelShortcutReMap;
std::vector<Shortcut> osLevelShortcutReMapSortedKeys;
// Stores the app-specific shortcut remappings. Maps application name to the shortcut map
AppSpecificShortcutRemapTable appSpecificShortcutReMap;
std::map<std::wstring, std::vector<Shortcut>> appSpecificShortcutReMapSortedKeys;
// Stores the keyboard layout
LayoutMap keyboardMap;
// Constructor
KeyboardManagerState();
// Destructor
~KeyboardManagerState();
// Function to reset the UI state members
void ResetUIState();
// Function to check the if the UI state matches the argument state. For states with detect windows it also checks if the window is in focus.
bool CheckUIState(KeyboardManagerUIState state);
// Function to set the window handle of the current UI window that is activated
void SetCurrentUIWindow(HWND windowHandle);
// Function to set the UI state. When a window is activated, the handle to the window can be passed in the windowHandle argument.
void SetUIState(KeyboardManagerUIState state, HWND windowHandle = nullptr);
// Function to clear the OS Level shortcut remapping table
void ClearOSLevelShortcuts();
// Function to clear the Keys remapping table
void ClearSingleKeyRemaps();
// Function to clear the App specific shortcut remapping table
void ClearAppSpecificShortcuts();
// Function to add a new single key to key remapping
bool AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey);
// Function to add a new OS level shortcut remapping
bool AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC);
// Function to add a new App specific level shortcut remapping
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC);
// Function to get the iterator of a single key remap given the source key. Returns nullopt if it isn't remapped
std::optional<SingleKeyRemapTable::iterator> GetSingleKeyRemap(const DWORD& originalKey);
bool CheckShortcutRemapInvoked(const std::optional<std::wstring>& appName);
std::vector<Shortcut>& GetSortedShortcutRemapVector(const std::optional<std::wstring>& appName);
// Function to get the source and target of a shortcut remap given the source shortcut. Returns nullopt if it isn't remapped
ShortcutRemapTable& GetShortcutRemapTable(const std::optional<std::wstring>& appName);
// Function to set the textblock of the detect shortcut UI so that it can be accessed by the hook
void ConfigureDetectShortcutUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock1, const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock2);
// Function to set the textblock of the detect remap key UI so that it can be accessed by the hook
void ConfigureDetectSingleKeyRemapUI(const winrt::Windows::UI::Xaml::Controls::StackPanel& textBlock);
// Function to update the detect shortcut UI based on the entered keys
void UpdateDetectShortcutUI();
// Function to update the detect remap key UI based on the entered key.
void UpdateDetectSingleKeyRemapUI();
// Function to return the currently detected shortcut which is displayed on the UI
Shortcut GetDetectedShortcut();
// Function to return the currently detected remap key which is displayed on the UI
DWORD GetDetectedSingleRemapKey();
// Function which can be used in HandleKeyboardHookEvent before the single key remap event to use the UI and suppress events while the remap window is active.
KeyboardManagerHelper::KeyboardHookDecision DetectSingleRemapKeyUIBackend(LowlevelKeyboardEvent* data);
// Function which can be used in HandleKeyboardHookEvent before the os level shortcut remap event to use the UI and suppress events while the remap window is active.
KeyboardManagerHelper::KeyboardHookDecision DetectShortcutUIBackend(LowlevelKeyboardEvent* data, bool isRemapKey);
// Add a KeyDelay object to get delayed key presses events for a given virtual key
// NOTE: this will throw an exception if a virtual key is registered twice.
// NOTE*: the virtual key should represent the original, unmapped virtual key.
void RegisterKeyDelay(
DWORD key,
std::function<void(DWORD)> onShortPress,
std::function<void(DWORD)> onLongPressDetected,
std::function<void(DWORD)> onLongPressReleased);
// Remove a KeyDelay.
// NOTE: this method will throw if the virtual key is not registered beforehand.
// NOTE*: the virtual key should represent the original, unmapped virtual key.
void UnregisterKeyDelay(DWORD key);
// Function to clear all the registered key delays
void ClearRegisteredKeyDelays();
// Handle a key event, for a delayed key.
bool HandleKeyDelayEvent(LowlevelKeyboardEvent* ev);
// Update the currently selected single key remap
void SelectDetectedRemapKey(DWORD key);
// Update the currently selected shortcut.
void SelectDetectedShortcut(DWORD key);
// Reset the shortcut (backend) state after releasing a key.
void ResetDetectedShortcutKey(DWORD key);
// Save the updated configuration.
bool SaveConfigToFile();
// Sets the Current Active Configuration Name.
void SetCurrentConfigName(const std::wstring& configName);
// Gets the Current Active Configuration Name.
std::wstring GetCurrentConfigName();
// Sets the activated target application in app-specific shortcut
void SetActivatedApp(const std::wstring& appName);
// Gets the activated target application in app-specific shortcut
std::wstring GetActivatedApp();
};

View File

@ -0,0 +1,393 @@
#include "pch.h"
#include "MappingConfiguration.h"
#include <common/SettingsAPI/settings_objects.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/logger/logger.h>
#include "KeyboardManagerConstants.h"
#include "Shortcut.h"
#include "RemapShortcut.h"
#include "Helpers.h"
// Function to clear the OS Level shortcut remapping table
void MappingConfiguration::ClearOSLevelShortcuts()
{
osLevelShortcutReMap.clear();
osLevelShortcutReMapSortedKeys.clear();
}
// Function to clear the Keys remapping table.
void MappingConfiguration::ClearSingleKeyRemaps()
{
singleKeyReMap.clear();
}
// Function to clear the App specific shortcut remapping table
void MappingConfiguration::ClearAppSpecificShortcuts()
{
appSpecificShortcutReMap.clear();
appSpecificShortcutReMapSortedKeys.clear();
}
// Function to add a new OS level shortcut remapping
bool MappingConfiguration::AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC)
{
// Check if the shortcut is already remapped
auto it = osLevelShortcutReMap.find(originalSC);
if (it != osLevelShortcutReMap.end())
{
return false;
}
osLevelShortcutReMap[originalSC] = RemapShortcut(newSC);
osLevelShortcutReMapSortedKeys.push_back(originalSC);
Helpers::SortShortcutVectorBasedOnSize(osLevelShortcutReMapSortedKeys);
return true;
}
// Function to add a new single key to key/shortcut remapping
bool MappingConfiguration::AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey)
{
// Check if the key is already remapped
auto it = singleKeyReMap.find(originalKey);
if (it != singleKeyReMap.end())
{
return false;
}
singleKeyReMap[originalKey] = newRemapKey;
return true;
}
// Function to add a new App specific shortcut remapping
bool MappingConfiguration::AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC)
{
// Convert app name to lower case
std::wstring process_name;
process_name.resize(app.length());
std::transform(app.begin(), app.end(), process_name.begin(), towlower);
// Check if there are any app specific shortcuts for this app
auto appIt = appSpecificShortcutReMap.find(process_name);
if (appIt != appSpecificShortcutReMap.end())
{
// Check if the shortcut is already remapped
auto shortcutIt = appSpecificShortcutReMap[process_name].find(originalSC);
if (shortcutIt != appSpecificShortcutReMap[process_name].end())
{
return false;
}
}
else
{
appSpecificShortcutReMapSortedKeys[process_name] = std::vector<Shortcut>();
}
appSpecificShortcutReMap[process_name][originalSC] = RemapShortcut(newSC);
appSpecificShortcutReMapSortedKeys[process_name].push_back(originalSC);
Helpers::SortShortcutVectorBasedOnSize(appSpecificShortcutReMapSortedKeys[process_name]);
return true;
}
bool MappingConfiguration::LoadSingleKeyRemaps(const json::JsonObject& jsonData)
{
bool result = true;
try
{
auto remapKeysData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapKeysSettingName);
ClearSingleKeyRemaps();
if (remapKeysData)
{
auto inProcessRemapKeys = remapKeysData.GetNamedArray(KeyboardManagerConstants::InProcessRemapKeysSettingName);
for (const auto& it : inProcessRemapKeys)
{
try
{
auto originalKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
auto newRemapKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
// If remapped to a shortcut
if (std::wstring(newRemapKey).find(L";") != std::string::npos)
{
AddSingleKeyRemap(std::stoul(originalKey.c_str()), Shortcut(newRemapKey.c_str()));
}
// If remapped to a key
else
{
AddSingleKeyRemap(std::stoul(originalKey.c_str()), std::stoul(newRemapKey.c_str()));
}
}
catch (...)
{
Logger::error(L"Improper Key Data JSON. Try the next remap.");
result = false;
}
}
}
}
catch (...)
{
Logger::error(L"Improper JSON format for single key remaps. Skip to next remap type");
result = false;
}
return result;
}
bool MappingConfiguration::LoadAppSpecificShortcutRemaps(const json::JsonObject& remapShortcutsData)
{
bool result = true;
try
{
auto appSpecificRemapShortcuts = remapShortcutsData.GetNamedArray(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName);
for (const auto& it : appSpecificRemapShortcuts)
{
try
{
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
auto targetApp = it.GetObjectW().GetNamedString(KeyboardManagerConstants::TargetAppSettingName);
// If remapped to a shortcut
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
{
AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
}
// If remapped to a key
else
{
AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
}
}
catch (...)
{
Logger::error(L"Improper Key Data JSON. Try the next shortcut.");
result = false;
}
}
}
catch (...)
{
Logger::error(L"Improper JSON format for os level shortcut remaps. Skip to next remap type");
result = false;
}
return result;
}
bool MappingConfiguration::LoadShortcutRemaps(const json::JsonObject& jsonData)
{
bool result = true;
try
{
auto remapShortcutsData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapShortcutsSettingName);
ClearOSLevelShortcuts();
ClearAppSpecificShortcuts();
if (remapShortcutsData)
{
// Load os level shortcut remaps
try
{
auto globalRemapShortcuts = remapShortcutsData.GetNamedArray(KeyboardManagerConstants::GlobalRemapShortcutsSettingName);
for (const auto& it : globalRemapShortcuts)
{
try
{
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
// If remapped to a shortcut
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
{
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
}
// If remapped to a key
else
{
AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
}
}
catch (...)
{
Logger::error(L"Improper Key Data JSON. Try the next shortcut.");
result = false;
}
}
}
catch (...)
{
Logger::error(L"Improper JSON format for os level shortcut remaps. Skip to next remap type");
result = false;
}
// Load app specific shortcut remaps
result = result && LoadAppSpecificShortcutRemaps(remapShortcutsData);
}
}
catch (...)
{
Logger::error(L"Improper JSON format for shortcut remaps. Skip to next remap type");
result = false;
}
return result;
}
MappingConfiguration::MappingConfiguration()
{
}
bool MappingConfiguration::LoadSettings()
{
Logger::trace(L"SettingsHelper::LoadSettings()");
try
{
PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(KeyboardManagerConstants::ModuleName);
auto current_config = settings.get_string_value(KeyboardManagerConstants::ActiveConfigurationSettingName);
if (!current_config)
{
return false;
}
currentConfig = *current_config;
// Read the config file and load the remaps.
auto configFile = json::from_file(PTSettingsHelper::get_module_save_folder_location(KeyboardManagerConstants::ModuleName) + L"\\" + *current_config + L".json");
if (!configFile)
{
return false;
}
bool result = LoadSingleKeyRemaps(*configFile);
result = result && LoadShortcutRemaps(*configFile);
return result;
}
catch (...)
{
Logger::error(L"SettingsHelper::LoadSettings() failed");
}
return false;
}
// Save the updated configuration.
bool MappingConfiguration::SaveSettingsToFile()
{
bool result = true;
json::JsonObject configJson;
json::JsonObject remapShortcuts;
json::JsonObject remapKeys;
json::JsonArray inProcessRemapKeysArray;
json::JsonArray appSpecificRemapShortcutsArray;
json::JsonArray globalRemapShortcutsArray;
for (const auto& it : singleKeyReMap)
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(winrt::to_hstring((unsigned int)it.first)));
// For key to key remapping
if (it.second.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second))));
}
// For key to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second).ToHstringVK()));
}
inProcessRemapKeysArray.Append(keys);
}
for (const auto& it : osLevelShortcutReMap)
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(it.first.ToHstringVK()));
// For shortcut to key remapping
if (it.second.targetShortcut.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(it.second.targetShortcut))));
}
// For shortcut to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(it.second.targetShortcut).ToHstringVK()));
}
globalRemapShortcutsArray.Append(keys);
}
for (const auto& itApp : appSpecificShortcutReMap)
{
// Iterate over apps
for (const auto& itKeys : itApp.second)
{
json::JsonObject keys;
keys.SetNamedValue(KeyboardManagerConstants::OriginalKeysSettingName, json::value(itKeys.first.ToHstringVK()));
// For shortcut to key remapping
if (itKeys.second.targetShortcut.index() == 0)
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(winrt::to_hstring((unsigned int)std::get<DWORD>(itKeys.second.targetShortcut))));
}
// For shortcut to shortcut remapping
else
{
keys.SetNamedValue(KeyboardManagerConstants::NewRemapKeysSettingName, json::value(std::get<Shortcut>(itKeys.second.targetShortcut).ToHstringVK()));
}
keys.SetNamedValue(KeyboardManagerConstants::TargetAppSettingName, json::value(itApp.first));
appSpecificRemapShortcutsArray.Append(keys);
}
}
remapShortcuts.SetNamedValue(KeyboardManagerConstants::GlobalRemapShortcutsSettingName, globalRemapShortcutsArray);
remapShortcuts.SetNamedValue(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName, appSpecificRemapShortcutsArray);
remapKeys.SetNamedValue(KeyboardManagerConstants::InProcessRemapKeysSettingName, inProcessRemapKeysArray);
configJson.SetNamedValue(KeyboardManagerConstants::RemapKeysSettingName, remapKeys);
configJson.SetNamedValue(KeyboardManagerConstants::RemapShortcutsSettingName, remapShortcuts);
try
{
json::to_file((PTSettingsHelper::get_module_save_folder_location(KeyboardManagerConstants::ModuleName) + L"\\" + currentConfig + L".json"), configJson);
}
catch (...)
{
result = false;
Logger::error(L"Failed to save the settings");
}
if (result)
{
auto hEvent = CreateEvent(nullptr, false, false, KeyboardManagerConstants::SettingsEventName.c_str());
if (hEvent)
{
SetEvent(hEvent);
Logger::trace(L"Signaled {} event", KeyboardManagerConstants::SettingsEventName);
}
else
{
Logger::error(L"Failed to signal {} event", KeyboardManagerConstants::SettingsEventName);
}
}
return result;
}

View File

@ -0,0 +1,65 @@
#pragma once
#include <common/utils/json.h>
#include <keyboardmanager/common/KeyboardManagerConstants.h>
#include <keyboardmanager/common/Shortcut.h>
#include <keyboardmanager/common/RemapShortcut.h>
using SingleKeyRemapTable = std::unordered_map<DWORD, KeyShortcutUnion>;
using ShortcutRemapTable = std::map<Shortcut, RemapShortcut>;
using AppSpecificShortcutRemapTable = std::map<std::wstring, ShortcutRemapTable>;
class MappingConfiguration
{
public:
MappingConfiguration();
~MappingConfiguration() = default;
// Load the configuration.
bool LoadSettings();
// Save the updated configuration.
bool SaveSettingsToFile();
// Function to clear the OS Level shortcut remapping table
void ClearOSLevelShortcuts();
// Function to clear the Keys remapping table
void ClearSingleKeyRemaps();
// Function to clear the App specific shortcut remapping table
void ClearAppSpecificShortcuts();
// Function to add a new single key to key remapping
bool AddSingleKeyRemap(const DWORD& originalKey, const KeyShortcutUnion& newRemapKey);
// Function to add a new OS level shortcut remapping
bool AddOSLevelShortcut(const Shortcut& originalSC, const KeyShortcutUnion& newSC);
// Function to add a new App specific level shortcut remapping
bool AddAppSpecificShortcut(const std::wstring& app, const Shortcut& originalSC, const KeyShortcutUnion& newSC);
// The map members and their mutexes are left as public since the maps are used extensively in dllmain.cpp.
// Maps which store the remappings for each of the features. The bool fields should be initialized to false. They are used to check the current state of the shortcut (i.e is that particular shortcut currently pressed down or not).
// Stores single key remappings
std::unordered_map<DWORD, KeyShortcutUnion> singleKeyReMap;
// Stores the os level shortcut remappings
ShortcutRemapTable osLevelShortcutReMap;
std::vector<Shortcut> osLevelShortcutReMapSortedKeys;
// Stores the app-specific shortcut remappings. Maps application name to the shortcut map
AppSpecificShortcutRemapTable appSpecificShortcutReMap;
std::map<std::wstring, std::vector<Shortcut>> appSpecificShortcutReMapSortedKeys;
// Stores the current configuration name.
std::wstring currentConfig = KeyboardManagerConstants::DefaultConfiguration;
private:
bool LoadSingleKeyRemaps(const json::JsonObject& jsonData);
bool LoadShortcutRemaps(const json::JsonObject& jsonData);
bool LoadAppSpecificShortcutRemaps(const json::JsonObject& remapShortcutsData);
};

View File

@ -1,194 +0,0 @@
#include "pch.h"
#include "SettingsHelper.h"
#include <common/SettingsAPI/settings_objects.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/logger/logger.h>
#include <common/KeyboardManagerConstants.h>
bool LoadSingleKeyRemaps(KeyboardManagerState& keyboardManagerState, const json::JsonObject& jsonData)
{
bool result = true;
try
{
auto remapKeysData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapKeysSettingName);
keyboardManagerState.ClearSingleKeyRemaps();
if (remapKeysData)
{
auto inProcessRemapKeys = remapKeysData.GetNamedArray(KeyboardManagerConstants::InProcessRemapKeysSettingName);
for (const auto& it : inProcessRemapKeys)
{
try
{
auto originalKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
auto newRemapKey = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
// If remapped to a shortcut
if (std::wstring(newRemapKey).find(L";") != std::string::npos)
{
keyboardManagerState.AddSingleKeyRemap(std::stoul(originalKey.c_str()), Shortcut(newRemapKey.c_str()));
}
// If remapped to a key
else
{
keyboardManagerState.AddSingleKeyRemap(std::stoul(originalKey.c_str()), std::stoul(newRemapKey.c_str()));
}
}
catch (...)
{
Logger::error(L"Improper Key Data JSON. Try the next remap.");
result = false;
}
}
}
}
catch (...)
{
Logger::error(L"Improper JSON format for single key remaps. Skip to next remap type");
result = false;
}
return result;
}
bool LoadAppSpecificShortcutRemaps(KeyboardManagerState& keyboardManagerState, const json::JsonObject& remapShortcutsData)
{
bool result = true;
try
{
auto appSpecificRemapShortcuts = remapShortcutsData.GetNamedArray(KeyboardManagerConstants::AppSpecificRemapShortcutsSettingName);
for (const auto& it : appSpecificRemapShortcuts)
{
try
{
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
auto targetApp = it.GetObjectW().GetNamedString(KeyboardManagerConstants::TargetAppSettingName);
// If remapped to a shortcut
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
{
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
}
// If remapped to a key
else
{
keyboardManagerState.AddAppSpecificShortcut(targetApp.c_str(), Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
}
}
catch (...)
{
Logger::error(L"Improper Key Data JSON. Try the next shortcut.");
result = false;
}
}
}
catch (...)
{
Logger::error(L"Improper JSON format for os level shortcut remaps. Skip to next remap type");
result = false;
}
return result;
}
bool LoadShortcutRemaps(KeyboardManagerState& keyboardManagerState, const json::JsonObject& jsonData)
{
bool result = true;
try
{
auto remapShortcutsData = jsonData.GetNamedObject(KeyboardManagerConstants::RemapShortcutsSettingName);
keyboardManagerState.ClearOSLevelShortcuts();
keyboardManagerState.ClearAppSpecificShortcuts();
if (remapShortcutsData)
{
// Load os level shortcut remaps
try
{
auto globalRemapShortcuts = remapShortcutsData.GetNamedArray(KeyboardManagerConstants::GlobalRemapShortcutsSettingName);
for (const auto& it : globalRemapShortcuts)
{
try
{
auto originalKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::OriginalKeysSettingName);
auto newRemapKeys = it.GetObjectW().GetNamedString(KeyboardManagerConstants::NewRemapKeysSettingName);
// If remapped to a shortcut
if (std::wstring(newRemapKeys).find(L";") != std::string::npos)
{
keyboardManagerState.AddOSLevelShortcut(Shortcut(originalKeys.c_str()), Shortcut(newRemapKeys.c_str()));
}
// If remapped to a key
else
{
keyboardManagerState.AddOSLevelShortcut(Shortcut(originalKeys.c_str()), std::stoul(newRemapKeys.c_str()));
}
}
catch (...)
{
Logger::error(L"Improper Key Data JSON. Try the next shortcut.");
result = false;
}
}
}
catch (...)
{
Logger::error(L"Improper JSON format for os level shortcut remaps. Skip to next remap type");
result = false;
}
// Load app specific shortcut remaps
result = result && LoadAppSpecificShortcutRemaps(keyboardManagerState, remapShortcutsData);
}
}
catch (...)
{
Logger::error(L"Improper JSON format for shortcut remaps. Skip to next remap type");
result = false;
}
return result;
}
bool SettingsHelper::LoadSettings(KeyboardManagerState& keyboardManagerState)
{
Logger::trace(L"SettingsHelper::LoadSettings()");
try
{
PowerToysSettings::PowerToyValues settings = PowerToysSettings::PowerToyValues::load_from_settings_file(KeyboardManagerConstants::ModuleName);
auto current_config = settings.get_string_value(KeyboardManagerConstants::ActiveConfigurationSettingName);
if (!current_config)
{
return false;
}
keyboardManagerState.SetCurrentConfigName(*current_config);
// Read the config file and load the remaps.
auto configFile = json::from_file(PTSettingsHelper::get_module_save_folder_location(KeyboardManagerConstants::ModuleName) + L"\\" + *current_config + L".json");
if (!configFile)
{
return false;
}
bool result = LoadSingleKeyRemaps(keyboardManagerState, *configFile);
result = result && LoadShortcutRemaps(keyboardManagerState, *configFile);
return result;
}
catch (...)
{
Logger::error(L"SettingsHelper::LoadSettings() failed");
}
return false;
}

View File

@ -1,9 +0,0 @@
#pragma once
#include <keyboardmanager/common/KeyboardManagerState.h>
class SettingsHelper
{
public:
static bool LoadSettings(KeyboardManagerState& state);
};

View File

@ -2,15 +2,29 @@
#include "Shortcut.h" #include "Shortcut.h"
#include <common/interop/keyboard_layout.h> #include <common/interop/keyboard_layout.h>
#include <common/interop/shared_constants.h> #include <common/interop/shared_constants.h>
#include "ErrorTypes.h"
#include "Helpers.h" #include "Helpers.h"
#include "InputInterface.h" #include "InputInterface.h"
// Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> Shortcut::splitwstring(const std::wstring& input, wchar_t delimiter)
{
std::wstringstream ss(input);
std::wstring item;
std::vector<std::wstring> splittedStrings;
while (std::getline(ss, item, delimiter))
{
splittedStrings.push_back(item);
}
return splittedStrings;
}
// Constructor to initialize Shortcut from it's virtual key code string representation. // Constructor to initialize Shortcut from it's virtual key code string representation.
Shortcut::Shortcut(const std::wstring& shortcutVK) : Shortcut::Shortcut(const std::wstring& shortcutVK) :
winKey(ModifierKey::Disabled), ctrlKey(ModifierKey::Disabled), altKey(ModifierKey::Disabled), shiftKey(ModifierKey::Disabled), actionKey(NULL) winKey(ModifierKey::Disabled), ctrlKey(ModifierKey::Disabled), altKey(ModifierKey::Disabled), shiftKey(ModifierKey::Disabled), actionKey(NULL)
{ {
auto keys = KeyboardManagerHelper::splitwstring(shortcutVK, ';'); auto keys = splitwstring(shortcutVK, ';');
for (auto it : keys) for (auto it : keys)
{ {
auto vkKeyCode = std::stoul(it); auto vkKeyCode = std::stoul(it);
@ -75,20 +89,6 @@ void Shortcut::Reset()
actionKey = NULL; actionKey = NULL;
} }
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
bool Shortcut::IsValidShortcut() const
{
if (actionKey != NULL)
{
if (winKey != ModifierKey::Disabled || ctrlKey != ModifierKey::Disabled || altKey != ModifierKey::Disabled || shiftKey != ModifierKey::Disabled)
{
return true;
}
}
return false;
}
// Function to return the action key // Function to return the action key
DWORD Shortcut::GetActionKey() const DWORD Shortcut::GetActionKey() const
{ {
@ -418,33 +418,6 @@ void Shortcut::ResetKey(const DWORD& input)
} }
} }
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> Shortcut::GetKeyVector(LayoutMap& keyboardMap) const
{
std::vector<winrt::hstring> keys;
if (winKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(GetWinKey(ModifierKey::Both)).c_str()));
}
if (ctrlKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(GetCtrlKey()).c_str()));
}
if (altKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(GetAltKey()).c_str()));
}
if (shiftKey != ModifierKey::Disabled)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(GetShiftKey()).c_str()));
}
if (actionKey != NULL)
{
keys.push_back(winrt::to_hstring(keyboardMap.GetKeyName(actionKey).c_str()));
}
return keys;
}
// Function to return the string representation of the shortcut in virtual key codes appended in a string by ";" separator. // Function to return the string representation of the shortcut in virtual key codes appended in a string by ";" separator.
winrt::hstring Shortcut::ToHstringVK() const winrt::hstring Shortcut::ToHstringVK() const
{ {
@ -842,55 +815,3 @@ int Shortcut::GetCommonModifiersCount(const Shortcut& input) const
return commonElements; return commonElements;
} }
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
KeyboardManagerHelper::ErrorType Shortcut::DoKeysOverlap(const Shortcut& first, const Shortcut& second)
{
if (first.IsValidShortcut() && second.IsValidShortcut())
{
// If the shortcuts are equal
if (first == second)
{
return KeyboardManagerHelper::ErrorType::SameShortcutPreviouslyMapped;
}
// action keys match
else if (first.actionKey == second.actionKey)
{
// corresponding modifiers are either both disabled or both not disabled - this ensures that both match in types of modifiers i.e. Ctrl(l/r/c) Shift (l/r/c) A matches Ctrl(l/r/c) Shift (l/r/c) A
if (((first.winKey != ModifierKey::Disabled && second.winKey != ModifierKey::Disabled) || (first.winKey == ModifierKey::Disabled && second.winKey == ModifierKey::Disabled)) &&
((first.ctrlKey != ModifierKey::Disabled && second.ctrlKey != ModifierKey::Disabled) || (first.ctrlKey == ModifierKey::Disabled && second.ctrlKey == ModifierKey::Disabled)) &&
((first.altKey != ModifierKey::Disabled && second.altKey != ModifierKey::Disabled) || (first.altKey == ModifierKey::Disabled && second.altKey == ModifierKey::Disabled)) &&
((first.shiftKey != ModifierKey::Disabled && second.shiftKey != ModifierKey::Disabled) || (first.shiftKey == ModifierKey::Disabled && second.shiftKey == ModifierKey::Disabled)))
{
// If one of the modifier is common
if ((first.winKey == ModifierKey::Both || second.winKey == ModifierKey::Both) ||
(first.ctrlKey == ModifierKey::Both || second.ctrlKey == ModifierKey::Both) ||
(first.altKey == ModifierKey::Both || second.altKey == ModifierKey::Both) ||
(first.shiftKey == ModifierKey::Both || second.shiftKey == ModifierKey::Both))
{
return KeyboardManagerHelper::ErrorType::ConflictingModifierShortcut;
}
}
}
}
return KeyboardManagerHelper::ErrorType::NoError;
}
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
KeyboardManagerHelper::ErrorType Shortcut::IsShortcutIllegal() const
{
// Win+L
if (winKey != ModifierKey::Disabled && ctrlKey == ModifierKey::Disabled && altKey == ModifierKey::Disabled && shiftKey == ModifierKey::Disabled && actionKey == 0x4C)
{
return KeyboardManagerHelper::ErrorType::WinL;
}
// Ctrl+Alt+Del
if (winKey == ModifierKey::Disabled && ctrlKey != ModifierKey::Disabled && altKey != ModifierKey::Disabled && shiftKey == ModifierKey::Disabled && actionKey == VK_DELETE)
{
return KeyboardManagerHelper::ErrorType::CtrlAltDel;
}
return KeyboardManagerHelper::ErrorType::NoError;
}

View File

@ -7,21 +7,20 @@ namespace KeyboardManagerInput
class InputInterface; class InputInterface;
} }
class LayoutMap; class LayoutMap;
namespace KeyboardManagerHelper
{
enum class ErrorType;
}
class Shortcut class Shortcut
{ {
private: private:
// Function to split a wstring based on a delimiter and return a vector of split strings
std::vector<std::wstring> splitwstring(const std::wstring& input, wchar_t delimiter);
public:
ModifierKey winKey; ModifierKey winKey;
ModifierKey ctrlKey; ModifierKey ctrlKey;
ModifierKey altKey; ModifierKey altKey;
ModifierKey shiftKey; ModifierKey shiftKey;
DWORD actionKey; DWORD actionKey;
public:
// By default create an empty shortcut // By default create an empty shortcut
Shortcut() : Shortcut() :
winKey(ModifierKey::Disabled), ctrlKey(ModifierKey::Disabled), altKey(ModifierKey::Disabled), shiftKey(ModifierKey::Disabled), actionKey(NULL) winKey(ModifierKey::Disabled), ctrlKey(ModifierKey::Disabled), altKey(ModifierKey::Disabled), shiftKey(ModifierKey::Disabled), actionKey(NULL)
@ -111,9 +110,6 @@ public:
// Function to reset all the keys in the shortcut // Function to reset all the keys in the shortcut
void Reset(); void Reset();
// Function to return true if the shortcut is valid. A valid shortcut has atleast one modifier, as well as an action key
bool IsValidShortcut() const;
// Function to return the action key // Function to return the action key
DWORD GetActionKey() const; DWORD GetActionKey() const;
@ -150,9 +146,6 @@ public:
// Function to return the string representation of the shortcut in virtual key codes appended in a string by ";" separator. // Function to return the string representation of the shortcut in virtual key codes appended in a string by ";" separator.
winrt::hstring ToHstringVK() const; winrt::hstring ToHstringVK() const;
// Function to return a vector of hstring for each key in the display order
std::vector<winrt::hstring> GetKeyVector(LayoutMap& keyboardMap) const;
// Function to return a vector of key codes in the display order // Function to return a vector of key codes in the display order
std::vector<DWORD> GetKeyCodes(); std::vector<DWORD> GetKeyCodes();
@ -167,12 +160,6 @@ public:
// Function to get the number of modifiers that are common between the current shortcut and the shortcut in the argument // Function to get the number of modifiers that are common between the current shortcut and the shortcut in the argument
int GetCommonModifiersCount(const Shortcut& input) const; int GetCommonModifiersCount(const Shortcut& input) const;
// Function to check if the two shortcuts are equal or cover the same set of keys. Return value depends on type of overlap
static KeyboardManagerHelper::ErrorType DoKeysOverlap(const Shortcut& first, const Shortcut& second);
// Function to check if the shortcut is illegal (i.e. Win+L or Ctrl+Alt+Del)
KeyboardManagerHelper::ErrorType IsShortcutIllegal() const;
}; };
using KeyShortcutUnion = std::variant<DWORD, Shortcut>; using KeyShortcutUnion = std::variant<DWORD, Shortcut>;

Some files were not shown because too many files have changed in this diff Show More