mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-04 19:36:57 +01:00
Compare commits
42 Commits
dev/vanzue
...
sachaple/p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac949c64d7 | ||
|
|
4fe3e27fa4 | ||
|
|
7105802831 | ||
|
|
17e8366c6e | ||
|
|
f01de620ca | ||
|
|
645e798d97 | ||
|
|
ef944a234f | ||
|
|
ba971d4e21 | ||
|
|
9cd4a3e414 | ||
|
|
c22e78870b | ||
|
|
1253ed6607 | ||
|
|
99fc54ce94 | ||
|
|
37162e24f6 | ||
|
|
b075c00f2b | ||
|
|
496220f77e | ||
|
|
195f14a3c7 | ||
|
|
60bf86825b | ||
|
|
4ef3f23897 | ||
|
|
5bd9dd5935 | ||
|
|
5590eb9484 | ||
|
|
6f06f76784 | ||
|
|
5981d0e81e | ||
|
|
539a4e5678 | ||
|
|
aea217ddca | ||
|
|
d001a4c0e0 | ||
|
|
5712123598 | ||
|
|
e504653323 | ||
|
|
b46b8d176f | ||
|
|
b98f233b75 | ||
|
|
083ec27a45 | ||
|
|
bca780f886 | ||
|
|
d4e618cdc9 | ||
|
|
e1cb01d188 | ||
|
|
bddfe42d39 | ||
|
|
30d346c93f | ||
|
|
899b5016b9 | ||
|
|
0d05089316 | ||
|
|
6295ed86ca | ||
|
|
73925885dd | ||
|
|
437d2dd5f7 | ||
|
|
a4bd09a2c8 | ||
|
|
c2aae52bba |
143
.github/actions/spell-check/expect.txt
vendored
143
.github/actions/spell-check/expect.txt
vendored
@@ -1,4 +1,5 @@
|
||||
aaaa
|
||||
Abbrivation
|
||||
abgr
|
||||
abi
|
||||
ABlocked
|
||||
@@ -54,6 +55,7 @@ AOC
|
||||
aocfnapldcnfbofgmbbllojgocaelgdd
|
||||
APARTMENTTHREADED
|
||||
APeriod
|
||||
apidl
|
||||
APIENTRY
|
||||
APIIs
|
||||
APPBARDATA
|
||||
@@ -80,6 +82,7 @@ ARCHITEW
|
||||
arcosh
|
||||
ARemapped
|
||||
argb
|
||||
ari
|
||||
arik
|
||||
Arity
|
||||
arjunbalgovind
|
||||
@@ -89,6 +92,7 @@ ARRAYSIZE
|
||||
arsinh
|
||||
artanh
|
||||
Artsakh
|
||||
arw
|
||||
asdf
|
||||
AShortcut
|
||||
ASingle
|
||||
@@ -126,11 +130,13 @@ Bayan
|
||||
bbwe
|
||||
bck
|
||||
Belarus
|
||||
BESTEFFORT
|
||||
betadele
|
||||
betsegaw
|
||||
BGR
|
||||
bgra
|
||||
bhid
|
||||
BHIDSF
|
||||
bigbar
|
||||
bigobj
|
||||
binlog
|
||||
@@ -176,15 +182,18 @@ buildtask
|
||||
buildtransitive
|
||||
Burkina
|
||||
Buryatia
|
||||
BVal
|
||||
BValue
|
||||
BYPOSITION
|
||||
bytearray
|
||||
Caiguna
|
||||
CALG
|
||||
callbackptr
|
||||
calpwstr
|
||||
Cangjie
|
||||
CANRENAME
|
||||
CAPTURECHANGED
|
||||
CARRAY
|
||||
CAtl
|
||||
cch
|
||||
CCom
|
||||
@@ -193,6 +202,7 @@ cdecl
|
||||
CDeclaration
|
||||
CDEF
|
||||
cdpx
|
||||
CElems
|
||||
CENTERALIGN
|
||||
cguid
|
||||
changecursor
|
||||
@@ -206,6 +216,7 @@ Chrzan
|
||||
cht
|
||||
Chukotka
|
||||
Chuuk
|
||||
cidl
|
||||
CIELAB
|
||||
CIEXYZ
|
||||
cim
|
||||
@@ -304,6 +315,7 @@ CREATEWINDOWFAILED
|
||||
critsec
|
||||
Crossdevice
|
||||
CRSEL
|
||||
crw
|
||||
CSearch
|
||||
CSettings
|
||||
CSIDL
|
||||
@@ -323,6 +335,7 @@ CURSORINFO
|
||||
cursorpos
|
||||
customaction
|
||||
CUSTOMACTIONTEST
|
||||
CVal
|
||||
cvd
|
||||
CVirtual
|
||||
cvtepu
|
||||
@@ -338,6 +351,7 @@ cziplib
|
||||
Dac
|
||||
dacl
|
||||
damienleroy
|
||||
DANGEROUSLYCOMMITMERELYTODISKCACHE
|
||||
Danmarkshavn
|
||||
DARKPURPLE
|
||||
DARKTEAL
|
||||
@@ -361,9 +375,13 @@ dcomp
|
||||
dcompi
|
||||
DCompiler
|
||||
DComposition
|
||||
DCR
|
||||
dcr
|
||||
dcs
|
||||
Dct
|
||||
DDEIf
|
||||
DDevice
|
||||
ddf
|
||||
Dds
|
||||
DDxgi
|
||||
Deact
|
||||
debian
|
||||
@@ -382,6 +400,7 @@ DEFERERASE
|
||||
DEFPUSHBUTTON
|
||||
deinitialization
|
||||
DELA
|
||||
DELAYCREATION
|
||||
deletethis
|
||||
Delimarsky
|
||||
dend
|
||||
@@ -433,6 +452,7 @@ DQTYPE
|
||||
DRAWFRAME
|
||||
drawingcolor
|
||||
dreamsofameaningfullife
|
||||
drf
|
||||
drivedetectionwarning
|
||||
dshow
|
||||
dutil
|
||||
@@ -447,6 +467,7 @@ DVTARGETDEVICE
|
||||
DWINRT
|
||||
dwl
|
||||
dwm
|
||||
Dwma
|
||||
dwmapi
|
||||
DWMCOLORIZATIONCOLORCHANGED
|
||||
DWMCOMPOSITIONCHANGED
|
||||
@@ -474,6 +495,7 @@ editkeyboardwindow
|
||||
EDITSHORTCUTS
|
||||
editshortcutswindow
|
||||
EFile
|
||||
eip
|
||||
ekus
|
||||
emmintrin
|
||||
Emoji
|
||||
@@ -513,8 +535,12 @@ EWXLOGOFF
|
||||
EWXPOWEROFF
|
||||
EWXREBOOT
|
||||
EWXSHUTDOWN
|
||||
Exa
|
||||
exabyte
|
||||
examplehandler
|
||||
examplepowertoy
|
||||
Excep
|
||||
EXCEPINFO
|
||||
EXCLUDEFROMCAPTURE
|
||||
exdisp
|
||||
executionpolicy
|
||||
@@ -530,6 +556,8 @@ EXSEL
|
||||
exstyle
|
||||
EXTENDEDKEY
|
||||
EXTENDEDVERBS
|
||||
EXTRINSICPROPERTIES
|
||||
EXTRINSICPROPERTIESONLY
|
||||
eyetracker
|
||||
fabricbot
|
||||
fancyzones
|
||||
@@ -538,6 +566,7 @@ FANCYZONESEDITOR
|
||||
Farbraum
|
||||
Faroe
|
||||
FARPROC
|
||||
FASTPROPERTIESONLY
|
||||
fdw
|
||||
feimage
|
||||
fff
|
||||
@@ -553,7 +582,7 @@ FILEOP
|
||||
FILEOS
|
||||
FILESUBTYPE
|
||||
FILESYSPATH
|
||||
FILETIME
|
||||
filetime
|
||||
FILEVERSION
|
||||
Filtergraph
|
||||
Filterkeyboard
|
||||
@@ -621,6 +650,7 @@ gwl
|
||||
GWLP
|
||||
HACCEL
|
||||
handlekeyboardhookevent
|
||||
HANDLERPROPERTIESONLY
|
||||
handlerroutine
|
||||
hangeul
|
||||
Hankaku
|
||||
@@ -645,6 +675,7 @@ HCRYPTHASH
|
||||
HCRYPTPROV
|
||||
hcwhite
|
||||
hdc
|
||||
HDR
|
||||
hdrop
|
||||
HEB
|
||||
Heiko
|
||||
@@ -658,8 +689,9 @@ HHmmss
|
||||
HHOOK
|
||||
hhx
|
||||
HIBYTE
|
||||
HICON
|
||||
hicon
|
||||
HIDEWINDOW
|
||||
hif
|
||||
highlighter
|
||||
HIMAGELIST
|
||||
himl
|
||||
@@ -712,6 +744,7 @@ HTCLIENT
|
||||
HTHUMBNAIL
|
||||
HTOUCHINPUT
|
||||
HTTRANSPARENT
|
||||
HVal
|
||||
HValue
|
||||
Hvci
|
||||
hwb
|
||||
@@ -724,12 +757,18 @@ HWNDPREV
|
||||
hyjiacan
|
||||
IBase
|
||||
IBeam
|
||||
IBitmap
|
||||
IBlock
|
||||
ICapture
|
||||
ICEBLUE
|
||||
IClass
|
||||
IColor
|
||||
icolumn
|
||||
ICONERROR
|
||||
IContext
|
||||
IData
|
||||
IDD
|
||||
IDecoder
|
||||
IDesktop
|
||||
IDirect
|
||||
idl
|
||||
@@ -738,6 +777,8 @@ IDOn
|
||||
IDR
|
||||
idx
|
||||
IDXGI
|
||||
IEncoder
|
||||
IEnum
|
||||
IExec
|
||||
IEXPLORE
|
||||
Iface
|
||||
@@ -746,8 +787,10 @@ IFACEMETHODIMP
|
||||
IFilter
|
||||
IGraphics
|
||||
iid
|
||||
IIDI
|
||||
Iindex
|
||||
IIO
|
||||
iiq
|
||||
Ijwhost
|
||||
IKs
|
||||
ILogon
|
||||
@@ -760,6 +803,7 @@ imageresizersettings
|
||||
imagingdevices
|
||||
Imc
|
||||
ime
|
||||
IMetadata
|
||||
imeutil
|
||||
inetcpl
|
||||
Infobar
|
||||
@@ -807,16 +851,20 @@ ipreviewhandler
|
||||
ipreviewhandlervisualssetfont
|
||||
IProperty
|
||||
IPublic
|
||||
IQuery
|
||||
IReader
|
||||
isbi
|
||||
ISearch
|
||||
ISettings
|
||||
isfinite
|
||||
IShell
|
||||
isocpp
|
||||
ISource
|
||||
iss
|
||||
ISurface
|
||||
ITask
|
||||
ith
|
||||
ITHUMBNAIL
|
||||
ithumbnail
|
||||
Ittoqqortoormiit
|
||||
IUI
|
||||
IUnknown
|
||||
@@ -826,8 +874,10 @@ IWIC
|
||||
iwr
|
||||
IYUV
|
||||
JArray
|
||||
jfi
|
||||
jfif
|
||||
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
|
||||
jif
|
||||
jjw
|
||||
jobject
|
||||
jpe
|
||||
@@ -841,6 +891,8 @@ jxr
|
||||
jyuwono
|
||||
KBDLLHOOKSTRUCT
|
||||
kbm
|
||||
kdc
|
||||
Keybd
|
||||
KEYBDINPUT
|
||||
keyboardeventhandlers
|
||||
keyboardmanager
|
||||
@@ -881,6 +933,7 @@ LBUTTON
|
||||
LBUTTONDBLCLK
|
||||
LBUTTONDOWN
|
||||
LBUTTONUP
|
||||
Lcid
|
||||
LCIDTo
|
||||
lcl
|
||||
Lclean
|
||||
@@ -921,6 +974,8 @@ LOCALPACKAGE
|
||||
localport
|
||||
LOCALSYSTEM
|
||||
LOCATIONCHANGE
|
||||
LOCKBYTES
|
||||
LOCKTYPE
|
||||
logconsole
|
||||
logfile
|
||||
LOGFONT
|
||||
@@ -969,6 +1024,7 @@ LTRB
|
||||
LTRREADING
|
||||
Luhansk
|
||||
luid
|
||||
LVal
|
||||
LWA
|
||||
lwin
|
||||
LZero
|
||||
@@ -1010,6 +1066,7 @@ MBUTTONDOWN
|
||||
MBUTTONUP
|
||||
MCDT
|
||||
MCST
|
||||
mdc
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -1017,6 +1074,8 @@ mdtxt
|
||||
mdwn
|
||||
MEDIASUBTYPE
|
||||
mediatype
|
||||
mef
|
||||
Mega
|
||||
Melman
|
||||
MENUITEMINFO
|
||||
MENUITEMINFOW
|
||||
@@ -1082,6 +1141,7 @@ mozilla
|
||||
mpmc
|
||||
MRM
|
||||
mru
|
||||
mrw
|
||||
msbuild
|
||||
msc
|
||||
msclr
|
||||
@@ -1143,6 +1203,7 @@ NCRENDERING
|
||||
Ndombe
|
||||
ndp
|
||||
NEEDDISPATCH
|
||||
neighborings
|
||||
Nemeth
|
||||
NESW
|
||||
netcore
|
||||
@@ -1185,6 +1246,7 @@ NONELEVATED
|
||||
NONINFRINGEMENT
|
||||
nonpackaged
|
||||
nonstd
|
||||
NOOPEN
|
||||
NOOWNERZORDER
|
||||
NOPARENTNOTIFY
|
||||
NOREDIRECTIONBITMAP
|
||||
@@ -1212,6 +1274,7 @@ NOUPDATE
|
||||
NOZORDER
|
||||
NPH
|
||||
NResize
|
||||
nrw
|
||||
NTAPI
|
||||
ntdll
|
||||
NTFS
|
||||
@@ -1248,14 +1311,20 @@ oledbconnection
|
||||
OLIVEGREEN
|
||||
onebranch
|
||||
onenote
|
||||
ONLYIFCURRENT
|
||||
ONLYONCE
|
||||
onstd
|
||||
oobe
|
||||
OOBEPT
|
||||
opencode
|
||||
OPENSLOWITEM
|
||||
opensource
|
||||
openspecs
|
||||
openxmlformats
|
||||
OPLOCK
|
||||
OPTIMIZEFORINVOKE
|
||||
ORAW
|
||||
ori
|
||||
ORPHANEDDIALOGTITLE
|
||||
oss
|
||||
ostr
|
||||
@@ -1271,6 +1340,7 @@ outro
|
||||
outsettings
|
||||
OVERLAPPEDWINDOW
|
||||
overlaywindow
|
||||
overriden
|
||||
Oversampling
|
||||
OWNDC
|
||||
Packagemanager
|
||||
@@ -1291,10 +1361,14 @@ pathcch
|
||||
Pathto
|
||||
PAUDIO
|
||||
pbc
|
||||
Pbgra
|
||||
pbgra
|
||||
PBlob
|
||||
pcb
|
||||
pcch
|
||||
pcelt
|
||||
pch
|
||||
PCIDLIST
|
||||
pcs
|
||||
PCWSTR
|
||||
pdb
|
||||
pdbonly
|
||||
@@ -1304,8 +1378,12 @@ pdtobj
|
||||
pdw
|
||||
PDWORD
|
||||
pedrolamas
|
||||
pef
|
||||
PElems
|
||||
PERCEIVEDFLAG
|
||||
Percision
|
||||
pesi
|
||||
petabyte
|
||||
peteblois
|
||||
PEXCEPTION
|
||||
pfn
|
||||
@@ -1354,10 +1432,12 @@ powertoy
|
||||
POWERTOYNAME
|
||||
powertoyssetup
|
||||
Powrprof
|
||||
ppenum
|
||||
ppidl
|
||||
ppmt
|
||||
pprm
|
||||
pproc
|
||||
pprop
|
||||
ppsi
|
||||
ppsid
|
||||
ppsrm
|
||||
@@ -1367,9 +1447,11 @@ pptal
|
||||
ppv
|
||||
prc
|
||||
precomp
|
||||
PREFERQUERYPROPERTIES
|
||||
Prefixer
|
||||
Preinstalled
|
||||
prevhost
|
||||
Previer
|
||||
previewer
|
||||
previewhandlerframeinfo
|
||||
previewpane
|
||||
@@ -1377,6 +1459,7 @@ previouscamera
|
||||
PREVIOUSINSTALLFOLDER
|
||||
PREVIOUSVERSIONSINSTALLED
|
||||
prevpane
|
||||
PRGBA
|
||||
prgms
|
||||
pri
|
||||
Primorsky
|
||||
@@ -1393,15 +1476,21 @@ PROGRAMFILES
|
||||
projectname
|
||||
PROPBAG
|
||||
PROPERTYKEY
|
||||
PROPERTYNOTFOUND
|
||||
propkey
|
||||
PROPVARIANT
|
||||
propvarutil
|
||||
prvpane
|
||||
psapi
|
||||
pscid
|
||||
PSECURITY
|
||||
psfgao
|
||||
psfi
|
||||
Psr
|
||||
psrm
|
||||
psrree
|
||||
pstatstg
|
||||
pstm
|
||||
pstr
|
||||
pstream
|
||||
pstrm
|
||||
@@ -1413,7 +1502,9 @@ ptd
|
||||
PTOKEN
|
||||
PToy
|
||||
ptstr
|
||||
pui
|
||||
PULONG
|
||||
pvar
|
||||
pwa
|
||||
pwcs
|
||||
pwsh
|
||||
@@ -1436,6 +1527,7 @@ QUNS
|
||||
qwertyuiopasdfghjklzxcvbnm
|
||||
qword
|
||||
qwrtyuiopsghjklzxvnm
|
||||
raf
|
||||
RAII
|
||||
RAlt
|
||||
randyrants
|
||||
@@ -1505,10 +1597,15 @@ restrictederrorinfo
|
||||
resultlist
|
||||
resw
|
||||
resx
|
||||
retunred
|
||||
retval
|
||||
rfc
|
||||
rfid
|
||||
RGBE
|
||||
RGBQUAD
|
||||
rgbs
|
||||
rgelt
|
||||
rgf
|
||||
rgn
|
||||
rgs
|
||||
rhs
|
||||
@@ -1554,9 +1651,13 @@ Rutkas
|
||||
RValue
|
||||
rvm
|
||||
rwin
|
||||
rwl
|
||||
rwz
|
||||
ryanbodrug
|
||||
saahmedm
|
||||
sachaple
|
||||
sacl
|
||||
SAFEARRAY
|
||||
safeprojectname
|
||||
SAMEKEYPREVIOUSLYMAPPED
|
||||
SAMESHORTCUTPREVIOUSLYMAPPED
|
||||
@@ -1565,8 +1666,10 @@ scancode
|
||||
scanled
|
||||
Schd
|
||||
schedtasks
|
||||
SCID
|
||||
Scip
|
||||
scipbe
|
||||
Scode
|
||||
scoobe
|
||||
SCOPEID
|
||||
screenshot
|
||||
@@ -1606,8 +1709,10 @@ SFP
|
||||
sharpkeys
|
||||
SHCNE
|
||||
SHCNF
|
||||
Shcontf
|
||||
shcore
|
||||
shellapi
|
||||
SHELLDETAILS
|
||||
SHELLDLL
|
||||
shellex
|
||||
SHELLEXECUTEINFO
|
||||
@@ -1616,6 +1721,7 @@ shellscalingapi
|
||||
shemptyrecyclebina
|
||||
SHFILEINFO
|
||||
SHGFI
|
||||
Shgno
|
||||
Shl
|
||||
shldisp
|
||||
shlobj
|
||||
@@ -1672,6 +1778,7 @@ smartphone
|
||||
SMTO
|
||||
snd
|
||||
snwprintf
|
||||
Softcoded
|
||||
softline
|
||||
somil
|
||||
Soref
|
||||
@@ -1695,6 +1802,8 @@ Srch
|
||||
sre
|
||||
Srednekolymsk
|
||||
SResize
|
||||
srf
|
||||
SRGB
|
||||
srme
|
||||
srre
|
||||
srw
|
||||
@@ -1724,8 +1833,10 @@ STDMETHODCALLTYPE
|
||||
STDMETHODIMP
|
||||
stefan
|
||||
Stereolithography
|
||||
STGC
|
||||
stgm
|
||||
STGMEDIUM
|
||||
STGTY
|
||||
sticpl
|
||||
stl
|
||||
storelogo
|
||||
@@ -1733,6 +1844,8 @@ STRINGIZE
|
||||
stringtable
|
||||
stringval
|
||||
Strmiids
|
||||
Stroe
|
||||
Strret
|
||||
strsafe
|
||||
strutil
|
||||
sttngs
|
||||
@@ -1797,6 +1910,7 @@ tasklist
|
||||
taskschd
|
||||
tchar
|
||||
Tcollab
|
||||
tcs
|
||||
tcscpy
|
||||
TCustom
|
||||
tdbuild
|
||||
@@ -1806,6 +1920,7 @@ telem
|
||||
Templated
|
||||
templatenamespace
|
||||
Tenggara
|
||||
terabyte
|
||||
testapp
|
||||
testcase
|
||||
testhost
|
||||
@@ -1826,12 +1941,15 @@ timediff
|
||||
timeunion
|
||||
timeutil
|
||||
timezones
|
||||
titlebar
|
||||
Titlecase
|
||||
TLayout
|
||||
tlb
|
||||
tlbimp
|
||||
TMPVAR
|
||||
TNP
|
||||
toggleswitch
|
||||
toogle
|
||||
toolkitcontrols
|
||||
toolkitconverters
|
||||
Toolset
|
||||
@@ -1882,6 +2000,7 @@ UIs
|
||||
Ulaanbaatar
|
||||
ULARGE
|
||||
ULONGLONG
|
||||
UMsg
|
||||
unassign
|
||||
uncompilable
|
||||
UNCPRIORITY
|
||||
@@ -1904,6 +2023,7 @@ unregistering
|
||||
unremapped
|
||||
unsubscribe
|
||||
unvirtualized
|
||||
UOffset
|
||||
Updatelayout
|
||||
UPGRADINGPRODUCTCODE
|
||||
Uptool
|
||||
@@ -1912,6 +2032,7 @@ Usb
|
||||
USEDEFAULT
|
||||
USEFILEATTRIBUTES
|
||||
USERDATA
|
||||
USERDEFINED
|
||||
USERDOMAIN
|
||||
userprofile
|
||||
USESHOWWINDOW
|
||||
@@ -1919,6 +2040,7 @@ USESTDHANDLES
|
||||
USRDLL
|
||||
Utc
|
||||
utf
|
||||
UType
|
||||
uuidof
|
||||
Uvs
|
||||
uwp
|
||||
@@ -1927,6 +2049,7 @@ UYVY
|
||||
vabdq
|
||||
validmodulename
|
||||
Vanara
|
||||
VARTYPE
|
||||
vcamp
|
||||
vccorlib
|
||||
vcdl
|
||||
@@ -1946,6 +2069,7 @@ VERBSONLY
|
||||
VERBW
|
||||
VERIFYCONTEXT
|
||||
verrsrc
|
||||
VERSIONED
|
||||
VERSIONINFO
|
||||
Versioning
|
||||
VFT
|
||||
@@ -1999,6 +2123,7 @@ WDA
|
||||
wdp
|
||||
wdupenv
|
||||
weakme
|
||||
webbrowser
|
||||
webcam
|
||||
webpage
|
||||
websearch
|
||||
@@ -2026,7 +2151,7 @@ WINDOWNAME
|
||||
WINDOWPLACEMENT
|
||||
WINDOWPOSCHANGED
|
||||
WINDOWPOSCHANGING
|
||||
Windowsapp
|
||||
windowsapp
|
||||
WINDOWSBUILDNUMBER
|
||||
Windowscodecs
|
||||
windowssearch
|
||||
@@ -2077,6 +2202,7 @@ WMICIM
|
||||
WMKEYDOWN
|
||||
WMKEYUP
|
||||
wmp
|
||||
WMSDK
|
||||
WMSYSKEYDOWN
|
||||
WMSYSKEYUP
|
||||
wnd
|
||||
@@ -2097,13 +2223,16 @@ wpr
|
||||
wprp
|
||||
wql
|
||||
wregex
|
||||
WReserved
|
||||
WResize
|
||||
writefile
|
||||
Wrk
|
||||
wrl
|
||||
WScan
|
||||
wsf
|
||||
wsh
|
||||
wsl
|
||||
wsp
|
||||
wss
|
||||
wstr
|
||||
wsz
|
||||
@@ -2115,6 +2244,7 @@ wtypes
|
||||
Wubi
|
||||
wuceffectsi
|
||||
WVC
|
||||
WVk
|
||||
Wwan
|
||||
Wwanpp
|
||||
XAttribute
|
||||
@@ -2142,6 +2272,7 @@ yinle
|
||||
yinwang
|
||||
yinyue
|
||||
YOffset
|
||||
YQuantized
|
||||
YStr
|
||||
YUY
|
||||
yuyoyuppe
|
||||
|
||||
@@ -85,6 +85,13 @@
|
||||
"modules\\FileLocksmith\\PowerToys.FileLocksmithUI.exe",
|
||||
"modules\\FileLocksmith\\PowerToys.FileLocksmithUI.dll",
|
||||
|
||||
"modules\\Peek\\Peek.Common.dll",
|
||||
"modules\\Peek\\Peek.FilePreviewer.dll",
|
||||
"modules\\Peek\\Powertoys.Peek.UI.dll",
|
||||
"modules\\Peek\\Powertoys.Peek.UI.exe",
|
||||
"modules\\Peek\\WIC.dll",
|
||||
"modules\\Peek\\Powertoys.Peek.dll",
|
||||
|
||||
"modules\\ImageResizer\\PowerToys.ImageResizer.exe",
|
||||
"modules\\ImageResizer\\PowerToys.ImageResizer.dll",
|
||||
"modules\\ImageResizer\\PowerToys.ImageResizerExt.dll",
|
||||
|
||||
@@ -38,6 +38,7 @@ $items | ForEach-Object {
|
||||
(-not $_.Name.EndsWith("Microsoft.Xaml.Interactions.dll")) -and
|
||||
(-not $_.Name.EndsWith("Microsoft.Xaml.Interactivity.dll")) -and
|
||||
(-not $_.Name.EndsWith("hyjiacan.py4n.dll")) -and
|
||||
(-not $_.Name.EndsWith("Microsoft.Windows.Widgets.Projection.dll")) -and
|
||||
(-not $_.Name.EndsWith("Microsoft.WindowsAppRuntime.Release.Net.dll"))
|
||||
)
|
||||
{
|
||||
|
||||
705
PowerToys.sln
705
PowerToys.sln
File diff suppressed because it is too large
Load Diff
@@ -99,6 +99,10 @@ In this release, we focused on stability and improvements.
|
||||
- Fixed an editor crash caused by deleting a zone while trying to move it.
|
||||
- Reduce the time it takes the tooltip for layout shortcut setting to appear in the editor.
|
||||
|
||||
### File explorer add-ons
|
||||
|
||||
- Added .log file support to the Monaco preview handler. Thanks [@Eagle3386](https://github.com/Eagle3386)!
|
||||
|
||||
### File Locksmith
|
||||
|
||||
- Fixed an issue causing File Locksmith to hang when looking for open handles in some machines.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<?define PowerAccentProjectName="PowerAccent"?>
|
||||
<?define PowerRenameProjectName="PowerRename"?>
|
||||
<?define FileLocksmithProjectName="FileLocksmith"?>
|
||||
<?define PeekProjectName="Peek"?>
|
||||
<?define ColorPickerProjectName="ColorPicker"?>
|
||||
<?define PowerOCRProjectName="PowerOCR"?>
|
||||
<?define VideoConferenceProjectName="VideoConference"?>
|
||||
@@ -57,11 +58,11 @@
|
||||
|
||||
<?define SettingsV2Files=backup_restore_settings.json;Ijwhost.dll;ColorCode.Core.dll;ColorCode.WinUI.dll;CommunityToolkit.Common.dll;CommunityToolkit.Labs.WinUI.SettingsControls.dll;CommunityToolkit.WinUI.dll;CommunityToolkit.WinUI.UI.Controls.Core.dll;CommunityToolkit.WinUI.UI.Controls.DataGrid.dll;CommunityToolkit.WinUI.UI.Controls.Input.dll;CommunityToolkit.WinUI.UI.Controls.Layout.dll;CommunityToolkit.WinUI.UI.Controls.Markdown.dll;CommunityToolkit.WinUI.UI.Controls.Media.dll;CommunityToolkit.WinUI.UI.Controls.Primitives.dll;CommunityToolkit.WinUI.UI.dll;icon.ico;Microsoft.Graphics.Canvas.Interop.dll;Microsoft.InteractiveExperiences.Projection.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.Settings.deps.json;PowerToys.Settings.dll;PowerToys.Settings.exe;PowerToys.Settings.runtimeconfig.json;PowerToys.Settings.UI.Lib.dll;resources.pri;System.IO.Abstractions.dll;WinRT.Runtime.dll;Microsoft.Graphics.Canvas.dll;PowerToys.GPOWrapper.dll?>
|
||||
|
||||
<?define SettingsV2AssetsModulesFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerAccent.png;PowerOCR.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ScreenRuler.png;ShortcutGuide.png;VideoConference.png?>
|
||||
<?define SettingsV2AssetsModulesFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;Peek.png;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;ImageResizer.png;KBM.png;MouseUtils.png;PowerAccent.png;PowerOCR.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ScreenRuler.png;ShortcutGuide.png;VideoConference.png?>
|
||||
|
||||
<?define SettingsV2OOBEAssetsModulesFiles=ColorPicker.gif;AlwaysOnTop.png;HostsFileEditor.png;Awake.png;FancyZones.gif;FileExplorer.png;FileLocksmith.gif;ImageResizer.gif;KBM.gif;MouseUtils.gif;PowerAccent.gif;PowerOCR.gif;PowerRename.gif;Run.gif;ScreenRuler.gif;OOBEShortcutGuide.png;VideoConferenceMute.png;OOBEPTHero.png?>
|
||||
|
||||
<?define SettingsV2OOBEAssetsFluentIconsFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;AlwaysOnTop.png;Awake.png;FileExplorerPreview.png;FindMyMouse.png;Hosts.png;ImageResizer.png;KeyboardManager.png;MouseHighlighter.png;MouseCrosshairs.png;MouseUtils.png;PowerAccent.png;PowerOcr.png;PowerRename.png;PowerToys.png;PowerToysRun.png;ScreenRuler.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png?>
|
||||
<?define SettingsV2OOBEAssetsFluentIconsFiles=ColorPicker.png;FancyZones.png;FileLocksmith.png;Peek.png;AlwaysOnTop.png;Awake.png;FileExplorerPreview.png;FindMyMouse.png;Hosts.png;ImageResizer.png;KeyboardManager.png;MouseHighlighter.png;MouseCrosshairs.png;MouseUtils.png;PowerAccent.png;PowerOcr.png;PowerRename.png;PowerToys.png;PowerToysRun.png;ScreenRuler.png;Settings.png;ShortcutGuide.png;VideoConferenceMute.png?>
|
||||
|
||||
<?define SettingsV2MicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||
|
||||
@@ -125,6 +126,10 @@
|
||||
|
||||
<?define FileLocksmithAssetsFiles=Icon.ico?>
|
||||
|
||||
<?define PeekFiles=CommunityToolkit.Mvvm.dll;Microsoft.InteractiveExperiences.Projection.dll;WinRT.Runtime.dll;Ijwhost.dll;Microsoft.Windows.ApplicationModel.DynamicDependency.Projection.dll;Microsoft.Windows.ApplicationModel.Resources.Projection.dll;Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.dll;Microsoft.Windows.AppLifecycle.Projection.dll;Microsoft.Windows.SDK.NET.dll;Microsoft.Windows.System.Power.Projection.dll;Microsoft.WindowsAppRuntime.Bootstrap.Net.dll;Microsoft.WinUI.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;Powertoys.Interop.dll;Peek.Common.dll;Peek.FilePreviewer.dll;Powertoys.Peek.UI.dll;Powertoys.Peek.UI.exe;Powertoys.Peek.UI.deps.json;Powertoys.Peek.UI.runtimeconfig.json;resources.pri;System.CodeDom.dll;System.Drawing.Common.dll;System.Management.dll;WIC.dll;WinUIEx.dll;System.IO.Abstractions.dll;PowerToys.Settings.UI.Lib.dll?>
|
||||
|
||||
<?define PeekAssetsFiles=Icon.ico?>
|
||||
|
||||
<?define PowerRenameSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?>
|
||||
|
||||
<?define ImageResizerSparsePackageAssets=LargeTile.png;SmallTile.png;SplashScreen.png;Square150x150Logo.png;Square44x44Logo.png;storelogo.png;Wide310x150Logo.png?>
|
||||
@@ -142,6 +147,8 @@
|
||||
<?define MeasureToolMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||
|
||||
<?define HostsMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||
|
||||
<?define PeekMicrosoftUIXamlAssetsInstallFiles=NoiseAsset_256x256_PNG.png?>
|
||||
|
||||
<?define PowerAccentFiles=ControlzEx.dll;Ijwhost.dll;Microsoft.Xaml.Behaviors.dll;PowerAccent.Core.dll;PowerToys.PowerAccentModuleInterface.dll;PowerToys.ManagedCommon.dll;PowerToys.ManagedTelemetry.dll;PowerToys.PowerAccent.deps.json;PowerToys.PowerAccent.dll;PowerToys.PowerAccent.exe;PowerToys.PowerAccent.runtimeconfig.json;PowerToys.Common.UI.dll;PowerToys.Settings.UI.Lib.dll;System.IO.Abstractions.dll;Vanara.Core.dll;Vanara.PInvoke.ComCtl32.dll;Vanara.PInvoke.Cryptography.dll;Vanara.PInvoke.Gdi32.dll;Vanara.PInvoke.Kernel32.dll;Vanara.PInvoke.Ole.dll;Vanara.PInvoke.Rpc.dll;Vanara.PInvoke.Security.dll;Vanara.PInvoke.Shared.dll;Vanara.PInvoke.Shell32.dll;Vanara.PInvoke.ShlwApi.dll;Vanara.PInvoke.User32.dll;PowerToys.PowerAccentKeyboardService.dll;Microsoft.Windows.SDK.NET.dll;WinRT.Runtime.dll;PowerToys.GPOWrapper.dll;UnicodeInformation.dll;ModernWpf.dll;ModernWpf.Controls.dll?>
|
||||
|
||||
@@ -604,8 +611,16 @@
|
||||
<Directory Id="HostsMicrosoftUIXamlAssetsInstallFolder" Name="Assets" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<!-- Launcher -->
|
||||
|
||||
<!-- Peek -->
|
||||
<Directory Id="PeekInstallFolder" Name="$(var.PeekProjectName)">
|
||||
<Directory Id="PeekAssetsInstallFolder" Name="Assets" />
|
||||
<Directory Id="PeekMicrosoftUIXamlInstallFolder" Name="Microsoft.UI.Xaml">
|
||||
<Directory Id="PeekMicrosoftUIXamlAssetsInstallFolder" Name="Assets" />
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<!-- Launcher -->
|
||||
<Directory Id="LauncherInstallFolder" Name="launcher">
|
||||
<Directory Id="LauncherImagesFolder" Name="Images" />
|
||||
<Directory Id="LauncherPropertiesFolder" Name="Properties" />
|
||||
@@ -1085,7 +1100,52 @@
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- KBM -->
|
||||
<!-- Peek -->
|
||||
<DirectoryRef Id="PeekInstallFolder" FileSource="$(var.BinDir)modules\$(var.PeekProjectName)">
|
||||
<Component Id="Module_Peek" Win64="yes">
|
||||
<File Source="$(var.BinDir)modules\$(var.PeekProjectName)\PowerToys.Peek.dll" />
|
||||
</Component>
|
||||
|
||||
<?foreach File in $(var.PeekFiles)?>
|
||||
<Component Id="Peek_$(var.File)" Win64="yes">
|
||||
<File Id="PeekFile_$(var.File)" Source="$(var.BinDir)modules\$(var.PeekProjectName)\$(var.File)" />
|
||||
</Component>
|
||||
<?endforeach?>
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- Peek Assets -->
|
||||
<DirectoryRef Id="PeekAssetsInstallFolder" FileSource="$(var.BinDir)modules\$(var.PeekProjectName)\Assets">
|
||||
<?foreach File in $(var.PeekAssetsFiles)?>
|
||||
<Component Id="Peek_Assets_$(var.File)" Win64="yes">
|
||||
<File Id="PeekAssetsFile_$(var.File)" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Assets\$(var.File)" />
|
||||
</Component>
|
||||
<?endforeach?>
|
||||
<Component Id="Peek_Assets_Applist_Scale100" Win64="yes">
|
||||
<File Id="Peek_AppList_Scale100" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Assets\AppList.scale-100.png" />
|
||||
</Component>
|
||||
<Component Id="Peek_Assets_Applist_Scale125" Win64="yes">
|
||||
<File Id="Peek_AppList_Scale125" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Assets\AppList.scale-125.png" />
|
||||
</Component>
|
||||
<Component Id="Peek_Assets_Applist_Scale150" Win64="yes">
|
||||
<File Id="Peek_AppList_Scale150" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Assets\AppList.scale-150.png" />
|
||||
</Component>
|
||||
<Component Id="Peek_Assets_Applist_Scale200" Win64="yes">
|
||||
<File Id="Peek_AppList_Scale200" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Assets\AppList.scale-200.png" />
|
||||
</Component>
|
||||
<Component Id="Peek_Assets_Applist_Scale400" Win64="yes">
|
||||
<File Id="Peek_AppList_Scale400" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Assets\AppList.scale-400.png" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<DirectoryRef Id="PeekMicrosoftUIXamlAssetsInstallFolder" FileSource="$(var.BinDir)modules\$(var.PeekProjectName)\Microsoft.UI.Xaml\Assets">
|
||||
<?foreach File in $(var.PeekMicrosoftUIXamlAssetsInstallFiles)?>
|
||||
<Component Id="PeekMicrosoftUIXamlAssets_$(var.File)" Win64="yes">
|
||||
<File Id="PeekMicrosoftUIXamlAssetsFile_$(var.File)" Source="$(var.BinDir)modules\$(var.PeekProjectName)\Microsoft.UI.Xaml\Assets\$(var.File)" />
|
||||
</Component>
|
||||
<?endforeach?>
|
||||
</DirectoryRef>
|
||||
|
||||
<!-- KBM -->
|
||||
<DirectoryRef Id="KeyboardManagerInstallFolder" FileSource="$(var.BinDir)modules\$(var.KeyboardManagerProjectName)\">
|
||||
<Component Id="Module_KeyboardManager" Win64="yes">
|
||||
<File Source="$(var.BinDir)modules\$(var.KeyboardManagerProjectName)\PowerToys.KeyboardManager.dll" />
|
||||
@@ -1212,7 +1272,7 @@
|
||||
<?endforeach?>
|
||||
</DirectoryRef>
|
||||
|
||||
<DirectoryRef Id="HostsMicrosoftUIXamlAssetsInstallFolder" FileSource="$(var.BinDir)modules\$(var.HostsProjectName)\Microsoft.UI.Xaml\Assets">
|
||||
<DirectoryRef Id="HostsMicrosoftUIXamlAssetsInstallFolder" FileSource="$(var.BinDir)modules\$(var.HostsProjectName)\Microsoft.UI.Xaml\Assets">
|
||||
<?foreach File in $(var.HostsMicrosoftUIXamlAssetsInstallFiles)?>
|
||||
<Component Id="HostsMicrosoftUIXamlAssets_$(var.File)" Win64="yes">
|
||||
<File Id="HostsMicrosoftUIXamlAssetsFile_$(var.File)" Source="$(var.BinDir)modules\$(var.HostsProjectName)\Microsoft.UI.Xaml\Assets\$(var.File)" />
|
||||
@@ -1360,6 +1420,21 @@
|
||||
<ComponentRef Id="FileLocksmith_Assets_Applist_Scale150" />
|
||||
<ComponentRef Id="FileLocksmith_Assets_Applist_Scale200" />
|
||||
<ComponentRef Id="FileLocksmith_Assets_Applist_Scale400" />
|
||||
<ComponentRef Id="Module_Peek" />
|
||||
<?foreach File in $(var.PeekFiles)?>
|
||||
<ComponentRef Id="Peek_$(var.File)" />
|
||||
<?endforeach?>
|
||||
<?foreach File in $(var.PeekAssetsFiles)?>
|
||||
<ComponentRef Id="Peek_Assets_$(var.File)" />
|
||||
<?endforeach?>
|
||||
<?foreach File in $(var.PeekMicrosoftUIXamlAssetsInstallFiles)?>
|
||||
<ComponentRef Id="PeekMicrosoftUIXamlAssets_$(var.File)" />
|
||||
<?endforeach?>
|
||||
<ComponentRef Id="Peek_Assets_Applist_Scale100" />
|
||||
<ComponentRef Id="Peek_Assets_Applist_Scale125" />
|
||||
<ComponentRef Id="Peek_Assets_Applist_Scale150" />
|
||||
<ComponentRef Id="Peek_Assets_Applist_Scale200" />
|
||||
<ComponentRef Id="Peek_Assets_Applist_Scale400" />
|
||||
<?foreach File in $(var.PowerAccentFiles)?>
|
||||
<ComponentRef Id="PowerAccent_$(var.File)" />
|
||||
<?endforeach?>
|
||||
@@ -1769,7 +1844,7 @@
|
||||
<!-- Localization languages shipped with WinAppSDK. We should ship these as well. -->
|
||||
<?define WinAppSDKLocLanguageList = af-ZA;ar-SA;az-Latn-AZ;bg-BG;bs-Latn-BA;ca-ES;cs-CZ;cy-GB;da-DK;de-DE;el-GR;en-GB;en-us;es-ES;es-MX;et-EE;eu-ES;fa-IR;fi-FI;fr-CA;fr-FR;gl-ES;he-IL;hi-IN;hr-HR;hu-HU;id-ID;is-IS;it-IT;ja-JP;ka-GE;kk-KZ;ko-KR;lt-LT;lv-LV;ms-MY;nb-NO;nl-NL;nn-NO;pl-PL;pt-BR;pt-PT;ro-RO;ru-RU;sk-SK;sl-SI;sq-AL;sr-Cyrl-RS;sr-Latn-RS;sv-SE;th-TH;tr-TR;uk-UA;vi-VN;zh-CN;zh-TW?>
|
||||
<Fragment>
|
||||
<?foreach ParentDirectory in SettingsV2InstallFolder;PowerRenameInstallFolder;MeasureToolInstallFolder;HostsInstallFolder;FileLocksmithInstallFolder?>
|
||||
<?foreach ParentDirectory in SettingsV2InstallFolder;PowerRenameInstallFolder;MeasureToolInstallFolder;HostsInstallFolder;FileLocksmithInstallFolder;PeekInstallFolder?>
|
||||
<DirectoryRef Id="$(var.ParentDirectory)">
|
||||
<?foreach Language in $(var.WinAppSDKLocLanguageList)?>
|
||||
<?if $(var.Language) = af-ZA?>
|
||||
@@ -2102,9 +2177,16 @@
|
||||
<File Id="FileLocksmith_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.FileLocksmithProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
|
||||
<File Id="FileLocksmith_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.FileLocksmithProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
|
||||
</Component>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?undef CompGUIDPrefix?>
|
||||
<?endforeach?>
|
||||
<Component
|
||||
Id="Peek_WinAppSDKLoc_$(var.IdSafeLanguage)_Component"
|
||||
Directory="WinAppSDKLoc$(var.IdSafeLanguage)PeekInstallFolder"
|
||||
Guid="$(var.CompGUIDPrefix)06">
|
||||
<File Id="Peek_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlMui_File" Source="$(var.BinDir)modules\$(var.PeekProjectName)\$(var.Language)\Microsoft.ui.xaml.dll.mui" />
|
||||
<File Id="Peek_WinAppSDKLoc_$(var.IdSafeLanguage)_XamlPhoneMui_File" Source="$(var.BinDir)modules\$(var.PeekProjectName)\$(var.Language)\Microsoft.UI.Xaml.Phone.dll.mui" />
|
||||
</Component>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?undef CompGUIDPrefix?>
|
||||
<?endforeach?>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
<Fragment>
|
||||
|
||||
@@ -1004,6 +1004,7 @@ const std::wstring WinAppSDKConsumers[] =
|
||||
L"modules\\MeasureTool",
|
||||
L"modules\\FileLocksmith",
|
||||
L"modules\\Hosts",
|
||||
L"modules\\Peek",
|
||||
};
|
||||
|
||||
UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
|
||||
@@ -1055,6 +1056,7 @@ const std::wstring PTInteropConsumers[] =
|
||||
L"modules\\PowerAccent",
|
||||
L"modules\\FileLocksmith",
|
||||
L"modules\\Hosts",
|
||||
L"modules\\Peek",
|
||||
L"modules\\FileExplorerPreview",
|
||||
};
|
||||
|
||||
@@ -1099,7 +1101,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
std::wstring installationFolder, dotnetRuntimeFilesSrcDir, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
||||
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir;
|
||||
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, hostsDir, fileLocksmithDir, peekDir;
|
||||
|
||||
hr = WcaInitialize(hInstall, "CreateDotnetRuntimeHardlinksCA");
|
||||
ExitOnFailure(hr, "Failed to initialize");
|
||||
@@ -1120,6 +1122,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
||||
fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\";
|
||||
hostsDir = installationFolder + L"modules\\Hosts\\";
|
||||
fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\";
|
||||
peekDir = installationFolder + L"modules\\Peek\\";
|
||||
|
||||
for (auto file : dotnetRuntimeFiles)
|
||||
{
|
||||
@@ -1136,6 +1139,7 @@ UINT __stdcall CreateDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
||||
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileExplorerAddOnsDir + file).c_str(), ec);
|
||||
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (hostsDir + file).c_str(), ec);
|
||||
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (fileLocksmithDir + file).c_str(), ec);
|
||||
std::filesystem::create_hard_link((dotnetRuntimeFilesSrcDir + file).c_str(), (peekDir + file).c_str(), ec);
|
||||
|
||||
if (ec.value() != S_OK)
|
||||
{
|
||||
@@ -1252,7 +1256,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
std::wstring installationFolder, colorPickerDir, powerOCRDir, launcherDir, fancyZonesDir,
|
||||
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir,
|
||||
imageResizerDir, settingsDir, awakeDir, measureToolDir, powerAccentDir, fileExplorerAddOnsDir, peekDir,
|
||||
hostsDir, fileLocksmithDir;
|
||||
|
||||
hr = WcaInitialize(hInstall, "DeleteDotnetRuntimeHardlinksCA");
|
||||
@@ -1273,6 +1277,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
||||
fileExplorerAddOnsDir = installationFolder + L"modules\\FileExplorerPreview\\";
|
||||
hostsDir = installationFolder + L"modules\\Hosts\\";
|
||||
fileLocksmithDir = installationFolder + L"modules\\FileLocksmith\\";
|
||||
peekDir = installationFolder + L"modules\\Peek\\";
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1290,6 +1295,7 @@ UINT __stdcall DeleteDotnetRuntimeHardlinksCA(MSIHANDLE hInstall)
|
||||
DeleteFile((fileExplorerAddOnsDir + file).c_str());
|
||||
DeleteFile((hostsDir + file).c_str());
|
||||
DeleteFile((fileLocksmithDir + file).c_str());
|
||||
DeleteFile((peekDir + file).c_str());
|
||||
}
|
||||
|
||||
for (auto file : dotnetRuntimeWPFFiles)
|
||||
@@ -1336,7 +1342,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
}
|
||||
processes.resize(bytes / sizeof(processes[0]));
|
||||
|
||||
std::array<std::wstring_view, 9> processesToTerminate = {
|
||||
std::array<std::wstring_view, 10> processesToTerminate = {
|
||||
L"PowerToys.PowerLauncher.exe",
|
||||
L"PowerToys.Settings.exe",
|
||||
L"PowerToys.Awake.exe",
|
||||
@@ -1345,6 +1351,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
|
||||
L"PowerToys.FileLocksmithUI.exe",
|
||||
L"PowerToys.ColorPickerUI.exe",
|
||||
L"PowerToys.AlwaysOnTop.exe",
|
||||
L"PowerToys.Peek.UI.exe",
|
||||
L"PowerToys.exe"
|
||||
};
|
||||
|
||||
|
||||
@@ -203,6 +203,10 @@ public
|
||||
return gcnew String(CommonSharedConstants::AWAKE_EXIT_EVENT);
|
||||
}
|
||||
|
||||
static String^ ShowPeekEvent() {
|
||||
return gcnew String(CommonSharedConstants::SHOW_PEEK_SHARED_EVENT);
|
||||
}
|
||||
|
||||
static String ^ PowerAccentExitEvent() {
|
||||
return gcnew String(CommonSharedConstants::POWERACCENT_EXIT_EVENT);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,9 @@ namespace CommonSharedConstants
|
||||
// Path to the event used by MarkdownPreviewHandler
|
||||
const wchar_t SVG_PREVIEW_RESIZE_EVENT[] = L"Local\\PowerToysSvgPreviewResizeEvent-0701a4fc-d5a1-4ee7-b885-f83982c62a0d";
|
||||
|
||||
// Path to the event used to show Peek
|
||||
const wchar_t SHOW_PEEK_SHARED_EVENT[] = L"Local\\ShowPeekEvent";
|
||||
|
||||
// Max DWORD for key code to disable keys.
|
||||
const DWORD VK_DISABLED = 0x100;
|
||||
}
|
||||
|
||||
14
src/modules/peek/Peek.Common/Converters/BoolConverter.cs
Normal file
14
src/modules/peek/Peek.Common/Converters/BoolConverter.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Peek.Common.Converters
|
||||
{
|
||||
public static class BoolConverter
|
||||
{
|
||||
public static bool Invert(bool value)
|
||||
{
|
||||
return !value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace Peek.Common.Extensions
|
||||
{
|
||||
public static class DispatcherExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Run work on UI thread safely.
|
||||
/// </summary>
|
||||
/// <returns>True if the work was run successfully, False otherwise.</returns>
|
||||
public static Task RunOnUiThread(this DispatcherQueue dispatcher, Func<Task> work)
|
||||
{
|
||||
var tcs = new TaskCompletionSource();
|
||||
dispatcher.TryEnqueue(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await work();
|
||||
|
||||
tcs.SetResult();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.SetException(e);
|
||||
}
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.Common.Extensions
|
||||
{
|
||||
public static class IPropertyStoreExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper method that retrieves a uint value from the given property store.
|
||||
/// Returns 0 if the value is not a VT_UI4 (4-byte unsigned integer in little-endian order).
|
||||
/// </summary>
|
||||
/// <param name="propertyStore">The property store</param>
|
||||
/// <param name="key">The key</param>
|
||||
/// <returns>The uint value</returns>
|
||||
public static uint GetUInt(this IPropertyStore propertyStore, PropertyKey key)
|
||||
{
|
||||
if (propertyStore == null)
|
||||
{
|
||||
throw new ArgumentNullException("propertyStore");
|
||||
}
|
||||
|
||||
PropVariant propVar;
|
||||
|
||||
propertyStore.GetValue(ref key, out propVar);
|
||||
|
||||
// VT_UI4 Indicates a 4-byte unsigned integer formatted in little-endian byte order.
|
||||
if ((VarEnum)propVar.Vt == VarEnum.VT_UI4)
|
||||
{
|
||||
return propVar.UlVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that retrieves a ulong value from the given property store.
|
||||
/// Returns 0 if the value is not a VT_UI8 (8-byte unsigned integer in little-endian order).
|
||||
/// </summary>
|
||||
/// <param name="propertyStore">The property store</param>
|
||||
/// <param name="key">the pkey</param>
|
||||
/// <returns>the ulong value</returns>
|
||||
public static ulong GetULong(this IPropertyStore propertyStore, PropertyKey key)
|
||||
{
|
||||
if (propertyStore == null)
|
||||
{
|
||||
throw new ArgumentNullException("propertyStore");
|
||||
}
|
||||
|
||||
PropVariant propVar;
|
||||
|
||||
propertyStore.GetValue(ref key, out propVar);
|
||||
|
||||
// VT_UI8 Indicates an 8-byte unsigned integer formatted in little-endian byte order.
|
||||
if ((VarEnum)propVar.Vt == VarEnum.VT_UI8)
|
||||
{
|
||||
return propVar.UhVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that retrieves a string value from the given property store.
|
||||
/// </summary>
|
||||
/// <param name="propertyStore">The property store</param>
|
||||
/// <param name="key">The pkey</param>
|
||||
/// <returns>The string value</returns>
|
||||
public static string GetString(this IPropertyStore propertyStore, PropertyKey key)
|
||||
{
|
||||
PropVariant propVar;
|
||||
|
||||
propertyStore.GetValue(ref key, out propVar);
|
||||
|
||||
if ((VarEnum)propVar.Vt == VarEnum.VT_LPWSTR)
|
||||
{
|
||||
return Marshal.PtrToStringUni(propVar.P) ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that retrieves an array of string values from the given property store.
|
||||
/// </summary>
|
||||
/// <param name="propertyStore">The property store</param>
|
||||
/// <param name="key">The pkey</param>
|
||||
/// <returns>The array of string values</returns>
|
||||
public static string[] GetStringArray(this IPropertyStore propertyStore, PropertyKey key)
|
||||
{
|
||||
PropVariant propVar;
|
||||
propertyStore.GetValue(ref key, out propVar);
|
||||
|
||||
List<string> values = new List<string>();
|
||||
|
||||
if ((VarEnum)propVar.Vt == (VarEnum.VT_LPWSTR | VarEnum.VT_VECTOR))
|
||||
{
|
||||
for (int elementIndex = 0; elementIndex < propVar.Calpwstr.CElems; elementIndex++)
|
||||
{
|
||||
var stringVal = Marshal.PtrToStringUni(Marshal.ReadIntPtr(propVar.Calpwstr.PElems, elementIndex));
|
||||
if (stringVal != null)
|
||||
{
|
||||
values.Add(stringVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/modules/peek/Peek.Common/Extensions/TaskExtension.cs
Normal file
32
src/modules/peek/Peek.Common/Extensions/TaskExtension.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Peek.Common.Extensions
|
||||
{
|
||||
public static class TaskExtension
|
||||
{
|
||||
public static Task<bool> RunSafe(Func<Task> work)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await work();
|
||||
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs
Normal file
64
src/modules/peek/Peek.Common/Helpers/PropertyStoreHelper.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Peek.Common.Models;
|
||||
using Windows.Win32.UI.Shell.PropertiesSystem;
|
||||
|
||||
namespace Peek.Common.Helpers
|
||||
{
|
||||
public static partial class PropertyStoreHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a IPropertyStore interface from the given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The file/folder path</param>
|
||||
/// <param name="flags">The property store flags</param>
|
||||
/// <returns>an IPropertyStroe interface</returns>
|
||||
public static IPropertyStore GetPropertyStoreFromPath(string path, GETPROPERTYSTOREFLAGS flags = GETPROPERTYSTOREFLAGS.GPS_EXTRINSICPROPERTIES)
|
||||
{
|
||||
IShellItem2? shellItem2 = null;
|
||||
IntPtr ppPropertyStore = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
SHCreateItemFromParsingName(path, IntPtr.Zero, typeof(IShellItem2).GUID, out shellItem2);
|
||||
|
||||
if (shellItem2 == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("Unable to get an IShellItem2 reference from file {0}.", path));
|
||||
}
|
||||
|
||||
int hr = shellItem2.GetPropertyStore((int)flags, typeof(IPropertyStore).GUID, out ppPropertyStore);
|
||||
|
||||
if (hr != 0)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format("GetPropertyStore retunred hresult={0}", hr));
|
||||
}
|
||||
|
||||
return (IPropertyStore)Marshal.GetObjectForIUnknown(ppPropertyStore);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (ppPropertyStore != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Release(ppPropertyStore);
|
||||
}
|
||||
|
||||
if (shellItem2 != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(shellItem2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
|
||||
private static extern void SHCreateItemFromParsingName(
|
||||
[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
|
||||
[In] IntPtr pbc,
|
||||
[In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,
|
||||
[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem2 ppv);
|
||||
}
|
||||
}
|
||||
57
src/modules/peek/Peek.Common/Helpers/ReadableStringHelper.cs
Normal file
57
src/modules/peek/Peek.Common/Helpers/ReadableStringHelper.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Peek.Common.Helpers
|
||||
{
|
||||
public static class ReadableStringHelper
|
||||
{
|
||||
private const int DecimalPercision = 10;
|
||||
|
||||
public static string BytesToReadableString(ulong bytes)
|
||||
{
|
||||
var resourceLoader = ResourceLoader.GetForViewIndependentUse();
|
||||
List<string> format = new List<string>
|
||||
{
|
||||
resourceLoader.GetString("ReadableString_ByteAbbreviationFormat"), // "B"
|
||||
resourceLoader.GetString("ReadableString_KiloByteAbbreviationFormat"), // "KB"
|
||||
resourceLoader.GetString("ReadableString_MegaByteAbbreviationFormat"), // "MB"
|
||||
resourceLoader.GetString("ReadableString_GigaByteAbbreviationFormat"), // "GB"
|
||||
resourceLoader.GetString("ReadableString_TeraByteAbbreviationFormat"), // "TB"
|
||||
resourceLoader.GetString("ReadableString_PetaByteAbbreviationFormat"), // "PB"
|
||||
resourceLoader.GetString("ReadableString_ExaByteAbbreviationFormat"), // "EB"
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
double number = 0.0;
|
||||
|
||||
if (bytes > 0)
|
||||
{
|
||||
index = (int)Math.Floor(Math.Log(bytes) / Math.Log(1024));
|
||||
number = Math.Round((bytes / Math.Pow(1024, index)) * DecimalPercision) / DecimalPercision;
|
||||
}
|
||||
|
||||
return string.Format(format[index], number);
|
||||
}
|
||||
|
||||
public static string FormatResourceString(string resourceId, object? args)
|
||||
{
|
||||
var formatString = ResourceLoader.GetForViewIndependentUse()?.GetString(resourceId);
|
||||
var formattedString = string.IsNullOrEmpty(formatString) ? string.Empty : string.Format(formatString, args);
|
||||
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
public static string FormatResourceString(string resourceId, object? args0, object? args1)
|
||||
{
|
||||
var formatString = ResourceLoader.GetForViewIndependentUse()?.GetString(resourceId);
|
||||
var formattedString = string.IsNullOrEmpty(formatString) ? string.Empty : string.Format(formatString, args0, args1);
|
||||
|
||||
return formattedString;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/modules/peek/Peek.Common/Models/Blob.cs
Normal file
16
src/modules/peek/Peek.Common/Models/Blob.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Blob
|
||||
{
|
||||
public int CbSize;
|
||||
public IntPtr PBlobData;
|
||||
}
|
||||
}
|
||||
16
src/modules/peek/Peek.Common/Models/CALPWSTR.cs
Normal file
16
src/modules/peek/Peek.Common/Models/CALPWSTR.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CALPWSTR
|
||||
{
|
||||
public uint CElems;
|
||||
public IntPtr PElems;
|
||||
}
|
||||
}
|
||||
40
src/modules/peek/Peek.Common/Models/File.cs
Normal file
40
src/modules/peek/Peek.Common/Models/File.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
// 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.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
#nullable enable
|
||||
|
||||
public class File
|
||||
{
|
||||
private StorageFile? storageFile;
|
||||
|
||||
public File(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public string Path { get; init; }
|
||||
|
||||
public string FileName => System.IO.Path.GetFileName(Path);
|
||||
|
||||
public string Extension => System.IO.Path.GetExtension(Path).ToLower();
|
||||
|
||||
public DateTime DateModified => System.IO.File.GetCreationTime(Path);
|
||||
|
||||
public async Task<StorageFile> GetStorageFileAsync()
|
||||
{
|
||||
if (storageFile == null)
|
||||
{
|
||||
storageFile = await StorageFile.GetFileFromPathAsync(Path);
|
||||
}
|
||||
|
||||
return storageFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/modules/peek/Peek.Common/Models/FileTime.cs
Normal file
15
src/modules/peek/Peek.Common/Models/FileTime.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct FileTime
|
||||
{
|
||||
public int DWHighDateTime;
|
||||
public int DWLowDateTime;
|
||||
}
|
||||
}
|
||||
24
src/modules/peek/Peek.Common/Models/HResult.cs
Normal file
24
src/modules/peek/Peek.Common/Models/HResult.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
public enum HResult
|
||||
{
|
||||
Ok = 0x0000,
|
||||
False = 0x0001,
|
||||
InvalidArguments = unchecked((int)0x80070057),
|
||||
OutOfMemory = unchecked((int)0x8007000E),
|
||||
NoInterface = unchecked((int)0x80004002),
|
||||
Fail = unchecked((int)0x80004005),
|
||||
ExtractionFailed = unchecked((int)0x8004B200),
|
||||
ElementNotFound = unchecked((int)0x80070490),
|
||||
TypeElementNotFound = unchecked((int)0x8002802B),
|
||||
NoObject = unchecked((int)0x800401E5),
|
||||
Win32ErrorCanceled = 1223,
|
||||
Canceled = unchecked((int)0x800704C7),
|
||||
ResourceInUse = unchecked((int)0x800700AA),
|
||||
AccessDenied = unchecked((int)0x80030005),
|
||||
}
|
||||
}
|
||||
27
src/modules/peek/Peek.Common/Models/IEnumIDList.cs
Normal file
27
src/modules/peek/Peek.Common/Models/IEnumIDList.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("000214F2-0000-0000-C000-000000000046")]
|
||||
public interface IEnumIDList
|
||||
{
|
||||
[PreserveSig]
|
||||
int Next(int celt, out IntPtr rgelt, out int pceltFetched);
|
||||
|
||||
[PreserveSig]
|
||||
int Skip(int celt);
|
||||
|
||||
[PreserveSig]
|
||||
int Reset();
|
||||
|
||||
[PreserveSig]
|
||||
int Clone(out IEnumIDList ppenum);
|
||||
}
|
||||
}
|
||||
46
src/modules/peek/Peek.Common/Models/IFolderView.cs
Normal file
46
src/modules/peek/Peek.Common/Models/IFolderView.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("cde725b0-ccc9-4519-917e-325d72fab4ce")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
public interface IFolderView
|
||||
{
|
||||
void GetCurrentViewMode([Out] out uint pViewMode);
|
||||
|
||||
void SetCurrentViewMode([In] uint viewMode);
|
||||
|
||||
void GetFolder([In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppv);
|
||||
|
||||
void Item([In] int iItemIndex, [Out] out IntPtr ppidl);
|
||||
|
||||
void ItemCount([In] uint uFlags, [Out] out int pcItems);
|
||||
|
||||
void Items([In] uint uFlags, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppv);
|
||||
|
||||
void GetSelectionMarkedItem([Out] out int piItem);
|
||||
|
||||
void GetFocusedItem([Out] out int piItem);
|
||||
|
||||
void GetItemPosition([In] IntPtr pidl, [Out] out Point ppt);
|
||||
|
||||
void GetSpacing([In, Out] ref Point ppt);
|
||||
|
||||
void GetDefaultSpacing([Out] out Point ppt);
|
||||
|
||||
void GetAutoArrange();
|
||||
|
||||
void SelectItem([In] int iItem, [In] uint dwFlags);
|
||||
|
||||
void SelectAndPositionItems([In] uint cidl, [In] IntPtr apidl, [In] IntPtr apt, [In] uint dwFlags);
|
||||
}
|
||||
}
|
||||
26
src/modules/peek/Peek.Common/Models/IPropertyStore.cs
Normal file
26
src/modules/peek/Peek.Common/Models/IPropertyStore.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using static Peek.Common.Helpers.PropertyStoreHelper;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IPropertyStore
|
||||
{
|
||||
void GetCount(out uint propertyCount);
|
||||
|
||||
void GetAt(uint iProp, out PropertyKey pkey);
|
||||
|
||||
void GetValue(ref PropertyKey key, out PropVariant pv);
|
||||
|
||||
void SetValue(ref PropertyKey key, ref PropVariant pv);
|
||||
|
||||
void Commit();
|
||||
}
|
||||
}
|
||||
17
src/modules/peek/Peek.Common/Models/IServiceProvider.cs
Normal file
17
src/modules/peek/Peek.Common/Models/IServiceProvider.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IServiceProvider
|
||||
{
|
||||
[return: MarshalAs(UnmanagedType.IUnknown)]
|
||||
object QueryService([MarshalAs(UnmanagedType.LPStruct)] Guid service, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
|
||||
}
|
||||
}
|
||||
46
src/modules/peek/Peek.Common/Models/IShellBrowser.cs
Normal file
46
src/modules/peek/Peek.Common/Models/IShellBrowser.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("000214E2-0000-0000-C000-000000000046")]
|
||||
public interface IShellBrowser
|
||||
{
|
||||
void GetWindow(out IntPtr phwnd);
|
||||
|
||||
void ContextSensitiveHelp(bool fEnterMode);
|
||||
|
||||
void InsertMenusSB(IntPtr hmenuShared, IntPtr lpMenuWidths);
|
||||
|
||||
void SetMenuSB(IntPtr hmenuShared, IntPtr holemenuRes, IntPtr hwndActiveObject);
|
||||
|
||||
void RemoveMenusSB(IntPtr hmenuShared);
|
||||
|
||||
void SetStatusTextSB(IntPtr pszStatusText);
|
||||
|
||||
void EnableModelessSB(bool fEnable);
|
||||
|
||||
void TranslateAcceleratorSB(IntPtr pmsg, ushort wID);
|
||||
|
||||
void BrowseObject(IntPtr pidl, uint wFlags);
|
||||
|
||||
void GetViewStateStream(uint grfMode, IntPtr ppStrm);
|
||||
|
||||
void GetControlWindow(uint id, out IntPtr lpIntPtr);
|
||||
|
||||
void SendControlMsg(uint id, uint uMsg, uint wParam, uint lParam, IntPtr pret);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.IUnknown)]
|
||||
object QueryActiveShellView();
|
||||
|
||||
void OnViewWindowActive(IShellView ppshv);
|
||||
|
||||
void SetToolbarItems(IntPtr lpButtons, uint nButtons, uint uFlags);
|
||||
}
|
||||
}
|
||||
67
src/modules/peek/Peek.Common/Models/IShellFolder2.cs
Normal file
67
src/modules/peek/Peek.Common/Models/IShellFolder2.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Windows.Win32.UI.Shell;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("93F2F68C-1D1B-11D3-A30E-00C04F79ABD1")]
|
||||
public interface IShellFolder2
|
||||
{
|
||||
[PreserveSig]
|
||||
int ParseDisplayName(IntPtr hwnd, IntPtr pbc, [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, ref int pchEaten, out IntPtr ppidl, ref int pdwAttributes);
|
||||
|
||||
[PreserveSig]
|
||||
int EnumObjects(IntPtr hwnd, _SHCONTF grfFlags, out IntPtr enumIDList);
|
||||
|
||||
[PreserveSig]
|
||||
int BindToObject(IntPtr pidl, IntPtr pbc, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int BindToStorage(IntPtr pidl, IntPtr pbc, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2);
|
||||
|
||||
[PreserveSig]
|
||||
int CreateViewObject(IntPtr hwndOwner, Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int GetAttributesOf(int cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, ref IntPtr rgfInOut);
|
||||
|
||||
[PreserveSig]
|
||||
int GetUIObjectOf(IntPtr hwndOwner, int cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, ref Guid riid, IntPtr rgfReserved, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDisplayNameOf(IntPtr pidl, SHGDNF uFlags, out Strret lpName);
|
||||
|
||||
[PreserveSig]
|
||||
int SetNameOf(IntPtr hwnd, IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] string pszName, int uFlags, out IntPtr ppidlOut);
|
||||
|
||||
[PreserveSig]
|
||||
int EnumSearches(out IntPtr ppenum);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDefaultColumn(int dwReserved, ref IntPtr pSort, out IntPtr pDisplay);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDefaultColumnState(int iColumn, out IntPtr pcsFlags);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDefaultSearchGUID(out IntPtr guid);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDetailsEx(IntPtr pidl, IntPtr pscid, out IntPtr pv);
|
||||
|
||||
[PreserveSig]
|
||||
int GetDetailsOf(IntPtr pidl, int iColumn, ref SHELLDETAILS psd);
|
||||
|
||||
[PreserveSig]
|
||||
int MapColumnToSCID(int icolumn, IntPtr pscid);
|
||||
}
|
||||
}
|
||||
31
src/modules/peek/Peek.Common/Models/IShellItem.cs
Normal file
31
src/modules/peek/Peek.Common/Models/IShellItem.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Windows.Win32.UI.Shell;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
|
||||
public interface IShellItem
|
||||
{
|
||||
void BindToHandler(
|
||||
IntPtr pbc,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
|
||||
out IntPtr ppv);
|
||||
|
||||
void GetParent(out IShellItem ppsi);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.LPWStr)]
|
||||
string GetDisplayName(SIGDN sigdnName);
|
||||
|
||||
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
|
||||
|
||||
void Compare(IShellItem psi, uint hint, out int piOrder);
|
||||
}
|
||||
}
|
||||
71
src/modules/peek/Peek.Common/Models/IShellItem2.cs
Normal file
71
src/modules/peek/Peek.Common/Models/IShellItem2.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.UI.Shell.PropertiesSystem;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("7E9FB0D3-919F-4307-AB2E-9B1860310C93")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IShellItem2 : IShellItem
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
public void BindToHandler(IntPtr pbc, [In] ref Guid bhid, [In] ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
new void GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
public int GetDisplayName([In] int sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
public void GetAttributes([In] int sfgaoMask, out int psfgaoAttribs);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
new void Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetPropertyStore(int flags, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
internal int GetPropertyStoreWithCreateObject(ref GETPROPERTYSTOREFLAGS flags, ref IntPtr punkFactory, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
internal int GetPropertyStoreForKeys(ref PropertyKey keys, uint cKeys, ref GETPROPERTYSTOREFLAGS flags, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetPropertyDescriptionList(ref PropertyKey key, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[PreserveSig]
|
||||
public int Update(ref IntPtr pbc);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetProperty(ref PropertyKey key, out PropVariant pPropVar);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetCLSID(ref PropertyKey key, out Guid clsid);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetFileTime(ref PropertyKey key, out FileTime pft);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetInt32(ref PropertyKey key, out int pi);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetString(ref PropertyKey key, [MarshalAs(UnmanagedType.LPWStr)] string ppsz);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetUint32(ref PropertyKey key, out uint pui);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetUint64(ref PropertyKey key, out uint pull);
|
||||
|
||||
[PreserveSig]
|
||||
public int GetBool(ref PropertyKey key, bool pf);
|
||||
}
|
||||
}
|
||||
38
src/modules/peek/Peek.Common/Models/IShellItemArray.cs
Normal file
38
src/modules/peek/Peek.Common/Models/IShellItemArray.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.UI.Shell;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("B63EA76D-1F85-456F-A19C-48159EFA858B")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IShellItemArray
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid rbhid, [In] ref Guid riid, out IntPtr ppvOut);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void GetPropertyStore([In] int flags, [In] ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void GetPropertyDescriptionList([In] ref PropertyKey keyType, [In] ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void GetAttributes([In] SIATTRIBFLAGS dwAttribFlags, [In] uint sfgaoMask, out uint psfgaoAttribs);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
int GetCount();
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
Common.Models.IShellItem GetItemAt(int dwIndex);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
void EnumItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenumShellItems);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IShellItemImageFactory
|
||||
{
|
||||
[PreserveSig]
|
||||
HResult GetImage(
|
||||
[In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
|
||||
[In] ThumbnailOptions flags,
|
||||
[Out] out IntPtr phbm);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NativeSize
|
||||
{
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public int Width
|
||||
{
|
||||
set { width = value; }
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
set { height = value; }
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ThumbnailOptions
|
||||
{
|
||||
None = 0x00,
|
||||
BiggerSizeOk = 0x01,
|
||||
InMemoryOnly = 0x02,
|
||||
IconOnly = 0x04,
|
||||
ThumbnailOnly = 0x08,
|
||||
InCacheOnly = 0x10,
|
||||
ScaleUp = 0x100,
|
||||
}
|
||||
}
|
||||
18
src/modules/peek/Peek.Common/Models/IShellView.cs
Normal file
18
src/modules/peek/Peek.Common/Models/IShellView.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("000214E3-0000-0000-C000-000000000046")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
public interface IShellView
|
||||
{
|
||||
}
|
||||
}
|
||||
60
src/modules/peek/Peek.Common/Models/PropVariant.cs
Normal file
60
src/modules/peek/Peek.Common/Models/PropVariant.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct PropVariant
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public short Vt;
|
||||
[FieldOffset(2)]
|
||||
public short WReserved1;
|
||||
[FieldOffset(4)]
|
||||
public short WReserved2;
|
||||
[FieldOffset(6)]
|
||||
public short WReserved3;
|
||||
[FieldOffset(8)]
|
||||
public sbyte CVal;
|
||||
[FieldOffset(8)]
|
||||
public byte BVal;
|
||||
[FieldOffset(8)]
|
||||
public short IVal;
|
||||
[FieldOffset(8)]
|
||||
public ushort UiVal;
|
||||
[FieldOffset(8)]
|
||||
public int LVal;
|
||||
[FieldOffset(8)]
|
||||
public uint UlVal;
|
||||
[FieldOffset(8)]
|
||||
public int IntVal;
|
||||
[FieldOffset(8)]
|
||||
public uint UintVal;
|
||||
[FieldOffset(8)]
|
||||
public long HVal;
|
||||
[FieldOffset(8)]
|
||||
public ulong UhVal;
|
||||
[FieldOffset(8)]
|
||||
public float FltVal;
|
||||
[FieldOffset(8)]
|
||||
public double DblVal;
|
||||
[FieldOffset(8)]
|
||||
public bool BoolVal;
|
||||
[FieldOffset(8)]
|
||||
public int Scode;
|
||||
[FieldOffset(8)]
|
||||
public DateTime Date;
|
||||
[FieldOffset(8)]
|
||||
public FileTime Filetime;
|
||||
[FieldOffset(8)]
|
||||
public Blob Blob;
|
||||
[FieldOffset(8)]
|
||||
public IntPtr P;
|
||||
[FieldOffset(8)]
|
||||
public CALPWSTR Calpwstr;
|
||||
}
|
||||
}
|
||||
65
src/modules/peek/Peek.Common/Models/PropertyKey.cs
Normal file
65
src/modules/peek/Peek.Common/Models/PropertyKey.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct PropertyKey
|
||||
{
|
||||
public Guid FormatId;
|
||||
public int PropertyId;
|
||||
|
||||
public PropertyKey(Guid guid, int propertyId)
|
||||
{
|
||||
this.FormatId = guid;
|
||||
this.PropertyId = propertyId;
|
||||
}
|
||||
|
||||
public PropertyKey(uint a, uint b, uint c, uint d, uint e, uint f, uint g, uint h, uint i, uint j, uint k, int propertyId)
|
||||
: this(new Guid((uint)a, (ushort)b, (ushort)c, (byte)d, (byte)e, (byte)f, (byte)g, (byte)h, (byte)i, (byte)j, (byte)k), propertyId)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if ((obj == null) || !(obj is PropertyKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyKey pk = (PropertyKey)obj;
|
||||
|
||||
return FormatId.Equals(pk.FormatId) && (PropertyId == pk.PropertyId);
|
||||
}
|
||||
|
||||
public static bool operator ==(PropertyKey a, PropertyKey b)
|
||||
{
|
||||
if (((object)a == null) || ((object)b == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.FormatId == b.FormatId && a.PropertyId == b.PropertyId;
|
||||
}
|
||||
|
||||
public static bool operator !=(PropertyKey a, PropertyKey b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return FormatId.GetHashCode() ^ PropertyId;
|
||||
}
|
||||
|
||||
// File properties: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wsp/2dbe759c-c955-4770-a545-e46d7f6332ed
|
||||
public static readonly PropertyKey ImageHorizontalSize = new PropertyKey(new Guid(0x6444048F, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03), 3);
|
||||
public static readonly PropertyKey ImageVerticalSize = new PropertyKey(new Guid(0x6444048F, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03), 4);
|
||||
public static readonly PropertyKey FileSizeBytes = new PropertyKey(new Guid(0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac), 12);
|
||||
public static readonly PropertyKey FileType = new PropertyKey(new Guid(0xd5cdd502, 0x2e9c, 0x101b, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae), 26);
|
||||
}
|
||||
}
|
||||
17
src/modules/peek/Peek.Common/Models/SHELLDETAILS.cs
Normal file
17
src/modules/peek/Peek.Common/Models/SHELLDETAILS.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using static Peek.Common.Helpers.PropertyStoreHelper;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
public struct SHELLDETAILS
|
||||
{
|
||||
public int Fmt;
|
||||
public int CxChar;
|
||||
public Strret Str;
|
||||
}
|
||||
}
|
||||
21
src/modules/peek/Peek.Common/Models/SHFILEINFO.cs
Normal file
21
src/modules/peek/Peek.Common/Models/SHFILEINFO.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct SHFILEINFO
|
||||
{
|
||||
public IntPtr HIcon;
|
||||
public int IIcon;
|
||||
public uint DwAttributes;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||
public string SzDisplayName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
|
||||
public string SzTypeName;
|
||||
}
|
||||
}
|
||||
30
src/modules/peek/Peek.Common/Models/Strret.cs
Normal file
30
src/modules/peek/Peek.Common/Models/Strret.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Peek.Common.Models
|
||||
{
|
||||
[SuppressMessage("Microsoft.Portability", "CA1900:ValueTypeFieldsShouldBePortable", Justification = "Targeting Windows (X86/AMD64/ARM) only")]
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct Strret
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int UType;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public IntPtr POleStr;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public IntPtr PStr;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public int UOffset;
|
||||
|
||||
[FieldOffset(4)]
|
||||
public IntPtr CStr;
|
||||
}
|
||||
}
|
||||
4
src/modules/peek/Peek.Common/NativeMethods.json
Normal file
4
src/modules/peek/Peek.Common/NativeMethods.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json",
|
||||
"public": true
|
||||
}
|
||||
5
src/modules/peek/Peek.Common/NativeMethods.txt
Normal file
5
src/modules/peek/Peek.Common/NativeMethods.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
GETPROPERTYSTOREFLAGS
|
||||
_SHCONTF
|
||||
SIGDN
|
||||
SHGDNF
|
||||
SIATTRIBFLAGS
|
||||
31
src/modules/peek/Peek.Common/Peek.Common.csproj
Normal file
31
src/modules/peek/Peek.Common/Peek.Common.csproj
Normal file
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>Peek.Common</RootNamespace>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>Peek_Win32</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Peek.Win32\Peek.Win32.vcxproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
|
||||
<!-- Licensed under the MIT License. See LICENSE in the project root for license information. -->
|
||||
|
||||
<UserControl
|
||||
x:Class="Peek.FilePreviewer.Controls.BrowserControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Peek.FilePreviewer.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<controls:WebView2 x:Name="PreviewBrowser"
|
||||
Loaded="PreviewWV2_Loaded"
|
||||
NavigationStarting="PreviewBrowser_NavigationStarting"
|
||||
NavigationCompleted="PreviewWV2_NavigationCompleted"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Windows.System;
|
||||
|
||||
namespace Peek.FilePreviewer.Controls
|
||||
{
|
||||
public sealed partial class BrowserControl : UserControl, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper private Uri where we cache the last navigated page
|
||||
/// so we can redirect internal PDF or Webpage links to external
|
||||
/// webbrowser, avoiding WebView internal navigation.
|
||||
/// </summary>
|
||||
private Uri? _navigatedUri;
|
||||
|
||||
public delegate void NavigationCompletedHandler(WebView2? sender, CoreWebView2NavigationCompletedEventArgs? args);
|
||||
|
||||
public delegate void DOMContentLoadedHandler(CoreWebView2? sender, CoreWebView2DOMContentLoadedEventArgs? args);
|
||||
|
||||
public event NavigationCompletedHandler? NavigationCompleted;
|
||||
|
||||
public event DOMContentLoadedHandler? DOMContentLoaded;
|
||||
|
||||
private string previewBrowserUserDataFolder = System.Environment.GetEnvironmentVariable("USERPROFILE") +
|
||||
"\\AppData\\LocalLow\\Microsoft\\PowerToys\\Peek-Temp";
|
||||
|
||||
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
|
||||
nameof(Source),
|
||||
typeof(Uri),
|
||||
typeof(BrowserControl),
|
||||
new PropertyMetadata(null, new PropertyChangedCallback((d, e) => ((BrowserControl)d).SourcePropertyChanged())));
|
||||
|
||||
public Uri? Source
|
||||
{
|
||||
get { return (Uri)GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
}
|
||||
|
||||
public BrowserControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", previewBrowserUserDataFolder, EnvironmentVariableTarget.Process);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (PreviewBrowser.CoreWebView2 != null)
|
||||
{
|
||||
PreviewBrowser.CoreWebView2.DOMContentLoaded -= CoreWebView2_DOMContentLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to the to the <see cref="Uri"/> set in <see cref="Source"/>.
|
||||
/// Calling <see cref="Navigate"/> will always trigger a navigation/refresh
|
||||
/// even if web target file is the same.
|
||||
/// </summary>
|
||||
public void Navigate()
|
||||
{
|
||||
var value = Environment.GetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS");
|
||||
|
||||
_navigatedUri = null;
|
||||
|
||||
if (Source != null)
|
||||
{
|
||||
/* CoreWebView2.Navigate() will always trigger a navigation even if the content/URI is the same.
|
||||
* Use WebView2.Source to avoid re-navigating to the same content. */
|
||||
PreviewBrowser.CoreWebView2.Navigate(Source.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void SourcePropertyChanged()
|
||||
{
|
||||
Navigate();
|
||||
}
|
||||
|
||||
private async void PreviewWV2_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await PreviewBrowser.EnsureCoreWebView2Async();
|
||||
|
||||
PreviewBrowser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.AreDevToolsEnabled = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.AreHostObjectsAllowed = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.IsScriptEnabled = false;
|
||||
PreviewBrowser.CoreWebView2.Settings.IsWebMessageEnabled = false;
|
||||
|
||||
PreviewBrowser.CoreWebView2.DOMContentLoaded += CoreWebView2_DOMContentLoaded;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// TODO: exception / telemetry log?
|
||||
}
|
||||
}
|
||||
|
||||
private void CoreWebView2_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args)
|
||||
{
|
||||
DOMContentLoaded?.Invoke(sender, args);
|
||||
}
|
||||
|
||||
private async void PreviewBrowser_NavigationStarting(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationStartingEventArgs args)
|
||||
{
|
||||
if (_navigatedUri == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// In case user starts or tries to navigate from within the HTML file we launch default web browser for navigation.
|
||||
if (args.Uri != null && args.Uri != _navigatedUri?.ToString() && args.IsUserInitiated)
|
||||
{
|
||||
args.Cancel = true;
|
||||
await Launcher.LaunchUriAsync(new Uri(args.Uri));
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewWV2_NavigationCompleted(WebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2NavigationCompletedEventArgs args)
|
||||
{
|
||||
if (args.IsSuccess)
|
||||
{
|
||||
_navigatedUri = Source;
|
||||
}
|
||||
|
||||
NavigationCompleted?.Invoke(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/modules/peek/Peek.FilePreviewer/FilePreview.xaml
Normal file
50
src/modules/peek/Peek.FilePreviewer/FilePreview.xaml
Normal file
@@ -0,0 +1,50 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<UserControl
|
||||
x:Class="Peek.FilePreviewer.FilePreview"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Peek.FilePreviewer.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Peek.FilePreviewer"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:previewers="using:Peek.FilePreviewer.Previewers"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<ProgressRing
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsActive="{x:Bind MatchPreviewState(Previewer.State, previewers:PreviewState.Loading), Mode=OneWay}" />
|
||||
|
||||
<Image
|
||||
x:Name="ImagePreview"
|
||||
Source="{x:Bind BitmapPreviewer.Preview, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind ImageInfoTooltip, Mode=OneWay}"
|
||||
Visibility="{x:Bind IsPreviewVisible(BitmapPreviewer, Previewer.State), Mode=OneWay}" />
|
||||
|
||||
<controls:BrowserControl
|
||||
x:Name="BrowserPreview"
|
||||
x:Load="True"
|
||||
DOMContentLoaded="BrowserPreview_DOMContentLoaded"
|
||||
NavigationCompleted="PreviewBrowser_NavigationCompleted"
|
||||
Source="{x:Bind BrowserPreviewer.Preview, Mode=OneWay}"
|
||||
Visibility="{x:Bind IsPreviewVisible(BrowserPreviewer, Previewer.State), Mode=OneWay, FallbackValue=Collapsed}" />
|
||||
|
||||
<local:UnsupportedFilePreview
|
||||
x:Name="UnsupportedFilePreview"
|
||||
DateModified="{x:Bind UnsupportedFilePreviewer.DateModified, Mode=OneWay}"
|
||||
FileName="{x:Bind UnsupportedFilePreviewer.FileName, Mode=OneWay}"
|
||||
FileSize="{x:Bind UnsupportedFilePreviewer.FileSize, Mode=OneWay}"
|
||||
FileType="{x:Bind UnsupportedFilePreviewer.FileType, Mode=OneWay}"
|
||||
IconPreview="{x:Bind UnsupportedFilePreviewer.IconPreview, Mode=OneWay}"
|
||||
Visibility="{x:Bind IsPreviewVisible(UnsupportedFilePreviewer, Previewer.State), Mode=OneWay}" />
|
||||
</Grid>
|
||||
<UserControl.KeyboardAccelerators>
|
||||
<KeyboardAccelerator
|
||||
Key="C"
|
||||
Invoked="KeyboardAccelerator_CtrlC_Invoked"
|
||||
Modifiers="Control" />
|
||||
</UserControl.KeyboardAccelerators>
|
||||
</UserControl>
|
||||
237
src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs
Normal file
237
src/modules/peek/Peek.FilePreviewer/FilePreview.xaml.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
// 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.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Peek.Common.Helpers;
|
||||
using Peek.Common.Models;
|
||||
using Peek.FilePreviewer.Models;
|
||||
using Peek.FilePreviewer.Previewers;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Peek.FilePreviewer
|
||||
{
|
||||
[INotifyPropertyChanged]
|
||||
public sealed partial class FilePreview : UserControl
|
||||
{
|
||||
private readonly PreviewerFactory previewerFactory = new();
|
||||
|
||||
public event EventHandler<PreviewSizeChangedArgs>? PreviewSizeChanged;
|
||||
|
||||
public static readonly DependencyProperty FilesProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(File),
|
||||
typeof(File),
|
||||
typeof(FilePreview),
|
||||
new PropertyMetadata(false, async (d, e) => await ((FilePreview)d).OnFilePropertyChanged()));
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(BitmapPreviewer))]
|
||||
[NotifyPropertyChangedFor(nameof(BrowserPreviewer))]
|
||||
[NotifyPropertyChangedFor(nameof(UnsupportedFilePreviewer))]
|
||||
|
||||
private IPreviewer? previewer;
|
||||
|
||||
[ObservableProperty]
|
||||
private string imageInfoTooltip = ResourceLoader.GetForViewIndependentUse().GetString("PreviewTooltip_Blank");
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource = new();
|
||||
|
||||
public FilePreview()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void Previewer_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
// Fallback on DefaultPreviewer if we fail to load the correct Preview
|
||||
if (e.PropertyName == nameof(IPreviewer.State))
|
||||
{
|
||||
if (Previewer?.State == PreviewState.Error)
|
||||
{
|
||||
// Cancel previous loading task
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource = new();
|
||||
|
||||
Previewer = previewerFactory.CreateDefaultPreviewer(File);
|
||||
await UpdatePreviewAsync(_cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IBitmapPreviewer? BitmapPreviewer => Previewer as IBitmapPreviewer;
|
||||
|
||||
public IBrowserPreviewer? BrowserPreviewer => Previewer as IBrowserPreviewer;
|
||||
|
||||
public bool IsImageVisible => BitmapPreviewer != null;
|
||||
|
||||
public IUnsupportedFilePreviewer? UnsupportedFilePreviewer => Previewer as IUnsupportedFilePreviewer;
|
||||
|
||||
public bool IsUnsupportedPreviewVisible => UnsupportedFilePreviewer != null;
|
||||
|
||||
public File File
|
||||
{
|
||||
get => (File)GetValue(FilesProperty);
|
||||
set => SetValue(FilesProperty, value);
|
||||
}
|
||||
|
||||
public bool MatchPreviewState(PreviewState? value, PreviewState stateToMatch)
|
||||
{
|
||||
return value == stateToMatch;
|
||||
}
|
||||
|
||||
public Visibility IsPreviewVisible(IPreviewer? previewer, PreviewState? state)
|
||||
{
|
||||
var isValidPreview = previewer != null && MatchPreviewState(state, PreviewState.Loaded);
|
||||
return isValidPreview ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private async Task OnFilePropertyChanged()
|
||||
{
|
||||
// Cancel previous loading task
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource = new();
|
||||
|
||||
// TODO: track and cancel existing async preview tasks
|
||||
// https://github.com/microsoft/PowerToys/issues/22480
|
||||
if (File == null)
|
||||
{
|
||||
Previewer = null;
|
||||
ImagePreview.Visibility = Visibility.Collapsed;
|
||||
BrowserPreview.Visibility = Visibility.Collapsed;
|
||||
UnsupportedFilePreview.Visibility = Visibility.Collapsed;
|
||||
return;
|
||||
}
|
||||
|
||||
Previewer = previewerFactory.Create(File);
|
||||
|
||||
await UpdatePreviewAsync(_cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
private async Task UpdatePreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (Previewer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var size = await Previewer.GetPreviewSizeAsync(cancellationToken);
|
||||
SizeFormat windowSizeFormat = UnsupportedFilePreviewer != null ? SizeFormat.Percentage : SizeFormat.Pixels;
|
||||
PreviewSizeChanged?.Invoke(this, new PreviewSizeChangedArgs(size, windowSizeFormat));
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Previewer.LoadPreviewAsync(cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await UpdateImageTooltipAsync(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// TODO: Log task cancelled exception?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnPreviewerChanging(IPreviewer? value)
|
||||
{
|
||||
if (Previewer != null)
|
||||
{
|
||||
Previewer.PropertyChanged -= Previewer_PropertyChanged;
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
value.PropertyChanged += Previewer_PropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void BrowserPreview_DOMContentLoaded(Microsoft.Web.WebView2.Core.CoreWebView2 sender, Microsoft.Web.WebView2.Core.CoreWebView2DOMContentLoadedEventArgs args)
|
||||
{
|
||||
/*
|
||||
* There is an odd behavior where the WebView2 would not raise the NavigationCompleted event
|
||||
* for certain HTML files, even though it has already been loaded. Probably related to certain
|
||||
* extra module that require more time to load. One example is saving and opening google.com locally.
|
||||
*
|
||||
* So to address this, we will make the Browser visible and display it as "Loaded" as soon the HTML document
|
||||
* has been parsed and loaded with the DOMContentLoaded event.
|
||||
*
|
||||
* Similar issue: https://github.com/MicrosoftEdge/WebView2Feedback/issues/998
|
||||
*/
|
||||
if (BrowserPreviewer != null)
|
||||
{
|
||||
BrowserPreviewer.State = PreviewState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewBrowser_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args)
|
||||
{
|
||||
/*
|
||||
* In theory most of navigation should work after DOM is loaded.
|
||||
* But in case something fails we check NavigationCompleted event
|
||||
* for failure and switch visibility accordingly.
|
||||
*
|
||||
* As an alternative, in the future, the preview Browser control
|
||||
* could also display error content.
|
||||
*/
|
||||
if (!args.IsSuccess)
|
||||
{
|
||||
if (BrowserPreviewer != null)
|
||||
{
|
||||
BrowserPreviewer.State = PreviewState.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void KeyboardAccelerator_CtrlC_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||
{
|
||||
if (Previewer != null)
|
||||
{
|
||||
await Previewer.CopyAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateImageTooltipAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (File == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch and format available file properties
|
||||
var sb = new StringBuilder();
|
||||
|
||||
string fileNameFormatted = ReadableStringHelper.FormatResourceString("PreviewTooltip_FileName", File.FileName);
|
||||
sb.Append(fileNameFormatted);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string fileType = await PropertyHelper.GetFileType(File.Path);
|
||||
string fileTypeFormatted = string.IsNullOrEmpty(fileType) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileType", fileType);
|
||||
sb.Append(fileTypeFormatted);
|
||||
|
||||
string dateModified = File.DateModified.ToString();
|
||||
string dateModifiedFormatted = string.IsNullOrEmpty(dateModified) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_DateModified", dateModified);
|
||||
sb.Append(dateModifiedFormatted);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Size dimensions = await PropertyHelper.GetImageSize(File.Path);
|
||||
string dimensionsFormatted = dimensions.IsEmpty ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_Dimensions", dimensions.Width, dimensions.Height);
|
||||
sb.Append(dimensionsFormatted);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ulong bytes = await PropertyHelper.GetFileSizeInBytes(File.Path);
|
||||
string fileSize = ReadableStringHelper.BytesToReadableString(bytes);
|
||||
string fileSizeFormatted = string.IsNullOrEmpty(fileSize) ? string.Empty : "\n" + ReadableStringHelper.FormatResourceString("PreviewTooltip_FileSize", fileSize);
|
||||
sb.Append(fileSizeFormatted);
|
||||
|
||||
ImageInfoTooltip = sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Peek.FilePreviewer.Models
|
||||
{
|
||||
public enum SizeFormat
|
||||
{
|
||||
Pixels,
|
||||
Percentage,
|
||||
}
|
||||
|
||||
public class PreviewSizeChangedArgs
|
||||
{
|
||||
public PreviewSizeChangedArgs(Size? windowSizeRequested, SizeFormat sizeFormat = SizeFormat.Pixels)
|
||||
{
|
||||
WindowSizeRequested = windowSizeRequested;
|
||||
WindowSizeFormat = sizeFormat;
|
||||
}
|
||||
|
||||
public Size? WindowSizeRequested { get; init; }
|
||||
|
||||
public SizeFormat WindowSizeFormat { get; init; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>Peek.FilePreviewer</RootNamespace>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Controls\BrowserControl.xaml" />
|
||||
<None Remove="FilePreview.xaml" />
|
||||
<None Remove="UnsupportedFilePreview.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\Peek.Common\Peek.Common.csproj" />
|
||||
<ProjectReference Include="..\WIC\WIC.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="FilePreview.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="UnsupportedFilePreview.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\BrowserControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers.Helpers
|
||||
{
|
||||
public static class IconHelper
|
||||
{
|
||||
// Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
|
||||
private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
|
||||
|
||||
public static HResult GetIcon(string fileName, out IntPtr hbitmap)
|
||||
{
|
||||
Guid shellItem2Guid = new Guid(IShellItem2Guid);
|
||||
int retCode = NativeMethods.SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out IShellItem nativeShellItem);
|
||||
|
||||
if (retCode != 0)
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(retCode)!;
|
||||
}
|
||||
|
||||
NativeSize large = new NativeSize { Width = 256, Height = 256 };
|
||||
var options = ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.IconOnly;
|
||||
|
||||
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(large, options, out hbitmap);
|
||||
|
||||
if (hr != HResult.Ok)
|
||||
{
|
||||
// TODO: fallback to a generic icon
|
||||
}
|
||||
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public interface IBitmapPreviewer : IPreviewer
|
||||
{
|
||||
public BitmapSource? Preview { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public interface IBrowserPreviewer : IPreviewer
|
||||
{
|
||||
public Uri? Preview { get; }
|
||||
}
|
||||
}
|
||||
33
src/modules/peek/Peek.FilePreviewer/Previewers/IPreviewer.cs
Normal file
33
src/modules/peek/Peek.FilePreviewer/Previewers/IPreviewer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public interface IPreviewer : INotifyPropertyChanged
|
||||
{
|
||||
PreviewState State { get; set; }
|
||||
|
||||
public static bool IsFileTypeSupported(string fileExt) => throw new NotImplementedException();
|
||||
|
||||
public Task<Size?> GetPreviewSizeAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task LoadPreviewAsync(CancellationToken cancellationToken);
|
||||
|
||||
Task CopyAsync();
|
||||
}
|
||||
|
||||
public enum PreviewState
|
||||
{
|
||||
Uninitialized,
|
||||
Loading,
|
||||
Loaded,
|
||||
Error,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public interface IUnsupportedFilePreviewer : IPreviewer
|
||||
{
|
||||
public BitmapSource? IconPreview { get; }
|
||||
|
||||
public string? FileName { get; }
|
||||
|
||||
public string? FileType { get; }
|
||||
|
||||
public string? FileSize { get; }
|
||||
|
||||
public string? DateModified { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.Common
|
||||
{
|
||||
public static class NativeMethods
|
||||
{
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern int SHCreateItemFromParsingName(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string path,
|
||||
IntPtr pbc,
|
||||
ref Guid riid,
|
||||
[MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DeleteObject(IntPtr hObject);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Peek.Common.Extensions;
|
||||
using Peek.Common.Helpers;
|
||||
using Peek.Common.Models;
|
||||
using Windows.Foundation;
|
||||
using Windows.Win32.UI.Shell.PropertiesSystem;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public static class PropertyHelper
|
||||
{
|
||||
public static Task<Size> GetImageSize(string filePath)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var propertyStore = PropertyStoreHelper.GetPropertyStoreFromPath(filePath, GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM);
|
||||
if (propertyStore != null)
|
||||
{
|
||||
var width = (int)propertyStore.GetUInt(PropertyKey.ImageHorizontalSize);
|
||||
var height = (int)propertyStore.GetUInt(PropertyKey.ImageVerticalSize);
|
||||
|
||||
Marshal.ReleaseComObject(propertyStore);
|
||||
return new Size(width, height);
|
||||
}
|
||||
|
||||
return Size.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
public static Task<ulong> GetFileSizeInBytes(string filePath)
|
||||
{
|
||||
ulong bytes = 0;
|
||||
var propertyStore = PropertyStoreHelper.GetPropertyStoreFromPath(filePath, GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM);
|
||||
if (propertyStore != null)
|
||||
{
|
||||
bytes = propertyStore.GetULong(PropertyKey.FileSizeBytes);
|
||||
|
||||
Marshal.ReleaseComObject(propertyStore);
|
||||
}
|
||||
|
||||
return Task.FromResult(bytes);
|
||||
}
|
||||
|
||||
public static Task<string> GetFileType(string filePath)
|
||||
{
|
||||
var type = string.Empty;
|
||||
var propertyStore = PropertyStoreHelper.GetPropertyStoreFromPath(filePath, GETPROPERTYSTOREFLAGS.GPS_OPENSLOWITEM);
|
||||
if (propertyStore != null)
|
||||
{
|
||||
// TODO: find a way to get user friendly description
|
||||
type = propertyStore.GetString(PropertyKey.FileType);
|
||||
|
||||
Marshal.ReleaseComObject(propertyStore);
|
||||
}
|
||||
|
||||
return Task.FromResult(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public static class ThumbnailHelper
|
||||
{
|
||||
// Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
|
||||
private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
|
||||
|
||||
public static readonly NativeSize HighQualityThumbnailSize = new NativeSize { Width = 720, Height = 720, };
|
||||
public static readonly NativeSize LowQualityThumbnailSize = new NativeSize { Width = 256, Height = 256, };
|
||||
|
||||
private static readonly NativeSize FallBackThumbnailSize = new NativeSize { Width = 96, Height = 96, };
|
||||
private static readonly NativeSize LastFallBackThumbnailSize = new NativeSize { Width = 32, Height = 32, };
|
||||
|
||||
private static readonly List<NativeSize> ThumbnailFallBackSizes = new List<NativeSize>
|
||||
{
|
||||
HighQualityThumbnailSize,
|
||||
LowQualityThumbnailSize,
|
||||
FallBackThumbnailSize,
|
||||
LastFallBackThumbnailSize,
|
||||
};
|
||||
|
||||
// TODO: Add a re-try system if there is no thumbnail of requested size.
|
||||
public static HResult GetThumbnail(string filename, out IntPtr hbitmap, NativeSize thumbnailSize)
|
||||
{
|
||||
Guid shellItem2Guid = new Guid(IShellItem2Guid);
|
||||
int retCode = NativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, ref shellItem2Guid, out IShellItem nativeShellItem);
|
||||
|
||||
if (retCode != 0)
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(retCode)!;
|
||||
}
|
||||
|
||||
var options = ThumbnailOptions.BiggerSizeOk | ThumbnailOptions.ThumbnailOnly | ThumbnailOptions.ScaleUp;
|
||||
|
||||
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(thumbnailSize, options, out hbitmap);
|
||||
|
||||
// Try to get thumbnail using the fallback sizes order
|
||||
if (hr != HResult.Ok)
|
||||
{
|
||||
var currentThumbnailFallBackIndex = ThumbnailFallBackSizes.IndexOf(thumbnailSize);
|
||||
var nextThumbnailFallBackIndex = currentThumbnailFallBackIndex + 1;
|
||||
if (nextThumbnailFallBackIndex < ThumbnailFallBackSizes.Count - 1)
|
||||
{
|
||||
hr = GetThumbnail(filename, out hbitmap, ThumbnailFallBackSizes[nextThumbnailFallBackIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
public static async Task<BitmapImage?> GetThumbnailAsync(File file, uint size)
|
||||
{
|
||||
BitmapImage? bitmapImage = null;
|
||||
|
||||
// preview image
|
||||
var storageFile = await file.GetStorageFileAsync();
|
||||
if (storageFile == null)
|
||||
{
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
var imageStream = await storageFile.GetThumbnailAsync(
|
||||
Windows.Storage.FileProperties.ThumbnailMode.SingleItem,
|
||||
size,
|
||||
Windows.Storage.FileProperties.ThumbnailOptions.None);
|
||||
|
||||
if (imageStream == null)
|
||||
{
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
bitmapImage = new BitmapImage();
|
||||
bitmapImage.SetSource(imageStream);
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.Threading.Tasks;
|
||||
using WIC;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public static class WICHelper
|
||||
{
|
||||
public static Task<Windows.Foundation.Size> GetImageSize(string filePath)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
// TODO: Find a way to get file metadata without hydrating files. Look into Shell API/Windows Property System, e.g., IPropertyStore
|
||||
IWICImagingFactory factory = (IWICImagingFactory)new WICImagingFactoryClass();
|
||||
var decoder = factory.CreateDecoderFromFilename(filePath, IntPtr.Zero, StreamAccessMode.GENERIC_READ, WICDecodeOptions.WICDecodeMetadataCacheOnLoad);
|
||||
var frame = decoder?.GetFrame(0);
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
// TODO: Respect EXIF data and find correct orientation
|
||||
frame?.GetSize(out width, out height);
|
||||
|
||||
return new Windows.Foundation.Size(width, height);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Extensions;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using File = Peek.Common.Models.File;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public partial class ImagePreviewer : ObservableObject, IBitmapPreviewer, IDisposable
|
||||
{
|
||||
[ObservableProperty]
|
||||
private BitmapSource? preview;
|
||||
|
||||
[ObservableProperty]
|
||||
private PreviewState state;
|
||||
|
||||
public ImagePreviewer(File file)
|
||||
{
|
||||
File = file;
|
||||
Dispatcher = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
private File File { get; }
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
private Task<bool>? LowQualityThumbnailTask { get; set; }
|
||||
|
||||
private Task<bool>? HighQualityThumbnailTask { get; set; }
|
||||
|
||||
private Task<bool>? FullQualityImageTask { get; set; }
|
||||
|
||||
private bool IsHighQualityThumbnailLoaded => HighQualityThumbnailTask?.Status == TaskStatus.RanToCompletion;
|
||||
|
||||
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task<Size?> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var propertyImageSize = await PropertyHelper.GetImageSize(File.Path);
|
||||
if (propertyImageSize != Size.Empty)
|
||||
{
|
||||
return propertyImageSize;
|
||||
}
|
||||
|
||||
return await WICHelper.GetImageSize(File.Path);
|
||||
}
|
||||
|
||||
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
State = PreviewState.Loading;
|
||||
|
||||
LowQualityThumbnailTask = LoadLowQualityThumbnailAsync(cancellationToken);
|
||||
HighQualityThumbnailTask = LoadHighQualityThumbnailAsync(cancellationToken);
|
||||
FullQualityImageTask = LoadFullQualityImageAsync(cancellationToken);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Task.WhenAll(LowQualityThumbnailTask, HighQualityThumbnailTask, FullQualityImageTask);
|
||||
|
||||
if (Preview == null && HasFailedLoadingPreview())
|
||||
{
|
||||
State = PreviewState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
var storageFile = await File.GetStorageFileAsync();
|
||||
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetStorageItems(new StorageFile[1] { storageFile }, false);
|
||||
|
||||
RandomAccessStreamReference imageStreamRef = RandomAccessStreamReference.CreateFromFile(storageFile);
|
||||
dataPackage.Properties.Thumbnail = imageStreamRef;
|
||||
dataPackage.SetBitmap(imageStreamRef);
|
||||
|
||||
Clipboard.SetContent(dataPackage);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Preview))
|
||||
{
|
||||
if (Preview != null)
|
||||
{
|
||||
State = PreviewState.Loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task<bool> LoadLowQualityThumbnailAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var hr = ThumbnailHelper.GetThumbnail(Path.GetFullPath(File.Path), out IntPtr hbitmap, ThumbnailHelper.LowQualityThumbnailSize);
|
||||
if (hr != Common.Models.HResult.Ok)
|
||||
{
|
||||
Debug.WriteLine("Error loading low quality thumbnail - hresult: " + hr);
|
||||
|
||||
throw new ArgumentNullException(nameof(hbitmap));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap, cancellationToken);
|
||||
if (!IsFullImageLoaded && !IsHighQualityThumbnailLoaded)
|
||||
{
|
||||
Preview = thumbnailBitmap;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Task<bool> LoadHighQualityThumbnailAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var hr = ThumbnailHelper.GetThumbnail(Path.GetFullPath(File.Path), out IntPtr hbitmap, ThumbnailHelper.HighQualityThumbnailSize);
|
||||
if (hr != Common.Models.HResult.Ok)
|
||||
{
|
||||
Debug.WriteLine("Error loading high quality thumbnail - hresult: " + hr);
|
||||
|
||||
throw new ArgumentNullException(nameof(hbitmap));
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var thumbnailBitmap = await GetBitmapFromHBitmapAsync(hbitmap, cancellationToken);
|
||||
if (!IsFullImageLoaded)
|
||||
{
|
||||
Preview = thumbnailBitmap;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Task<bool> LoadFullQualityImageAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// TODO: Check if this is performant
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var bitmap = await GetFullBitmapFromPathAsync(File.Path, cancellationToken);
|
||||
Preview = bitmap;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private bool HasFailedLoadingPreview()
|
||||
{
|
||||
var hasFailedLoadingLowQualityThumbnail = !(LowQualityThumbnailTask?.Result ?? true);
|
||||
var hasFailedLoadingHighQualityThumbnail = !(HighQualityThumbnailTask?.Result ?? true);
|
||||
var hasFailedLoadingFullQualityImage = !(FullQualityImageTask?.Result ?? true);
|
||||
|
||||
return hasFailedLoadingLowQualityThumbnail && hasFailedLoadingHighQualityThumbnail && hasFailedLoadingFullQualityImage;
|
||||
}
|
||||
|
||||
private static async Task<BitmapImage> GetFullBitmapFromPathAsync(string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (FileStream stream = System.IO.File.OpenRead(path))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await bitmap.SetSourceAsync(stream.AsRandomAccessStream());
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private static async Task<BitmapSource> GetBitmapFromHBitmapAsync(IntPtr hbitmap, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bitmap = System.Drawing.Image.FromHbitmap(hbitmap);
|
||||
var bitmapImage = new BitmapImage();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
bitmap.Save(stream, ImageFormat.Bmp);
|
||||
stream.Position = 0;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
|
||||
}
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// delete HBitmap to avoid memory leaks
|
||||
NativeMethods.DeleteObject(hbitmap);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsFileTypeSupported(string fileExt)
|
||||
{
|
||||
return _supportedFileTypes.Contains(fileExt);
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes = new HashSet<string>
|
||||
{
|
||||
// Image types
|
||||
".bmp",
|
||||
".gif",
|
||||
".jpg",
|
||||
".jfif",
|
||||
".jfi",
|
||||
".jif",
|
||||
".jpeg",
|
||||
".jpe",
|
||||
|
||||
// ".png", // The current ImagePreviewer logic does not support transparency so PNG has it's own logic in PngPreviewer
|
||||
".tif",
|
||||
".tiff",
|
||||
".dib",
|
||||
|
||||
".heic", // Error in System.Drawing.Image.FromHbitmap(hbitmap);
|
||||
".heif",
|
||||
".hif",
|
||||
".avif",
|
||||
".jxr",
|
||||
".wdp",
|
||||
".ico",
|
||||
".thumb",
|
||||
|
||||
// Raw types
|
||||
".arw",
|
||||
".cr2",
|
||||
|
||||
// ".crw", // Error in WICImageFactory.CreateDecoderFromFilename
|
||||
// ".erf", // Error in WICImageFactory.CreateDecoderFromFilename
|
||||
".kdc",
|
||||
".mrw",
|
||||
".nef",
|
||||
".nrw",
|
||||
".orf",
|
||||
".pef",
|
||||
".raf",
|
||||
".raw",
|
||||
".rw2",
|
||||
".rwl",
|
||||
".sr2",
|
||||
".srw",
|
||||
".srf",
|
||||
".dcs",
|
||||
".dcr",
|
||||
".drf",
|
||||
".k25",
|
||||
".3fr",
|
||||
".ari",
|
||||
".bay",
|
||||
".cap",
|
||||
".iiq",
|
||||
".eip",
|
||||
".fff",
|
||||
".mef",
|
||||
".mdc",
|
||||
".mos",
|
||||
".R3D",
|
||||
".rwz",
|
||||
".x3f",
|
||||
".ori",
|
||||
".cr3",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// 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.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Extensions;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using File = Peek.Common.Models.File;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public partial class PngPreviewer : ObservableObject, IBitmapPreviewer
|
||||
{
|
||||
private readonly uint _png_image_size = 1280;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsPreviewLoaded))]
|
||||
private BitmapSource? preview;
|
||||
|
||||
[ObservableProperty]
|
||||
private PreviewState state;
|
||||
|
||||
public PngPreviewer(File file)
|
||||
{
|
||||
File = file;
|
||||
Dispatcher = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
}
|
||||
|
||||
public bool IsPreviewLoaded => preview != null;
|
||||
|
||||
private File File { get; }
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
private Task<bool>? PreviewQualityThumbnailTask { get; set; }
|
||||
|
||||
private Task<bool>? FullQualityImageTask { get; set; }
|
||||
|
||||
private bool IsFullImageLoaded => FullQualityImageTask?.Status == TaskStatus.RanToCompletion;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public async Task<Size?> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var propertyImageSize = await PropertyHelper.GetImageSize(File.Path);
|
||||
if (propertyImageSize != Size.Empty)
|
||||
{
|
||||
return propertyImageSize;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return await WICHelper.GetImageSize(File.Path);
|
||||
}
|
||||
|
||||
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
State = PreviewState.Loading;
|
||||
|
||||
PreviewQualityThumbnailTask = LoadPreviewImageAsync(cancellationToken);
|
||||
FullQualityImageTask = LoadFullImageAsync(cancellationToken);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Task.WhenAll(PreviewQualityThumbnailTask, FullQualityImageTask);
|
||||
|
||||
if (Preview == null)
|
||||
{
|
||||
State = PreviewState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
var storageFile = await File.GetStorageFileAsync();
|
||||
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetStorageItems(new StorageFile[1] { storageFile }, false);
|
||||
|
||||
RandomAccessStreamReference imageStreamRef = RandomAccessStreamReference.CreateFromFile(storageFile);
|
||||
dataPackage.Properties.Thumbnail = imageStreamRef;
|
||||
dataPackage.SetBitmap(imageStreamRef);
|
||||
|
||||
Clipboard.SetContent(dataPackage);
|
||||
});
|
||||
}
|
||||
|
||||
public static bool IsFileTypeSupported(string fileExt)
|
||||
{
|
||||
return fileExt == ".png" ? true : false;
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(Preview))
|
||||
{
|
||||
if (Preview != null)
|
||||
{
|
||||
State = PreviewState.Loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task<bool> LoadPreviewImageAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var thumbnailTCS = new TaskCompletionSource();
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var thumbnail = await ThumbnailHelper.GetThumbnailAsync(File, _png_image_size);
|
||||
if (!IsFullImageLoaded)
|
||||
{
|
||||
Preview = thumbnail;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Task<bool> LoadFullImageAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var thumbnailTCS = new TaskCompletionSource();
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
WriteableBitmap? bitmap = null;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var sFile = await StorageFile.GetFileFromPathAsync(File.Path);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (var randomAccessStream = await sFile.OpenStreamForReadAsync())
|
||||
{
|
||||
// Create an encoder with the desired format
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var decoder = await BitmapDecoder.CreateAsync(
|
||||
BitmapDecoder.PngDecoderId,
|
||||
randomAccessStream.AsRandomAccessStream());
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var softwareBitmap = await decoder.GetSoftwareBitmapAsync(
|
||||
BitmapPixelFormat.Bgra8,
|
||||
BitmapAlphaMode.Premultiplied);
|
||||
|
||||
// full quality image
|
||||
bitmap = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
softwareBitmap?.CopyToBuffer(bitmap.PixelBuffer);
|
||||
}
|
||||
|
||||
Preview = bitmap;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.Threading;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public class PreviewerFactory
|
||||
{
|
||||
public IPreviewer Create(File file)
|
||||
{
|
||||
if (PngPreviewer.IsFileTypeSupported(file.Extension))
|
||||
{
|
||||
return new PngPreviewer(file);
|
||||
}
|
||||
else if (ImagePreviewer.IsFileTypeSupported(file.Extension))
|
||||
{
|
||||
return new ImagePreviewer(file);
|
||||
}
|
||||
else if (WebBrowserPreviewer.IsFileTypeSupported(file.Extension))
|
||||
{
|
||||
return new WebBrowserPreviewer(file);
|
||||
}
|
||||
|
||||
// Other previewer types check their supported file types here
|
||||
return CreateDefaultPreviewer(file);
|
||||
}
|
||||
|
||||
public IPreviewer CreateDefaultPreviewer(File file)
|
||||
{
|
||||
return new UnsupportedFilePreviewer(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// 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.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common;
|
||||
using Peek.Common.Extensions;
|
||||
using Peek.Common.Helpers;
|
||||
using Peek.FilePreviewer.Previewers.Helpers;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage;
|
||||
using File = Peek.Common.Models.File;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public partial class UnsupportedFilePreviewer : ObservableObject, IUnsupportedFilePreviewer, IDisposable
|
||||
{
|
||||
[ObservableProperty]
|
||||
private BitmapSource? iconPreview;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? fileName;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? fileType;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? fileSize;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? dateModified;
|
||||
|
||||
[ObservableProperty]
|
||||
private PreviewState state;
|
||||
|
||||
public UnsupportedFilePreviewer(File file)
|
||||
{
|
||||
File = file;
|
||||
FileName = file.FileName;
|
||||
DateModified = file.DateModified.ToString();
|
||||
Dispatcher = DispatcherQueue.GetForCurrentThread();
|
||||
PropertyChanged += OnPropertyChanged;
|
||||
|
||||
var settingsUtils = new SettingsUtils();
|
||||
var settings = settingsUtils.GetSettingsOrDefault<PeekSettings>(PeekSettings.ModuleName);
|
||||
|
||||
if (settings != null)
|
||||
{
|
||||
UnsupportedFileWidthPercent = settings.Properties.UnsupportedFileWidthPercent / 100.0;
|
||||
UnsupportedFileHeightPercent = settings.Properties.UnsupportedFileHeightPercent / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
private double UnsupportedFileWidthPercent { get; set; }
|
||||
|
||||
private double UnsupportedFileHeightPercent { get; set; }
|
||||
|
||||
public bool IsPreviewLoaded => iconPreview != null;
|
||||
|
||||
private File File { get; }
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
private Task<bool>? IconPreviewTask { get; set; }
|
||||
|
||||
private Task<bool>? DisplayInfoTask { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public Task<Size?> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
Size? size = new Size(UnsupportedFileWidthPercent, UnsupportedFileHeightPercent);
|
||||
return size;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
State = PreviewState.Loading;
|
||||
|
||||
IconPreviewTask = LoadIconPreviewAsync(cancellationToken);
|
||||
DisplayInfoTask = LoadDisplayInfoAsync(cancellationToken);
|
||||
|
||||
await Task.WhenAll(IconPreviewTask, DisplayInfoTask);
|
||||
|
||||
if (HasFailedLoadingPreview())
|
||||
{
|
||||
State = PreviewState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
var storageFile = await File.GetStorageFileAsync();
|
||||
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetStorageItems(new StorageFile[1] { storageFile }, false);
|
||||
|
||||
Clipboard.SetContent(dataPackage);
|
||||
});
|
||||
}
|
||||
|
||||
public Task<bool> LoadIconPreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
IconHelper.GetIcon(Path.GetFullPath(File.Path), out IntPtr hbitmap);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var iconBitmap = await GetBitmapFromHBitmapWithTransparencyAsync(hbitmap, cancellationToken);
|
||||
IconPreview = iconBitmap;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public Task<bool> LoadDisplayInfoAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return TaskExtension.RunSafe(async () =>
|
||||
{
|
||||
// File Properties
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var bytes = await PropertyHelper.GetFileSizeInBytes(File.Path);
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var type = await PropertyHelper.GetFileType(File.Path);
|
||||
|
||||
await Dispatcher.RunOnUiThread(() =>
|
||||
{
|
||||
FileSize = ReadableStringHelper.BytesToReadableString(bytes);
|
||||
FileType = type;
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(IconPreview))
|
||||
{
|
||||
if (IconPreview != null)
|
||||
{
|
||||
State = PreviewState.Loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasFailedLoadingPreview()
|
||||
{
|
||||
var hasFailedLoadingIconPreview = !(IconPreviewTask?.Result ?? true);
|
||||
var hasFailedLoadingDisplayInfo = !(DisplayInfoTask?.Result ?? true);
|
||||
|
||||
return hasFailedLoadingIconPreview && hasFailedLoadingDisplayInfo;
|
||||
}
|
||||
|
||||
// TODO: Move this to a common helper file and make transparency a parameter (ImagePrevier uses the same code)
|
||||
private static async Task<BitmapSource> GetBitmapFromHBitmapWithTransparencyAsync(IntPtr hbitmap, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var bitmap = System.Drawing.Image.FromHbitmap(hbitmap);
|
||||
bitmap.MakeTransparent();
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
bitmap.Save(stream, ImageFormat.Png);
|
||||
stream.Position = 0;
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
|
||||
}
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// delete HBitmap to avoid memory leaks
|
||||
NativeMethods.DeleteObject(hbitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Peek.Common.Extensions;
|
||||
using Peek.FilePreviewer.Controls;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Foundation;
|
||||
using Windows.Storage;
|
||||
using File = Peek.Common.Models.File;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
public partial class WebBrowserPreviewer : ObservableObject, IBrowserPreviewer
|
||||
{
|
||||
private static readonly HashSet<string> _supportedFileTypes = new HashSet<string>
|
||||
{
|
||||
// Web
|
||||
".html",
|
||||
".htm",
|
||||
|
||||
// Document
|
||||
".pdf",
|
||||
};
|
||||
|
||||
[ObservableProperty]
|
||||
private Uri? preview;
|
||||
|
||||
[ObservableProperty]
|
||||
private PreviewState state;
|
||||
|
||||
public WebBrowserPreviewer(File file)
|
||||
{
|
||||
File = file;
|
||||
Dispatcher = DispatcherQueue.GetForCurrentThread();
|
||||
}
|
||||
|
||||
private File File { get; }
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
public Task<Size?> GetPreviewSizeAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Size? size = null;
|
||||
return Task.FromResult(size);
|
||||
}
|
||||
|
||||
public Task LoadPreviewAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
State = PreviewState.Loading;
|
||||
|
||||
Preview = new Uri(File.Path);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task CopyAsync()
|
||||
{
|
||||
await Dispatcher.RunOnUiThread(async () =>
|
||||
{
|
||||
var storageFile = await File.GetStorageFileAsync();
|
||||
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetStorageItems(new StorageFile[1] { storageFile }, false);
|
||||
|
||||
Clipboard.SetContent(dataPackage);
|
||||
});
|
||||
}
|
||||
|
||||
public static bool IsFileTypeSupported(string fileExt)
|
||||
{
|
||||
return _supportedFileTypes.Contains(fileExt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<UserControl
|
||||
x:Class="Peek.FilePreviewer.UnsupportedFilePreview"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
Margin="48"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="Icon" Width="Auto" />
|
||||
<ColumnDefinition x:Name="FileInfo" Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image
|
||||
x:Name="PreviewImage"
|
||||
Grid.Column="0"
|
||||
Width="180"
|
||||
Height="180"
|
||||
Margin="24"
|
||||
Source="{x:Bind IconPreview, Mode=OneWay}" />
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="5">
|
||||
|
||||
<TextBlock
|
||||
FontSize="26"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind FileName, Mode=OneWay}" />
|
||||
<TextBlock Text="{x:Bind FormattedFileType, Mode=OneWay}" />
|
||||
<TextBlock Text="{x:Bind FormattedFileSize, Mode=OneWay}" />
|
||||
<TextBlock Text="{x:Bind FormattedDateModified, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Peek.Common.Helpers;
|
||||
|
||||
namespace Peek.FilePreviewer
|
||||
{
|
||||
[INotifyPropertyChanged]
|
||||
public sealed partial class UnsupportedFilePreview : UserControl
|
||||
{
|
||||
[ObservableProperty]
|
||||
private BitmapSource? iconPreview;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? fileName;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(FormattedFileType))]
|
||||
private string? fileType;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(FormattedFileSize))]
|
||||
private string? fileSize;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(FormattedDateModified))]
|
||||
private string? dateModified;
|
||||
|
||||
public string FormattedFileType => ReadableStringHelper.FormatResourceString("UnsupportedFile_FileType", FileType);
|
||||
|
||||
public string FormattedFileSize => ReadableStringHelper.FormatResourceString("UnsupportedFile_FileSize", FileSize);
|
||||
|
||||
public string FormattedDateModified => ReadableStringHelper.FormatResourceString("UnsupportedFile_DateModified", DateModified);
|
||||
|
||||
public UnsupportedFilePreview()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/modules/peek/Peek.UI/App.xaml
Normal file
18
src/modules/peek/Peek.UI/App.xaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<Application
|
||||
x:Class="Peek.UI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Peek.UI">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Other app resources here -->
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
55
src/modules/peek/Peek.UI/App.xaml.cs
Normal file
55
src/modules/peek/Peek.UI/App.xaml.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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 ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Peek.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public static int PowerToysPID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="App"/> class.
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched.
|
||||
/// </summary>
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
var cmdArgs = Environment.GetCommandLineArgs();
|
||||
if (cmdArgs?.Length > 1)
|
||||
{
|
||||
if (int.TryParse(cmdArgs[cmdArgs.Length - 1], out int powerToysRunnerPid))
|
||||
{
|
||||
RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () =>
|
||||
{
|
||||
Environment.Exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window = new MainWindow();
|
||||
|
||||
window.Activate();
|
||||
window.Hide();
|
||||
}
|
||||
|
||||
private Window? window;
|
||||
}
|
||||
}
|
||||
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-100.png
Normal file
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-100.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 513 B |
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-125.png
Normal file
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-125.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 638 B |
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-150.png
Normal file
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-150.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 745 B |
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-200.png
Normal file
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-200.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-400.png
Normal file
BIN
src/modules/peek/Peek.UI/Assets/AppList.scale-400.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/modules/peek/Peek.UI/Assets/Icon.ico
Normal file
BIN
src/modules/peek/Peek.UI/Assets/Icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
45
src/modules/peek/Peek.UI/Extensions/HWNDExtensions.cs
Normal file
45
src/modules/peek/Peek.UI/Extensions/HWNDExtensions.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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.Text;
|
||||
using Peek.UI.Native;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
namespace Peek.UI.Extensions
|
||||
{
|
||||
public static class HWNDExtensions
|
||||
{
|
||||
internal static HWND GetActiveTab(this HWND windowHandle)
|
||||
{
|
||||
var activeTab = windowHandle.FindChildWindow("ShellTabWindowClass");
|
||||
if (activeTab == HWND.Null)
|
||||
{
|
||||
activeTab = windowHandle.FindChildWindow("TabWindowClass");
|
||||
}
|
||||
|
||||
return activeTab;
|
||||
}
|
||||
|
||||
internal static bool IsDesktopWindow(this HWND windowHandle)
|
||||
{
|
||||
StringBuilder strClassName = new StringBuilder(256);
|
||||
NativeMethods.GetClassName(windowHandle, strClassName, 256);
|
||||
|
||||
var className = strClassName.ToString();
|
||||
|
||||
if (className != "Progman" && className != "WorkerW")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return windowHandle.FindChildWindow("SHELLDLL_DefView") != HWND.Null;
|
||||
}
|
||||
|
||||
internal static HWND FindChildWindow(this HWND windowHandle, string className)
|
||||
{
|
||||
return PInvoke.FindWindowEx(windowHandle, HWND.Null, className, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/modules/peek/Peek.UI/Extensions/SizeExtensions.cs
Normal file
49
src/modules/peek/Peek.UI/Extensions/SizeExtensions.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Peek.UI.Extensions
|
||||
{
|
||||
public static class SizeExtensions
|
||||
{
|
||||
public static Size Fit(this Size sizeToFit, Size maxSize, Size minSize)
|
||||
{
|
||||
double fittedWidth = sizeToFit.Width;
|
||||
double fittedHeight = sizeToFit.Height;
|
||||
|
||||
double ratioWidth = sizeToFit.Width / maxSize.Width;
|
||||
double ratioHeight = sizeToFit.Height / maxSize.Height;
|
||||
|
||||
if (ratioWidth > ratioHeight)
|
||||
{
|
||||
if (ratioWidth > 1)
|
||||
{
|
||||
fittedWidth = maxSize.Width;
|
||||
fittedHeight = sizeToFit.Height / ratioWidth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ratioHeight > 1)
|
||||
{
|
||||
fittedWidth = sizeToFit.Width / ratioHeight;
|
||||
fittedHeight = maxSize.Height;
|
||||
}
|
||||
}
|
||||
|
||||
if (fittedWidth < minSize.Width)
|
||||
{
|
||||
fittedWidth = minSize.Width;
|
||||
}
|
||||
|
||||
if (fittedHeight < minSize.Height)
|
||||
{
|
||||
fittedHeight = minSize.Height;
|
||||
}
|
||||
|
||||
return new Size(fittedWidth, fittedHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs
Normal file
57
src/modules/peek/Peek.UI/Extensions/WindowExtensions.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Foundation;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Gdi;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Peek.UI.Extensions
|
||||
{
|
||||
public static class WindowExtensions
|
||||
{
|
||||
public static Size GetMonitorSize(this Window window)
|
||||
{
|
||||
var hwnd = new HWND(window.GetWindowHandle());
|
||||
var hwndDesktop = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO info = default(MONITORINFO);
|
||||
info.cbSize = 40;
|
||||
PInvoke.GetMonitorInfo(hwndDesktop, ref info);
|
||||
double monitorWidth = info.rcMonitor.left + info.rcMonitor.right;
|
||||
double monitorHeight = info.rcMonitor.bottom + info.rcMonitor.top;
|
||||
|
||||
return new Size(monitorWidth, monitorHeight);
|
||||
}
|
||||
|
||||
public static double GetMonitorScale(this Window window)
|
||||
{
|
||||
var hwnd = new HWND(window.GetWindowHandle());
|
||||
var dpi = PInvoke.GetDpiForWindow(new HWND(hwnd));
|
||||
double scalingFactor = dpi / 96d;
|
||||
|
||||
return scalingFactor;
|
||||
}
|
||||
|
||||
public static void BringToForeground(this Window window)
|
||||
{
|
||||
var foregroundWindowHandle = PInvoke.GetForegroundWindow();
|
||||
|
||||
uint targetProcessId = 0;
|
||||
uint windowThreadProcessId = 0;
|
||||
unsafe
|
||||
{
|
||||
windowThreadProcessId = PInvoke.GetWindowThreadProcessId(foregroundWindowHandle, &targetProcessId);
|
||||
}
|
||||
|
||||
var windowHandle = window.GetWindowHandle();
|
||||
var currentThreadId = PInvoke.GetCurrentThreadId();
|
||||
PInvoke.AttachThreadInput(windowThreadProcessId, currentThreadId, true);
|
||||
PInvoke.BringWindowToTop(new HWND(windowHandle));
|
||||
PInvoke.ShowWindow(new HWND(windowHandle), Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_SHOW);
|
||||
PInvoke.AttachThreadInput(windowThreadProcessId, currentThreadId, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
src/modules/peek/Peek.UI/FolderItemsQuery.cs
Normal file
163
src/modules/peek/Peek.UI/FolderItemsQuery.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Peek.Common.Models;
|
||||
using Peek.UI.Helpers;
|
||||
|
||||
namespace Peek.UI
|
||||
{
|
||||
public partial class FolderItemsQuery : ObservableObject
|
||||
{
|
||||
private const int UninitializedItemIndex = -1;
|
||||
private readonly object _mutateQueryDataLock = new();
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
[ObservableProperty]
|
||||
private File? currentFile;
|
||||
|
||||
[ObservableProperty]
|
||||
private List<File> files = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isMultiSelection;
|
||||
|
||||
[ObservableProperty]
|
||||
private int currentItemIndex = UninitializedItemIndex;
|
||||
|
||||
private CancellationTokenSource CancellationTokenSource { get; set; } = new CancellationTokenSource();
|
||||
|
||||
private Task? InitializeFilesTask { get; set; } = null;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
CurrentFile = null;
|
||||
IsMultiSelection = false;
|
||||
|
||||
if (InitializeFilesTask != null && InitializeFilesTask.Status == TaskStatus.Running)
|
||||
{
|
||||
Debug.WriteLine("Detected existing initializeFilesTask running. Cancelling it..");
|
||||
CancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
InitializeFilesTask = null;
|
||||
|
||||
lock (_mutateQueryDataLock)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
Files = new List<File>();
|
||||
CurrentItemIndex = UninitializedItemIndex;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateCurrentItemIndex(int desiredIndex)
|
||||
{
|
||||
if (Files.Count <= 1 || CurrentItemIndex == UninitializedItemIndex ||
|
||||
(InitializeFilesTask != null && InitializeFilesTask.Status == TaskStatus.Running))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Current index wraps around when reaching min/max folder item indices
|
||||
desiredIndex %= Files.Count;
|
||||
CurrentItemIndex = desiredIndex < 0 ? Files.Count + desiredIndex : desiredIndex;
|
||||
|
||||
if (CurrentItemIndex < 0 || CurrentItemIndex >= Files.Count)
|
||||
{
|
||||
Debug.Assert(false, "Out of bounds folder item index detected.");
|
||||
CurrentItemIndex = 0;
|
||||
}
|
||||
|
||||
CurrentFile = Files[CurrentItemIndex];
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
var selectedItems = FileExplorerHelper.GetSelectedItems();
|
||||
if (!selectedItems.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasMoreThanOneItem = selectedItems.Skip(1).Any();
|
||||
IsMultiSelection = hasMoreThanOneItem;
|
||||
|
||||
// Prioritize setting CurrentFile, which notifies UI
|
||||
var firstSelectedItem = selectedItems.First();
|
||||
CurrentFile = firstSelectedItem;
|
||||
|
||||
// TODO: we shouldn't get all files from the SHell API, we should query them
|
||||
var items = hasMoreThanOneItem ? selectedItems : FileExplorerHelper.GetItems();
|
||||
if (items == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (InitializeFilesTask != null && InitializeFilesTask.Status == TaskStatus.Running)
|
||||
{
|
||||
Debug.WriteLine("Detected unexpected existing initializeFilesTask running. Cancelling it..");
|
||||
CancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
CancellationTokenSource = new CancellationTokenSource();
|
||||
InitializeFilesTask = new Task(() => InitializeFiles(items, firstSelectedItem, CancellationTokenSource.Token));
|
||||
|
||||
// Execute file initialization/querying on background thread
|
||||
InitializeFilesTask.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine("Exception trying to run initializeFilesTask:\n" + e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Finds index of firstSelectedItem either amongst folder items, initializing our internal File list
|
||||
// since storing Shell32.FolderItems as a field isn't reliable.
|
||||
// Can take a few seconds for folders with 1000s of items; ensure it runs on a background thread.
|
||||
//
|
||||
// TODO optimization:
|
||||
// Handle case where selected items count > 1 separately. Although it'll still be slow for 1000s of items selected,
|
||||
// we can leverage faster APIs like Windows.Storage when 1 item is selected, and navigation is scoped to
|
||||
// the entire folder. We can then avoid iterating through all items here, and maintain a dynamic window of
|
||||
// loaded items around the current item index.
|
||||
private void InitializeFiles(
|
||||
IEnumerable<File> items,
|
||||
File firstSelectedItem,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var listOfItems = items.ToList();
|
||||
var currentItemIndex = listOfItems.FindIndex(item => item.Path == firstSelectedItem.Path);
|
||||
|
||||
if (currentItemIndex < 0)
|
||||
{
|
||||
Debug.WriteLine("File query initialization: selectedItem index not found. Navigation remains disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
lock (_mutateQueryDataLock)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
Files = listOfItems;
|
||||
CurrentItemIndex = currentItemIndex;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/modules/peek/Peek.UI/Helpers/DefaultAppHelper.cs
Normal file
41
src/modules/peek/Peek.UI/Helpers/DefaultAppHelper.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Peek.Common.Models;
|
||||
using Peek.UI.Native;
|
||||
|
||||
namespace Peek.UI.Helpers
|
||||
{
|
||||
public static class DefaultAppHelper
|
||||
{
|
||||
public static string TryGetDefaultAppName(string extension)
|
||||
{
|
||||
string appName = string.Empty;
|
||||
|
||||
// Get the length of the app name
|
||||
uint length = 0;
|
||||
HResult ret = NativeMethods.AssocQueryString(NativeMethods.AssocF.Verify, NativeMethods.AssocStr.FriendlyAppName, extension, null, null, ref length);
|
||||
if (ret != HResult.False)
|
||||
{
|
||||
Debug.WriteLine($"Error when getting accessString for {extension} file: {Marshal.GetExceptionForHR((int)ret)!.Message}");
|
||||
return appName;
|
||||
}
|
||||
|
||||
// Get the app name
|
||||
StringBuilder sb = new((int)length);
|
||||
ret = NativeMethods.AssocQueryString(NativeMethods.AssocF.Verify, NativeMethods.AssocStr.FriendlyAppName, extension, null, sb, ref length);
|
||||
if (ret != HResult.Ok)
|
||||
{
|
||||
Debug.WriteLine($"Error when getting accessString for {extension} file: {Marshal.GetExceptionForHR((int)ret)!.Message}" );
|
||||
return appName;
|
||||
}
|
||||
|
||||
appName = sb.ToString();
|
||||
return appName;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
src/modules/peek/Peek.UI/Helpers/FileExplorerHelper.cs
Normal file
112
src/modules/peek/Peek.UI/Helpers/FileExplorerHelper.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Peek.Common.Models;
|
||||
using Peek.UI.Extensions;
|
||||
using SHDocVw;
|
||||
using Shell32;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
using IServiceProvider = Peek.Common.Models.IServiceProvider;
|
||||
|
||||
namespace Peek.UI.Helpers
|
||||
{
|
||||
public static class FileExplorerHelper
|
||||
{
|
||||
public static IEnumerable<File> GetSelectedItems()
|
||||
{
|
||||
return GetItemsInternal(onlySelectedFiles: true);
|
||||
}
|
||||
|
||||
public static IEnumerable<File> GetItems()
|
||||
{
|
||||
return GetItemsInternal(onlySelectedFiles: false);
|
||||
}
|
||||
|
||||
private static IEnumerable<File> GetItemsInternal(bool onlySelectedFiles)
|
||||
{
|
||||
var foregroundWindowHandle = PInvoke.GetForegroundWindow();
|
||||
if (foregroundWindowHandle.IsDesktopWindow())
|
||||
{
|
||||
return GetItemsFromDesktop(foregroundWindowHandle, onlySelectedFiles);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetItemsFromFileExplorer(foregroundWindowHandle, onlySelectedFiles);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<File> GetItemsFromDesktop(HWND foregroundWindowHandle, bool onlySelectedFiles)
|
||||
{
|
||||
const int SWC_DESKTOP = 8;
|
||||
const int SWFO_NEEDDISPATCH = 1;
|
||||
|
||||
var shell = new Shell();
|
||||
ShellWindows shellWindows = shell.Windows();
|
||||
object? oNull1 = null;
|
||||
object? oNull2 = null;
|
||||
var serviceProvider = (IServiceProvider)shellWindows.FindWindowSW(ref oNull1, ref oNull2, SWC_DESKTOP, out int pHWND, SWFO_NEEDDISPATCH);
|
||||
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(PInvoke.SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
|
||||
var shellView = (IFolderView)shellBrowser.QueryActiveShellView();
|
||||
|
||||
var selectionFlag = onlySelectedFiles ? (uint)_SVGIO.SVGIO_SELECTION : (uint)_SVGIO.SVGIO_ALLVIEW;
|
||||
shellView.Items(selectionFlag, typeof(IShellItemArray).GUID, out var items);
|
||||
if (items is IShellItemArray array)
|
||||
{
|
||||
return array.ToFilesEnumerable();
|
||||
}
|
||||
|
||||
return new List<File>();
|
||||
}
|
||||
|
||||
private static IEnumerable<File> GetItemsFromFileExplorer(HWND foregroundWindowHandle, bool onlySelectedFiles)
|
||||
{
|
||||
var activeTab = foregroundWindowHandle.GetActiveTab();
|
||||
|
||||
var shell = new Shell();
|
||||
ShellWindows shellWindows = shell.Windows();
|
||||
foreach (IWebBrowserApp webBrowserApp in shell.Windows())
|
||||
{
|
||||
var shellFolderView = (IShellFolderViewDual2)webBrowserApp.Document;
|
||||
var folderTitle = shellFolderView.Folder.Title;
|
||||
|
||||
if (webBrowserApp.HWND == foregroundWindowHandle)
|
||||
{
|
||||
var serviceProvider = (IServiceProvider)webBrowserApp;
|
||||
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(PInvoke.SID_STopLevelBrowser, typeof(IShellBrowser).GUID);
|
||||
shellBrowser.GetWindow(out IntPtr shellBrowserHandle);
|
||||
|
||||
if (activeTab == shellBrowserHandle)
|
||||
{
|
||||
var items = onlySelectedFiles ? shellFolderView.SelectedItems() : shellFolderView.Folder?.Items();
|
||||
return items != null ? items.ToFilesEnumerable() : new List<File>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new List<File>();
|
||||
}
|
||||
|
||||
private static IEnumerable<File> ToFilesEnumerable(this IShellItemArray array)
|
||||
{
|
||||
for (var i = 0; i < array.GetCount(); i++)
|
||||
{
|
||||
var item = array.GetItemAt(i);
|
||||
var path = item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH);
|
||||
yield return new File(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<File> ToFilesEnumerable(this FolderItems folderItems)
|
||||
{
|
||||
foreach (FolderItem item in folderItems)
|
||||
{
|
||||
yield return new File(item.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/modules/peek/Peek.UI/MainWindow.xaml
Normal file
42
src/modules/peek/Peek.UI/MainWindow.xaml
Normal file
@@ -0,0 +1,42 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<winuiex:WindowEx
|
||||
x:Class="Peek.UI.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:fp="using:Peek.FilePreviewer"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="using:Peek.UI.Views"
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
mc:Ignorable="d">
|
||||
<winuiex:WindowEx.Backdrop>
|
||||
<winuiex:MicaSystemBackdrop />
|
||||
</winuiex:WindowEx.Backdrop>
|
||||
|
||||
<Grid KeyboardAcceleratorPlacementMode="Hidden">
|
||||
<Grid.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Left" Invoked="LeftNavigationInvoked" />
|
||||
<KeyboardAccelerator Key="Right" Invoked="RightNavigationInvoked" />
|
||||
</Grid.KeyboardAccelerators>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<views:TitleBar
|
||||
x:Name="TitleBarControl"
|
||||
Grid.Row="0"
|
||||
File="{x:Bind ViewModel.FolderItemsQuery.CurrentFile, Mode=OneWay}"
|
||||
FileIndex="{x:Bind ViewModel.FolderItemsQuery.CurrentItemIndex, Mode=OneWay}"
|
||||
IsMultiSelection="{x:Bind ViewModel.FolderItemsQuery.IsMultiSelection, Mode=OneWay}"
|
||||
NumberOfFiles="{x:Bind ViewModel.FolderItemsQuery.Files.Count, Mode=OneWay}" />
|
||||
|
||||
<fp:FilePreview
|
||||
Grid.Row="1"
|
||||
File="{x:Bind ViewModel.FolderItemsQuery.CurrentFile, Mode=OneWay}"
|
||||
PreviewSizeChanged="FilePreviewer_PreviewSizeChanged" />
|
||||
</Grid>
|
||||
</winuiex:WindowEx>
|
||||
172
src/modules/peek/Peek.UI/MainWindow.xaml.cs
Normal file
172
src/modules/peek/Peek.UI/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using interop;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Peek.FilePreviewer.Models;
|
||||
using Peek.UI.Extensions;
|
||||
using Peek.UI.Helpers;
|
||||
using Peek.UI.Native;
|
||||
using Windows.Foundation;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Peek.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainWindow : WindowEx
|
||||
{
|
||||
private const double MaxWindowToMonitorRatio = 0.80;
|
||||
private const double MinWindowHeight = 500;
|
||||
private const double MinWindowWidth = 680;
|
||||
private const double WindowWidthContentPadding = 7;
|
||||
private const double WindowHeightContentPadding = 16;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ViewModel = new MainWindowViewModel();
|
||||
|
||||
NativeEventWaiter.WaitForEventLoop(Constants.ShowPeekEvent(), OnPeekHotkey);
|
||||
|
||||
TitleBarControl.SetTitleBarToWindow(this);
|
||||
|
||||
AppWindow.Closing += AppWindow_Closing;
|
||||
}
|
||||
|
||||
public MainWindowViewModel ViewModel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle Peek hotkey, by toggling the window visibility and querying files when necessary.
|
||||
/// </summary>
|
||||
private void OnPeekHotkey()
|
||||
{
|
||||
if (AppWindow.IsVisible)
|
||||
{
|
||||
if (IsNewSingleSelectedItem())
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
Uninitialize();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void LeftNavigationInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||
{
|
||||
ViewModel.AttemptLeftNavigation();
|
||||
}
|
||||
|
||||
private void RightNavigationInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||
{
|
||||
ViewModel.AttemptRightNavigation();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
ViewModel.FolderItemsQuery.Start();
|
||||
}
|
||||
|
||||
private void Uninitialize()
|
||||
{
|
||||
this.Restore();
|
||||
this.Hide();
|
||||
|
||||
// TODO: move into general ViewModel method when needed
|
||||
ViewModel.FolderItemsQuery.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle FilePreviewerSizeChanged event to adjust window size and position accordingly.
|
||||
/// </summary>
|
||||
/// <param name="sender">object</param>
|
||||
/// <param name="e">PreviewSizeChangedArgs</param>
|
||||
private void FilePreviewer_PreviewSizeChanged(object sender, PreviewSizeChangedArgs e)
|
||||
{
|
||||
var monitorSize = this.GetMonitorSize();
|
||||
|
||||
// If no size is requested, try to fit to the monitor size.
|
||||
Size requestedSize = e.WindowSizeRequested ?? monitorSize;
|
||||
|
||||
var titleBarHeight = TitleBarControl.ActualHeight;
|
||||
|
||||
var maxContentSize = new Size(0, 0);
|
||||
var minContentSize = new Size(MinWindowWidth, MinWindowHeight - titleBarHeight);
|
||||
|
||||
var adjustedContentSize = new Size(0, 0);
|
||||
|
||||
if (e.WindowSizeFormat == SizeFormat.Percentage)
|
||||
{
|
||||
maxContentSize = new Size(monitorSize.Width * requestedSize.Width, (monitorSize.Height - titleBarHeight) * requestedSize.Height);
|
||||
minContentSize = new Size(MinWindowWidth, MinWindowHeight - titleBarHeight);
|
||||
|
||||
adjustedContentSize = maxContentSize.Fit(maxContentSize, minContentSize);
|
||||
}
|
||||
else if (e.WindowSizeFormat == SizeFormat.Pixels)
|
||||
{
|
||||
maxContentSize = new Size(monitorSize.Width * MaxWindowToMonitorRatio, (monitorSize.Height - titleBarHeight) * MaxWindowToMonitorRatio);
|
||||
minContentSize = new Size(MinWindowWidth, MinWindowHeight - titleBarHeight);
|
||||
|
||||
adjustedContentSize = requestedSize.Fit(maxContentSize, minContentSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(false, "Unknown SizeFormat set for resizing window.");
|
||||
adjustedContentSize = minContentSize;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Only re-center if window has not been resized by user (or use design-defined logic).
|
||||
// TODO: Investigate why portrait images do not perfectly fit edge-to-edge
|
||||
var monitorScale = this.GetMonitorScale();
|
||||
var scaledWindowWidth = adjustedContentSize.Width / monitorScale;
|
||||
var scaledWindowHeight = adjustedContentSize.Height / monitorScale;
|
||||
|
||||
this.CenterOnScreen(scaledWindowWidth + WindowHeightContentPadding, scaledWindowHeight + titleBarHeight + WindowWidthContentPadding);
|
||||
this.Show();
|
||||
this.BringToForeground();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle AppWindow closing to prevent app termination on close.
|
||||
/// </summary>
|
||||
/// <param name="sender">AppWindow</param>
|
||||
/// <param name="args">AppWindowClosingEventArgs</param>
|
||||
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
|
||||
{
|
||||
args.Cancel = true;
|
||||
Uninitialize();
|
||||
}
|
||||
|
||||
private bool IsNewSingleSelectedItem()
|
||||
{
|
||||
var selectedItems = FileExplorerHelper.GetSelectedItems();
|
||||
if (!selectedItems.Any() || selectedItems.Skip(1).Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var fileExplorerSelectedItemPath = selectedItems.First().Path;
|
||||
var currentFilePath = ViewModel.FolderItemsQuery.CurrentFile?.Path;
|
||||
if (fileExplorerSelectedItemPath == null || currentFilePath == null || fileExplorerSelectedItemPath == currentFilePath)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/modules/peek/Peek.UI/MainWindowViewModel.cs
Normal file
62
src/modules/peek/Peek.UI/MainWindowViewModel.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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 CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Peek.UI
|
||||
{
|
||||
public partial class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
private const int NavigationThrottleDelayMs = 100;
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
NavigationThrottleTimer.Tick += NavigationThrottleTimer_Tick;
|
||||
NavigationThrottleTimer.Interval = TimeSpan.FromMilliseconds(NavigationThrottleDelayMs);
|
||||
}
|
||||
|
||||
public void AttemptLeftNavigation()
|
||||
{
|
||||
if (NavigationThrottleTimer.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationThrottleTimer.Start();
|
||||
|
||||
// TODO: return a bool so UI can give feedback in case navigation is unavailable
|
||||
FolderItemsQuery.UpdateCurrentItemIndex(FolderItemsQuery.CurrentItemIndex - 1);
|
||||
}
|
||||
|
||||
public void AttemptRightNavigation()
|
||||
{
|
||||
if (NavigationThrottleTimer.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationThrottleTimer.Start();
|
||||
|
||||
// TODO: return a bool so UI can give feedback in case navigation is unavailable
|
||||
FolderItemsQuery.UpdateCurrentItemIndex(FolderItemsQuery.CurrentItemIndex + 1);
|
||||
}
|
||||
|
||||
private void NavigationThrottleTimer_Tick(object? sender, object e)
|
||||
{
|
||||
if (sender == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
((DispatcherTimer)sender).Stop();
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private FolderItemsQuery _folderItemsQuery = new();
|
||||
|
||||
private DispatcherTimer NavigationThrottleTimer { get; set; } = new();
|
||||
}
|
||||
}
|
||||
55
src/modules/peek/Peek.UI/Native/NativeMethods.cs
Normal file
55
src/modules/peek/Peek.UI/Native/NativeMethods.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using Peek.Common.Models;
|
||||
|
||||
namespace Peek.UI.Native
|
||||
{
|
||||
public static class NativeMethods
|
||||
{
|
||||
[Flags]
|
||||
public enum AssocF
|
||||
{
|
||||
None = 0,
|
||||
Init_NoRemapCLSID = 0x1,
|
||||
Init_ByExeName = 0x2,
|
||||
Open_ByExeName = 0x2,
|
||||
Init_DefaultToStar = 0x4,
|
||||
Init_DefaultToFolder = 0x8,
|
||||
NoUserSettings = 0x10,
|
||||
NoTruncate = 0x20,
|
||||
Verify = 0x40,
|
||||
RemapRunDll = 0x80,
|
||||
NoFixUps = 0x100,
|
||||
IgnoreBaseClass = 0x200,
|
||||
}
|
||||
|
||||
public enum AssocStr
|
||||
{
|
||||
Command = 1,
|
||||
Executable,
|
||||
FriendlyDocName,
|
||||
FriendlyAppName,
|
||||
NoOpen,
|
||||
ShellNewValue,
|
||||
DDECommand,
|
||||
DDEIfExec,
|
||||
DDEApplication,
|
||||
DDETopic,
|
||||
}
|
||||
|
||||
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
internal static extern HResult AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string? pszExtra, [Out] StringBuilder? pszOut, [In][Out] ref uint pcchOut);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int GetWindowText(Windows.Win32.Foundation.HWND hWnd, StringBuilder lpString, int nMaxCount);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetClassName(IntPtr hWnd, StringBuilder buf, int nMaxCount);
|
||||
}
|
||||
}
|
||||
29
src/modules/peek/Peek.UI/NativeEventWaiter.cs
Normal file
29
src/modules/peek/Peek.UI/NativeEventWaiter.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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.Threading;
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace Peek.UI.Native
|
||||
{
|
||||
public static class NativeEventWaiter
|
||||
{
|
||||
public static void WaitForEventLoop(string eventName, Action callback)
|
||||
{
|
||||
var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
new Thread(() =>
|
||||
{
|
||||
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
while (true)
|
||||
{
|
||||
if (eventHandle.WaitOne())
|
||||
{
|
||||
dispatcherQueue.TryEnqueue(() => callback());
|
||||
}
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/modules/peek/Peek.UI/NativeMethods.txt
Normal file
14
src/modules/peek/Peek.UI/NativeMethods.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
MonitorFromWindow
|
||||
GetMonitorInfo
|
||||
GetDpiForWindow
|
||||
GetForegroundWindow
|
||||
GetWindowThreadProcessId
|
||||
GetCurrentThreadId
|
||||
AttachThreadInput
|
||||
BringWindowToTop
|
||||
ShowWindow
|
||||
GetWindowTextLength
|
||||
FindWindowEx
|
||||
SID_STopLevelBrowser
|
||||
GetClassName
|
||||
_SVGIO
|
||||
48
src/modules/peek/Peek.UI/Package.appxmanifest
Normal file
48
src/modules/peek/Peek.UI/Package.appxmanifest
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="62c2f436-8802-4c26-a73d-b3b8613016fd"
|
||||
Publisher="CN=sachaple"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Peek.UI</DisplayName>
|
||||
<PublisherDisplayName>sachaple</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="Peek.UI"
|
||||
Description="Peek.UI"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
115
src/modules/peek/Peek.UI/Peek.UI.csproj
Normal file
115
src/modules/peek/Peek.UI/Peek.UI.csproj
Normal file
@@ -0,0 +1,115 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
<PropertyGroup>
|
||||
<AssemblyName>PowerToys.Peek.UI</AssemblyName>
|
||||
<AssemblyTitle>PowerToys.Peek.UI</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys Peek UI</AssemblyDescription>
|
||||
<RootNamespace>Peek.UI</RootNamespace>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\modules\Peek\</OutputPath>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Platforms>x86;x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win10-x86;win10-x64;win10-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
<Nullable>Enable</Nullable>
|
||||
<ApplicationIcon>Assets\Icon.ico</ApplicationIcon>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
|
||||
<PropertyGroup Condition="'$(Platform)'=='x64'">
|
||||
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
|
||||
<RuntimeIdentifier>win10-arm64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\Icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\AppList.scale-100.png" />
|
||||
<None Remove="Assets\AppList.scale-125.png" />
|
||||
<None Remove="Assets\AppList.scale-150.png" />
|
||||
<None Remove="Assets\AppList.scale-200.png" />
|
||||
<None Remove="Assets\AppList.scale-400.png" />
|
||||
<None Remove="Views\TitleBar.xaml" />
|
||||
<None Remove="Views\UnsupportedFile.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<COMReference Include="Shell32">
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="SHDocVw">
|
||||
<VersionMinor>1</VersionMinor>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<Guid>eab22ac0-30c1-11cf-a7eb-0000c05bae0b</Guid>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>false</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>Peek.Win32</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.755" />
|
||||
<PackageReference Include="WinUIEx" Version="1.8.0" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
Tools extension to be activated for this project even if the Windows App SDK Nuget
|
||||
package has not yet been restored.
|
||||
-->
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\Peek.Common\Peek.Common.csproj" />
|
||||
<ProjectReference Include="..\Peek.FilePreviewer\Peek.FilePreviewer.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Assets\Icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\UnsupportedFile.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\TitleBar.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
208
src/modules/peek/Peek.UI/Strings/en-us/Resources.resw
Normal file
208
src/modules/peek/Peek.UI/Strings/en-us/Resources.resw
Normal file
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AppTitle.Title" xml:space="preserve">
|
||||
<value>Peek</value>
|
||||
<comment>Name of application.</comment>
|
||||
</data>
|
||||
<data name="AppTitle_FileCounts_Text" xml:space="preserve">
|
||||
<value>({0}/{1})</value>
|
||||
<comment>Text for the file count in the titlebar. 0: the index of the current file. 1: the total number of files selected.</comment>
|
||||
</data>
|
||||
<data name="LaunchAppButton_OpenWith_Text" xml:space="preserve">
|
||||
<value>Open with</value>
|
||||
<comment>Text for button to launch the application picker.</comment>
|
||||
</data>
|
||||
<data name="LaunchAppButton_OpenWith_ToolTip" xml:space="preserve">
|
||||
<value>Open with (Enter)</value>
|
||||
<comment>Tooltip for button to launch the application picker.</comment>
|
||||
</data>
|
||||
<data name="LaunchAppButton_OpenWithApp_Text" xml:space="preserve">
|
||||
<value>Open with {0}</value>
|
||||
<comment>Text for button to launch default application. 0: name of the default application.</comment>
|
||||
</data>
|
||||
<data name="LaunchAppButton_OpenWithApp_ToolTip" xml:space="preserve">
|
||||
<value>Open with {0} (Enter)</value>
|
||||
<comment>Tooltip for button to launch default application. 0: name of the default application.</comment>
|
||||
</data>
|
||||
<data name="UnsupportedFile_FileType" xml:space="preserve">
|
||||
<value>File Type: {0}</value>
|
||||
<comment>File Type label for the unsupported files view. {0} is the type.</comment>
|
||||
</data>
|
||||
<data name="UnsupportedFile_FileSize" xml:space="preserve">
|
||||
<value>Size: {0}</value>
|
||||
<comment>File Size label for the unsupported files view. {0} is the size.</comment>
|
||||
</data>
|
||||
<data name="UnsupportedFile_DateModified" xml:space="preserve">
|
||||
<value>Date Modified: {0}</value>
|
||||
<comment>Date Modified label for the unsupported files view. {0} is the date.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_ByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} B</value>
|
||||
<comment>Abbrivation for the size unit byte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_KiloByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} KB</value>
|
||||
<comment>Abbrivation for the size unit kilobyte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_MegaByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} MB</value>
|
||||
<comment>Abbrivation for the size unit megabyte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_GigaByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} GB</value>
|
||||
<comment>Abbrivation for the size unit gigabyte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_TeraByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} TB</value>
|
||||
<comment>Abbrivation for the size unit terabyte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_PetaByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} PB</value>
|
||||
<comment>Abbrivation for the size unit petabyte.</comment>
|
||||
</data>
|
||||
<data name="ReadableString_ExaByteAbbreviationFormat" xml:space="preserve">
|
||||
<value>{0} EB</value>
|
||||
<comment>Abbrivation for the size unit exabyte.</comment>
|
||||
</data>
|
||||
<data name="PreviewTooltip_FileName" xml:space="preserve">
|
||||
<value>Filename: {0}</value>
|
||||
<comment>Filename for the tooltip of preview. {0} is the name.</comment>
|
||||
</data>
|
||||
<data name="PreviewTooltip_FileType" xml:space="preserve">
|
||||
<value>Item Type: {0}</value>
|
||||
<comment>Item Type for the tooltip of preview. {0} is the type.</comment>
|
||||
</data>
|
||||
<data name="PreviewTooltip_DateModified" xml:space="preserve">
|
||||
<value>Date Modified: {0}</value>
|
||||
<comment>Date Modified label for the tooltip of preview. {0} is the date.</comment>
|
||||
</data>
|
||||
<data name="PreviewTooltip_Dimensions" xml:space="preserve">
|
||||
<value>Dimensions: {0} x {1}</value>
|
||||
<comment>Dimensions label for the tooltip of preview. {0} is the width, {1} is the height.</comment>
|
||||
</data>
|
||||
<data name="PreviewTooltip_FileSize" xml:space="preserve">
|
||||
<value>Size: {0}</value>
|
||||
<comment>File Size label for the tooltip of preview. {0} is the size.</comment>
|
||||
</data>
|
||||
<data name="PreviewTooltip_Blank" xml:space="preserve">
|
||||
<value>File preview</value>
|
||||
<comment>Tooltip of preview when there's no file info available.</comment>
|
||||
</data>
|
||||
</root>
|
||||
133
src/modules/peek/Peek.UI/Views/TitleBar.xaml
Normal file
133
src/modules/peek/Peek.UI/Views/TitleBar.xaml
Normal file
@@ -0,0 +1,133 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<UserControl
|
||||
x:Class="Peek.UI.Views.TitleBar"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Peek.UI.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="TitleBarRootContainer" Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="SystemLeftPaddingColumn" Width="0" />
|
||||
<ColumnDefinition x:Name="DraggableColumn" Width="*" />
|
||||
<ColumnDefinition x:Name="LaunchAppButtonColumn" Width="Auto" />
|
||||
<ColumnDefinition x:Name="AppRightPaddingColumn" Width="65" />
|
||||
<ColumnDefinition x:Name="SystemRightPaddingColumn" Width="0" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid
|
||||
x:Name="AppIconAndFileTitleContainer"
|
||||
Grid.Column="1"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
ColumnSpacing="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="AppIconColumn" Width="32" />
|
||||
<ColumnDefinition x:Name="FileTitleColumn" Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image
|
||||
x:Name="PeekLogo"
|
||||
x:Uid="PeekLogo"
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Source="../Assets/AppList.scale-400.png"
|
||||
Stretch="UniformToFill" />
|
||||
|
||||
<Grid
|
||||
x:Name="FileCountAndNameContainer"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
ColumnSpacing="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="FileCountColumn" Width="auto" />
|
||||
<ColumnDefinition x:Name="FileNameColumn" Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
x:Name="AppTitle_FileCount"
|
||||
x:Uid="AppTitle_FileCount"
|
||||
Grid.Column="0"
|
||||
FontWeight="Bold"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind FileCountText, Mode=OneWay}"
|
||||
Visibility="{x:Bind IsMultiSelection, Mode=OneWay}" />
|
||||
|
||||
<TextBlock
|
||||
x:Name="AppTitle_FileName"
|
||||
x:Uid="AppTitle_FileName"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind File.FileName, Mode=OneWay}"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Button
|
||||
x:Name="LaunchAppButton"
|
||||
x:Uid="LaunchAppButton"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind LaunchDefaultAppButtonAsyncCommand, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind OpenWithAppToolTip, Mode=OneWay}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<FontIcon
|
||||
x:Name="LaunchAppButton_Icon"
|
||||
x:Uid="LaunchAppButton_Icon"
|
||||
FontSize="{StaticResource CaptionTextBlockFontSize}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
x:Name="LaunchAppButton_Text"
|
||||
x:Uid="LaunchAppButton_Text"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind OpenWithAppText, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Enter" />
|
||||
</Button.KeyboardAccelerators>
|
||||
</Button>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveWidth">
|
||||
<VisualState x:Name="MaximumLayout">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="560" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LaunchAppButton_Text.Visibility" Value="Visible" />
|
||||
<Setter Target="LaunchAppButton.Visibility" Value="Visible" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MediumLayout">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="340" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LaunchAppButton_Text.Visibility" Value="Collapsed" />
|
||||
<Setter Target="LaunchAppButton.Visibility" Value="Visible" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="MinimumLayout">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LaunchAppButton_Text.Visibility" Value="Collapsed" />
|
||||
<Setter Target="LaunchAppButton.Visibility" Value="Collapsed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
237
src/modules/peek/Peek.UI/Views/TitleBar.xaml.cs
Normal file
237
src/modules/peek/Peek.UI/Views/TitleBar.xaml.cs
Normal file
@@ -0,0 +1,237 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Peek.Common.Models;
|
||||
using Peek.UI.Extensions;
|
||||
using Peek.UI.Helpers;
|
||||
using Windows.ApplicationModel.Resources;
|
||||
using Windows.Graphics;
|
||||
using Windows.Storage;
|
||||
using Windows.System;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Peek.UI.Views
|
||||
{
|
||||
[INotifyPropertyChanged]
|
||||
public sealed partial class TitleBar : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty FileProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(File),
|
||||
typeof(File),
|
||||
typeof(TitleBar),
|
||||
new PropertyMetadata(null, (d, e) => ((TitleBar)d).OnFilePropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty FileIndexProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(FileIndex),
|
||||
typeof(int),
|
||||
typeof(TitleBar),
|
||||
new PropertyMetadata(-1, (d, e) => ((TitleBar)d).OnFileIndexPropertyChanged()));
|
||||
|
||||
public static readonly DependencyProperty IsMultiSelectionProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(IsMultiSelection),
|
||||
typeof(bool),
|
||||
typeof(TitleBar),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
public static readonly DependencyProperty NumberOfFilesProperty =
|
||||
DependencyProperty.Register(
|
||||
nameof(NumberOfFiles),
|
||||
typeof(int),
|
||||
typeof(TitleBar),
|
||||
new PropertyMetadata(null, null));
|
||||
|
||||
[ObservableProperty]
|
||||
private string openWithAppText = ResourceLoader.GetForViewIndependentUse().GetString("LaunchAppButton_OpenWith_Text");
|
||||
|
||||
[ObservableProperty]
|
||||
private string openWithAppToolTip = ResourceLoader.GetForViewIndependentUse().GetString("LaunchAppButton_OpenWith_ToolTip");
|
||||
|
||||
[ObservableProperty]
|
||||
private string? fileCountText;
|
||||
|
||||
public TitleBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
TitleBarRootContainer.SizeChanged += TitleBarRootContainer_SizeChanged;
|
||||
}
|
||||
|
||||
public File File
|
||||
{
|
||||
get => (File)GetValue(FileProperty);
|
||||
set => SetValue(FileProperty, value);
|
||||
}
|
||||
|
||||
public int FileIndex
|
||||
{
|
||||
get => (int)GetValue(FileIndexProperty);
|
||||
set => SetValue(FileIndexProperty, value);
|
||||
}
|
||||
|
||||
public bool IsMultiSelection
|
||||
{
|
||||
get => (bool)GetValue(IsMultiSelectionProperty);
|
||||
set => SetValue(IsMultiSelectionProperty, value);
|
||||
}
|
||||
|
||||
public int NumberOfFiles
|
||||
{
|
||||
get => (int)GetValue(NumberOfFilesProperty);
|
||||
set => SetValue(NumberOfFilesProperty, value);
|
||||
}
|
||||
|
||||
private string? DefaultAppName { get; set; }
|
||||
|
||||
private Window? MainWindow { get; set; }
|
||||
|
||||
public void SetTitleBarToWindow(MainWindow mainWindow)
|
||||
{
|
||||
MainWindow = mainWindow;
|
||||
|
||||
if (AppWindowTitleBar.IsCustomizationSupported())
|
||||
{
|
||||
UpdateTitleBarCustomization(mainWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
ThemeHelpers.SetImmersiveDarkMode(hWnd, ThemeHelpers.GetAppTheme() == AppTheme.Dark);
|
||||
Visibility = Visibility.Collapsed;
|
||||
|
||||
// Set window icon
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
|
||||
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.SetIcon("Assets/Icon.ico");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async void LaunchDefaultAppButtonAsync()
|
||||
{
|
||||
StorageFile storageFile = await File.GetStorageFileAsync();
|
||||
LauncherOptions options = new();
|
||||
|
||||
if (string.IsNullOrEmpty(DefaultAppName))
|
||||
{
|
||||
// If there's no default app found, open the App picker
|
||||
options.DisplayApplicationPicker = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to launch the default app for current file format
|
||||
bool result = await Launcher.LaunchFileAsync(storageFile, options);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
// If we couldn't successfully open the default app, open the App picker as a fallback
|
||||
options.DisplayApplicationPicker = true;
|
||||
await Launcher.LaunchFileAsync(storageFile, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TitleBarRootContainer_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateDragRegion();
|
||||
}
|
||||
|
||||
private void UpdateDragRegion()
|
||||
{
|
||||
if (MainWindow == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var appWindow = MainWindow.GetAppWindow();
|
||||
if (AppWindowTitleBar.IsCustomizationSupported() && appWindow != null && appWindow.TitleBar.ExtendsContentIntoTitleBar)
|
||||
{
|
||||
var scale = MainWindow.GetMonitorScale();
|
||||
|
||||
SystemRightPaddingColumn.Width = new GridLength(appWindow.TitleBar.RightInset / scale);
|
||||
SystemLeftPaddingColumn.Width = new GridLength(appWindow.TitleBar.LeftInset / scale);
|
||||
|
||||
var dragRectsList = new List<RectInt32>();
|
||||
|
||||
RectInt32 dragRectangleLeft;
|
||||
dragRectangleLeft.X = (int)(SystemLeftPaddingColumn.ActualWidth * scale);
|
||||
dragRectangleLeft.Y = 0;
|
||||
dragRectangleLeft.Height = (int)(TitleBarRootContainer.ActualHeight * scale);
|
||||
dragRectangleLeft.Width = (int)(DraggableColumn.ActualWidth * scale);
|
||||
|
||||
RectInt32 dragRectangleRight;
|
||||
dragRectangleRight.X = (int)((SystemLeftPaddingColumn.ActualWidth + DraggableColumn.ActualWidth + LaunchAppButtonColumn.ActualWidth) * scale);
|
||||
dragRectangleRight.Y = 0;
|
||||
dragRectangleRight.Height = (int)(TitleBarRootContainer.ActualHeight * scale);
|
||||
dragRectangleRight.Width = (int)(AppRightPaddingColumn.ActualWidth * scale);
|
||||
|
||||
dragRectsList.Add(dragRectangleLeft);
|
||||
dragRectsList.Add(dragRectangleRight);
|
||||
|
||||
appWindow.TitleBar.SetDragRectangles(dragRectsList.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTitleBarCustomization(MainWindow mainWindow)
|
||||
{
|
||||
if (AppWindowTitleBar.IsCustomizationSupported())
|
||||
{
|
||||
AppWindow appWindow = mainWindow.GetAppWindow();
|
||||
appWindow.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
|
||||
mainWindow.SetTitleBar(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilePropertyChanged()
|
||||
{
|
||||
if (File == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateFileCountText();
|
||||
UpdateDefaultAppToLaunch();
|
||||
}
|
||||
|
||||
private void OnFileIndexPropertyChanged()
|
||||
{
|
||||
UpdateFileCountText();
|
||||
}
|
||||
|
||||
private void UpdateFileCountText()
|
||||
{
|
||||
// Update file count
|
||||
if (NumberOfFiles > 1)
|
||||
{
|
||||
string fileCountTextFormat = ResourceLoader.GetForViewIndependentUse().GetString("AppTitle_FileCounts_Text");
|
||||
FileCountText = string.Format(fileCountTextFormat, FileIndex + 1, NumberOfFiles);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDefaultAppToLaunch()
|
||||
{
|
||||
// Update the name of default app to launch
|
||||
DefaultAppName = DefaultAppHelper.TryGetDefaultAppName(File.Extension);
|
||||
|
||||
string openWithAppTextFormat = ResourceLoader.GetForViewIndependentUse().GetString("LaunchAppButton_OpenWithApp_Text");
|
||||
OpenWithAppText = string.Format(openWithAppTextFormat, DefaultAppName);
|
||||
|
||||
string openWithAppToolTipFormat = ResourceLoader.GetForViewIndependentUse().GetString("LaunchAppButton_OpenWithApp_ToolTip");
|
||||
OpenWithAppToolTip = string.Format(openWithAppToolTipFormat, DefaultAppName);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/modules/peek/Peek.UI/app.manifest
Normal file
25
src/modules/peek/Peek.UI/app.manifest
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="Peek.UI.app"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8.
|
||||
For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
|
||||
|
||||
It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
15
src/modules/peek/Peek.Win32/Class.cpp
Normal file
15
src/modules/peek/Peek.Win32/Class.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "pch.h"
|
||||
#include "Class.h"
|
||||
#include "Class.g.cpp"
|
||||
|
||||
namespace winrt::Peek_Win32::implementation
|
||||
{
|
||||
int32_t Class::MaPropriete()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Class::MaPropriete(int32_t /* value */)
|
||||
{
|
||||
}
|
||||
}
|
||||
21
src/modules/peek/Peek.Win32/Class.h
Normal file
21
src/modules/peek/Peek.Win32/Class.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Class.g.h"
|
||||
|
||||
namespace winrt::Peek_Win32::implementation
|
||||
{
|
||||
struct Class : ClassT<Class>
|
||||
{
|
||||
Class() = default;
|
||||
|
||||
int32_t MaPropriete();
|
||||
void MaPropriete(int32_t value);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Peek_Win32::factory_implementation
|
||||
{
|
||||
struct Class : ClassT<Class, implementation::Class>
|
||||
{
|
||||
};
|
||||
}
|
||||
9
src/modules/peek/Peek.Win32/Class.idl
Normal file
9
src/modules/peek/Peek.Win32/Class.idl
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Peek_Win32
|
||||
{
|
||||
[default_interface]
|
||||
runtimeclass Class
|
||||
{
|
||||
Class();
|
||||
Int32 MaPropriete;
|
||||
}
|
||||
}
|
||||
122
src/modules/peek/Peek.Win32/Peek.Win32.vcxproj
Normal file
122
src/modules/peek/Peek.Win32/Peek.Win32.vcxproj
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<ProjectGuid>{c9419e76-e798-4e30-af25-efdd27432e25}</ProjectGuid>
|
||||
<ProjectName>Peek_Win32</ProjectName>
|
||||
<RootNamespace>Peek_Win32</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>false</AppContainerApplication>
|
||||
<AppxPackage>false</AppxPackage>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="PropertySheet.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions>
|
||||
<!--Temporarily disable cppwinrt heap enforcement to work around xaml compiler generated std::shared_ptr use -->
|
||||
<AdditionalOptions Condition="'$(CppWinRTHeapEnforcement)'==''">/DWINRT_NO_MAKE_DETECTION %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
<ModuleDefinitionFile>Peek_Win32.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>Shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="Class.h">
|
||||
<DependentUpon>Class.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Class.cpp">
|
||||
<DependentUpon>Class.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="Class.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Peek_Win32.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
<Text Include="readme.txt">
|
||||
<DeploymentContent>false</DeploymentContent>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.220929.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
3
src/modules/peek/Peek.Win32/Peek_Win32.def
Normal file
3
src/modules/peek/Peek.Win32/Peek_Win32.def
Normal file
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
16
src/modules/peek/Peek.Win32/PropertySheet.props
Normal file
16
src/modules/peek/Peek.Win32/PropertySheet.props
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<!--
|
||||
To customize common C++/WinRT project properties:
|
||||
* right-click the project node
|
||||
* expand the Common Properties item
|
||||
* select the C++/WinRT property page
|
||||
|
||||
For more advanced scenarios, and complete documentation, please see:
|
||||
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||
-->
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup />
|
||||
</Project>
|
||||
4
src/modules/peek/Peek.Win32/packages.config
Normal file
4
src/modules/peek/Peek.Win32/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.220531.1" targetFramework="native" />
|
||||
</packages>
|
||||
1
src/modules/peek/Peek.Win32/pch.cpp
Normal file
1
src/modules/peek/Peek.Win32/pch.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
2
src/modules/peek/Peek.Win32/pch.h
Normal file
2
src/modules/peek/Peek.Win32/pch.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include <unknwn.h>
|
||||
23
src/modules/peek/Peek.Win32/readme.txt
Normal file
23
src/modules/peek/Peek.Win32/readme.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
========================================================================
|
||||
C++/WinRT Peek.Win32 Project Overview
|
||||
========================================================================
|
||||
|
||||
This project demonstrates how to get started authoring Windows Runtime
|
||||
classes directly with standard C++, using the C++/WinRT SDK component
|
||||
to generate implementation headers from interface (IDL) files. The
|
||||
generated Windows Runtime component binary and WinMD files should then
|
||||
be bundled with the Universal Windows Platform (UWP) app consuming them.
|
||||
|
||||
Steps:
|
||||
1. Create an interface (IDL) file to define your Windows Runtime class,
|
||||
its default interface, and any other interfaces it implements.
|
||||
2. Build the project once to generate module.g.cpp, module.h.cpp, and
|
||||
implementation templates under the "Generated Files" folder, as
|
||||
well as skeleton class definitions under "Generated Files\sources".
|
||||
3. Use the skeleton class definitions for reference to implement your
|
||||
Windows Runtime classes.
|
||||
|
||||
========================================================================
|
||||
Learn more about C++/WinRT here:
|
||||
http://aka.ms/cppwinrt/
|
||||
========================================================================
|
||||
7
src/modules/peek/WIC/Classes/CLSID.cs
Normal file
7
src/modules/peek/WIC/Classes/CLSID.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace WIC
|
||||
{
|
||||
internal static class CLSID
|
||||
{
|
||||
public const string WICImagingFactory = "cacaf262-9370-4615-a13b-9f5539da4c0a";
|
||||
}
|
||||
}
|
||||
14
src/modules/peek/WIC/Classes/WICImagingFactory.cs
Normal file
14
src/modules/peek/WIC/Classes/WICImagingFactory.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WIC
|
||||
{
|
||||
[ComImport]
|
||||
[Guid(IID.IWICImagingFactory)]
|
||||
[CoClass(typeof(WICImagingFactoryClass))]
|
||||
public interface WICImagingFactory : IWICImagingFactory { }
|
||||
|
||||
[ComImport]
|
||||
[Guid(CLSID.WICImagingFactory)]
|
||||
[ComDefaultInterface(typeof(IWICImagingFactory))]
|
||||
public class WICImagingFactoryClass { }
|
||||
}
|
||||
43
src/modules/peek/WIC/CoTaskMemPtr.cs
Normal file
43
src/modules/peek/WIC/CoTaskMemPtr.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WIC
|
||||
{
|
||||
internal struct CoTaskMemPtr : IDisposable
|
||||
{
|
||||
public static CoTaskMemPtr From<T>(T? nullableStructure) where T : struct
|
||||
{
|
||||
IntPtr value;
|
||||
if (nullableStructure.HasValue)
|
||||
{
|
||||
value = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T)));
|
||||
Marshal.StructureToPtr(nullableStructure, value, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = IntPtr.Zero;
|
||||
}
|
||||
return new CoTaskMemPtr(value);
|
||||
}
|
||||
|
||||
public CoTaskMemPtr(IntPtr value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private IntPtr value;
|
||||
|
||||
public static implicit operator IntPtr(CoTaskMemPtr safeIntPtr)
|
||||
{
|
||||
return safeIntPtr.value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (value != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeCoTaskMem(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user