Compare commits

..

36 Commits

Author SHA1 Message Date
Shawn Yuan
78ed5c7966 Add new pipeline file 2024-11-28 11:49:56 +08:00
Shuai Yuan
3dc491339a Upgrade Windows App SDK to the latest version (#36093)
Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
2024-11-27 13:57:20 -08:00
grzhan
271d64b8dc [PTRun][Docs] Add HttpStatusCodes to Third-Party plugins (#36055)
* [PTRun][Docs] Add HttpStatusCodes to Third-Party plugins

* adding grzhan to names.txt

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
2024-11-27 11:44:56 -08:00
Andrea Bartolucci
a90b646e72 [PTRun][WindowWalker]Added score to the results (#35691)
Added Score to the results returned by WindowWalker

Co-authored-by: Andrea Bartolucci <8514491+andbartol@users.noreply.github.com>
2024-11-27 17:30:25 +00:00
Christian Gaarden Gaardmark
084402a2bd [New+]Windows 10 support (#35832) 2024-11-27 14:22:05 +00:00
Ani
08fe8089b8 [Logs]Fixed name of logging method in managed logging (#35952)
* Fixed name of method in managed logging

* Added hardening
2024-11-26 20:38:41 +00:00
Michael Clayton
53212188b7 [Mouse Jump] Customisable appearance - borders, margins, colours, etc - final part (#35521)
* [MouseJump] move Mouse Jump settings into separate control (#27511)

* [MouseJump] added Mouse Jump style controls to Settings UI (#27511)

* [MouseJump] added Mouse Jump style controls to Settings UI (#27511)

* [MouseJump] removing unused MouseJumpUI code (#27511)

* [MouseJump] whitespace (#27511)

* [MouseJump] fix spellcheck (#27511)

* [MouseJump] enabled "Copy to custom style" (#27511)

* [MouseJump] fixing build (internal members -> public) (#27511)

* [MouseJump] remove unused "using"s (#27511)

* [MouseJump] use custom styles in preview image (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] delinting to trigger a build (#27511)

* [MouseJump] updated settings preview image ("browser" header) (#27511)

* [MouseJump] upgrade default "custom" style settings in config (#27511)

* [MouseJump] fixed a glitch in settings upgrade (#27511)

* [MouseJump] fixed spell checker (#27511)

* [MouseJump] typo in resource strings (image -> images) (#27511)

* Remove unused include
2024-11-26 15:37:59 +00:00
Ishita Agarwal
7c9876582c Removed extra space from welcome page (#35778)
* Removed extra space from welcome page

* Added space between info bar and release notes
2024-11-25 21:13:44 -08:00
Dave Rayment
6cece12b85 [Peek]Add support for previewing .ahk files as plaintext (#35538) 2024-11-22 16:25:01 +00:00
Dave Rayment
863f7aa233 [Peek] Fix Monaco assets folder discovery (#35925) 2024-11-22 16:24:15 +00:00
Dave Rayment
b81478eb97 [Peek]Expand image format support for Image Previewer using local capabilities (#35622)
* Use BitmapDecoder to query compatible file extensions.

* Delete spellcheck exceptions for removed file types

* Remove unused usings.
2024-11-22 14:49:35 +00:00
Søren Kottal
40acdea356 Adds two new third party plugins for Run (#35453)
* Update thirdPartyRunPlugins.md

* Update expect.txt

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
2024-11-21 07:35:18 -08:00
Jaime Bernardo
f1322d22c3 [ci]Fix signing new Workspaces dll in Dart (#36036) 2024-11-21 08:29:57 -06:00
Laszlo Nemeth
6b3bbf7e8f [Workspaces] Implement PWA recognition, launch. (#35913)
* [Workspaces] PWA: first steps: implement PWA app searcher, add basic controls to the editor

* spell checker

* Snapshot tool: adding command line args for edge

* PWA: add icon handling, add launch of PWA

* Impllement Aumid getters and comparison to connect PWA windows and processes. Update LauncherUI, Launcher

* Minor fixes, simplifications

* Spell checker

* Removing manual PWA selection, spell checker

* Fix merge conflict

* Trying to convince spell checker, that "PEB" is a correct word.

* XAML format fix

* Extending snapshot tool by logs for better testablility

* spell checker fix

* extending logs

* extending logs

* Removing some logs, modifying search criteria for pwa helper process search

* extending PWA detection for the case the directory with the app-id is missing

* Fix issue when pwaAppId is null

* fix missing pwa-app-id handling in the editor. Removed unused property (code cleaning) and updating json parser in Launcher

* Code cleaning: Moving duplicate code to a common project

* Fix issue: adding new Guid as app id if it is empty

* Code cleanup: moving Pwa related code from snapshotUtils to PwaHelper

* Code cleaning

* Code cleanup: Move common Application model to Csharp Library

* code cleanup

* modifying package name

* Ading project reference to Common.UI

* Code cleaning, fixing references

---------

Co-authored-by: donlaci <donlaci@yahoo.com>
2024-11-20 20:15:18 +01:00
桜井 ホタル
f66450f6a8 [PTRun][Docs] Add Bilibili to Third-Party plugins (#35926)
* Update thirdPartyRunPlugins.md

* Update names.txt
2024-11-20 09:25:35 -08:00
Ethan Fang
d31934b7de [DATA_AND_PRIVACY.md] updating the list of diagnostic data events (#35922)
* updating the list of diagnostic data events

* updating the list of events
2024-11-20 09:25:21 -08:00
Sanskar Gupta
4f3d7009a8 Simplify language and structure for clarity and ease of use. (#35943)
1. Clearer Section Headings: Renamed sections like "Indicating Interest in Issues" to improve readability.
2. Conciseness and Flow: Shortened sentences and rephrased for directness.
3. Improved Organization: Streamlined instructions in sections like “Filing an Issue” and “Contributing Fixes/Features.”
4. Reduced Redundancy: Simplified repetitive language, especially in localization and contribution details.
5. Enhanced Call to Action: Adjusted "Help Wanted" and "Becoming a Collaborator" to guide users clearly on next steps.
2024-11-20 09:24:04 -08:00
Heiko
25aac6b63d SpellCheck > Allowed names: Remove duplicates (#35907)
Update names.txt
2024-11-13 14:41:55 -08:00
Jeremy Sinclair
00ee6c1510 [Dev][Build] .NET 9 Upgrade (#35716)
* [Deps] Upgrade Framework Libraries to .NET 9 RC2

* [Common][Build] Update TFM to NET9

* [FileLocksmith][Build] Update TFM to NET9 in Publish Profile

* [PreviewPane][Build] Update TFM to NET9 in Publish Profile

* [PTRun][Build] Update TFM to NET9 in Publish Profile

* [Settings][Build] Update TFM to NET9 in Publish Profile

* [MouseWithoutBorders][Analyzers] Resolve WFO1000 by configuring Designer Serialization Visibility

* [Deps] Update Microsoft.CodeAnalysis.NetAnalyzers

* [Analyzers] Set CA1859,CA2263,CA2022 to be excluded from error

* [MouseWithoutBorders] Use System.Threading.Lock to lock instead of object instance

* [ColorPicker] Use System.Threading.Lock to lock instead of object instance

* [AdvancedPaste] Use System.Threading.Lock to lock instead of object instance

* [TextExtractor] Use System.Threading.Lock to lock instead of object instance

* [Hosts] Use System.Threading.Lock to lock instead of object instance

* [MouseJump] Use System.Threading.Lock to lock instead of object instance

* [PTRun] Use System.Threading.Lock to lock instead of object instance

* [Wox] Use System.Threading.Lock to lock instead of object instance

* [Peek] Use System.Threading.Lock to lock instead of object instance

* [PowerAccent] Use System.Threading.Lock to lock instead of object instance

* [Settings] Use System.Threading.Lock to lock instead of object instance

* [Deps] Update NOTICE.md

* [CI] Update .NET version step to target 9.0

* [Build] Attempt to add manual trigger for using Visual Studio Preview for building

* [Build] Fix variable typo

* [Build][Temporary] set to use preview builds

* [Build] Add missing parameters

* [Build][Temporary] directly hardcode preview image

* [Build][Temporary] Trying ImageOverride

* [Build] Revert hardcode and use ImageOverride

* [Build] Add env var for adding prerelease argument for vswhere

* [Build] Update VCToolsVersion script to use env var to optionally add prerelease version checking

* [Build] Remove unneeded parameter

* [Build] Re-add parameter in all the right places

* [CI][Build] Add NoWarn NU5104 when building with VS Preview

* [Deps] Update to stable .NET 9 packages

* [Deps] Update NOTICE.md

* Everything is WPF and WindowsForms now to fix .NET 9 dependency conflicts

* Ensure .NET 9 SDK for tests too

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2024-11-13 12:36:45 -05:00
Ethan Fang
0a1fd8b134 updating the README.md for better clarity (#35885) 2024-11-13 10:43:41 -06:00
Jaime Bernardo
057c13a34d [ci]Fix building for Visual Studio 17.12 (#35905) 2024-11-13 01:13:23 +00:00
Davide Giacometti
2ea9d56129 [Deps]Upgrade System.IO.Abstractions (#35656)
* Upgrade System.IO.Abstractions to the latest stable release
2024-11-11 09:42:40 +00:00
Ionuț Manța
3d306f6177 [Settings]Fix release cycle links in OOBE What's New page (#35801)
* Fix release cycle link in OOBE

Previously it would point to the 5ft last release instead.

* Adressed feedback
2024-11-08 15:38:33 +00:00
Jaime Bernardo
eed39e5d13 [Docs]Fix 0.86.0 release hashes in README (#35767) 2024-11-05 11:42:43 +00:00
Jaime Bernardo
da58b83a88 0.86 changelog (#35693)
* 0.86 changelog

* Fix spellcheck

* Update README.md

Co-authored-by: PesBandi <127593627+PesBandi@users.noreply.github.com>

* Update README.md

Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com>

* Update installer hashes

* 0.86 tweaks proposals (#35749)

* tweaks

* added callout for pwa

* Update README.md

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update README.md

tweaks for clarity

* Fix current release work link

---------

Co-authored-by: PesBandi <127593627+PesBandi@users.noreply.github.com>
Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
Co-authored-by: Ethan Fang <117125208+ethanfangg@users.noreply.github.com>
2024-11-04 12:47:01 -06:00
Jaime Bernardo
ce5e5647b3 [ci][Arm64]Manually copy WebView2 core dll after publishing (#35711)
After we upgraded Windows App SDK to 1.6, Dev Files Preview on Peek has been broken on ARM64.
For .86, we've added WebView2 to Registry Preview in order to have Monaco Editor as the text editor, which is also broken on ARM64.
After a lengthy investigation, it seems we've found the core issue, PowerToys has been shipping with a x64 Microsoft.Web.WebView2.Core.dll in the ARM64 installer, which fails at runtime.
We seem to have hit a version of https://github.com/microsoft/WindowsAppSDK/issues/4826

When we build PowerToys in Dart for release, we publish some of the C# WinUI3Apps after building PowerToys and before signing / building the install. This means that the WindowsAppSDK build will recopy its WebView2 dependency, which for some reason is ARM64. On local builds of PowerToys, PowerRename, a C++ WinAppSDK application finished last, which copies the right dll and it's the reason we weren't being able to repro the issue on local builds of ARM64 PowerToys.

This PR solves the issue by including a short time hack in the CI to copy the right dll after publishing the C# WinUI3Apps when building for ARM64.

## Validation Steps Performed
Waiting for 4 concurrent builds of ARM64 from Dart to test whether the problem is solved.
2024-11-01 15:18:33 -05:00
Davide Giacometti
7382f1836f [Settings]Fix "Diagnostic Data Viewer" UI freeze (#35681)
fix UI freeze
2024-10-31 08:15:42 +00:00
Clint Rutkas
9e3a19804c Update DATA_AND_PRIVACY.md (#35678)
* Update DATA_AND_PRIVACY.md

* Update DATA_AND_PRIVACY.md
2024-10-30 18:45:14 -07:00
Ethan Fang
a3018b0f7d Adding in our "Data and Privacy" documentation (#35601)
* adding in our data and privacy documentation

* Spellcheck

* updated missing descriptions

* Description update

* adding in additional descriptions

---------

Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
2024-10-30 11:53:19 -07:00
Ethan Fang
c04b4a761f [New+]Update icons to PowerToys Style (#35658) 2024-10-30 16:10:24 +00:00
Laszlo Nemeth
369a147a80 [Workspaces]Fix launcher restart for admin users (#35652)
* [Workspaces] fix endless restarting of the launcher

* Fixing restart, creating mutex after restart.
2024-10-30 14:52:15 +00:00
Stefan Markovic
00c86fef4f [Hosts]Reset Terminate event after closing (#35663) 2024-10-30 14:51:51 +00:00
Stefan Markovic
72a481b17c [General]Use background threads in NativeEventWaiters to prevent dangling threads (#35637)
* Use Tasks in NativeEventWaiters to prevent dangling threads

* Use background threads instead
2024-10-29 15:31:13 +00:00
Stefan Markovic
b204353308 [Settings]Fix crash on View Diagnostic data click (#35635) 2024-10-29 14:20:24 +00:00
gokcekantarci
da52d485a6 [runner]Fix opening settings through the update notification (#35429) 2024-10-28 12:10:18 +00:00
Stefan Markovic
515f2386d9 [Telemetry]Re-add preview handlers telemetry logs and add tracers (#35597) 2024-10-28 12:09:08 +00:00
235 changed files with 6025 additions and 2091 deletions

View File

@@ -56,7 +56,6 @@ damienleroy
davidegiacometti
debian
Deibisu
Deibisu
Delimarsky
Deondre
DHowett
@@ -72,6 +71,7 @@ Garside
Gershaft
Giordani
Gokce
grzhan
Guo
hanselman
Harmath
@@ -87,7 +87,6 @@ jefflord
Jordi
jyuwono
Kairu
Kairu
Kamra
Kantarci
Karthick
@@ -123,7 +122,6 @@ Quriz
randyrants
ricardosantos
riri
riri
ritchielawrence
robmikh
Rutkas
@@ -147,6 +145,7 @@ TBM
tilovell
Triet
waaverecords
Whuihuan
Xpg
ycv
Yuniardi
@@ -157,6 +156,8 @@ Zykova
# OTHERS
Bilibili
BVID
cmdow
Controlz
cortana

View File

@@ -1,7 +1,6 @@
# FALSE POSITIVES
## "PackagemanagerWrapper.cs" should be "PackageManagerWrapper.cs"
## NOTICE.MD > MOZILLA PUBLIC LICENSE v1.1
aaaa
abcdefghjkmnpqrstuvxyz
abgr
@@ -43,7 +42,6 @@ AMPROPSETID
amr
ANDSCANS
animatedvisuals
anr
ansicolor
ANull
AOC
@@ -71,11 +69,9 @@ AQS
ARandom
ARCHITEW
ARemapped
ari
ARPINSTALLLOCATION
ARPPRODUCTICON
ARRAYSIZE
arw
asf
AShortcut
ASingle
@@ -106,6 +102,7 @@ backtracer
bbwe
bck
BESTEFFORT
bezelled
bhid
BIF
bigbar
@@ -153,6 +150,7 @@ CALG
callbackptr
calpwstr
Cangjie
caniuse
CANRENAME
CAPTUREBLT
CAPTURECHANGED
@@ -261,7 +259,7 @@ CRH
critsec
Crossdevice
CRSEL
crw
crx
CSearch
CSettings
cso
@@ -303,8 +301,8 @@ DCOM
dcommon
dcomp
DComposition
dcr
dcs
DCR
DCs
ddd
DDEIf
DDevice
@@ -374,7 +372,6 @@ DRAWCLIPBOARD
DRAWFRAME
drawingcolor
dreamsofameaningfullife
drf
drivedetectionwarning
dshow
DSTINVERT
@@ -418,7 +415,6 @@ editkeyboardwindow
EDITSHORTCUTS
editshortcutswindow
EFile
eip
ekus
emmintrin
Emoji
@@ -592,7 +588,7 @@ Hiberboot
HIBYTE
hicon
HIDEWINDOW
hif
Hif
HIMAGELIST
himl
hinst
@@ -641,6 +637,7 @@ HWNDLAST
HWNDNEXT
HWNDPREV
hyjiacan
IApp
IBeam
ICapture
IClass
@@ -667,7 +664,6 @@ IGNOREUNKNOWN
IGraphics
iid
Iindex
iiq
IJson
Ijwhost
IKs
@@ -730,17 +726,17 @@ ISSEPARATOR
ITask
ith
ITHUMBNAIL
ITwoWayPipeMessageIPCManaged
IUI
IUnknown
IUse
IWbem
IWeb
IWIC
iwr
IYUV
jfi
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jif
jjw
jobject
jpe
@@ -749,7 +745,6 @@ Jsons
jsonval
junja
jxr
kdc
keybd
KEYBDDATA
KEYBDINPUT
@@ -874,7 +869,6 @@ MAXIMIZEBOX
MAXSHORTCUTSIZE
maxversiontested
MBR
mdc
MDICHILD
MDL
mdtext
@@ -882,7 +876,6 @@ mdtxt
mdwn
MEDIASUBTYPE
mediatype
mef
MENUITEMINFO
MENUITEMINFOW
MERGECOPY
@@ -940,7 +933,6 @@ mpmc
MRM
MRT
mru
mrw
msc
mscorlib
msdata
@@ -1005,6 +997,7 @@ newitem
newpath
newplus
NEWPLUSCONTEXTMENU
NEWPLUSSHELLEXTENSIONWIN
newrow
newsgroups
NIF
@@ -1063,7 +1056,6 @@ NOZORDER
NPH
npmjs
NResize
nrw
nsunt
NTAPI
ntdll
@@ -1093,7 +1085,6 @@ opensource
openxmlformats
OPTIMIZEFORINVOKE
ORAW
ori
ORPHANEDDIALOGTITLE
ORSCANS
oss
@@ -1142,7 +1133,7 @@ pdo
pdto
pdtobj
pdw
Peb
peb
pef
PElems
Pels
@@ -1178,6 +1169,7 @@ pnid
Pnp
Popups
POPUPWINDOW
POSITIONITEM
POWERRENAMECONTEXTMENU
powerrenameinput
POWERRENAMETEST
@@ -1214,6 +1206,7 @@ prgms
pri
PRINTCLIENT
printmanagement
privacystatement
prm
proactively
PROCESSENTRY
@@ -1271,7 +1264,6 @@ QUERYENDSESSION
QUERYOPEN
QUEUESYNC
QUNS
raf
RAII
RAlt
Rasterize
@@ -1375,8 +1367,6 @@ runtimes
ruuid
rvm
rwin
rwl
rwz
sacl
safeprojectname
SAMEKEYPREVIOUSLYMAPPED
@@ -1510,7 +1500,6 @@ Srch
SRCINVERT
SRCPAINT
SResize
srf
srme
srre
srw
@@ -1585,6 +1574,8 @@ SYSLIB
SYSMENU
SYSTEMAPPS
SYSTEMTIME
SYSTEMWOW
tailwindcss
tapp
TApplication
TApplied
@@ -1628,6 +1619,7 @@ tkconverters
TLayout
tlb
tlbimp
tlhelp
TMPVAR
TNP
Toolhelp

View File

@@ -181,6 +181,7 @@
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll",
"WinUI3Apps\\NewPlusPackage.msix",
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll",
"PowerAccent.Core.dll",
"PowerToys.PowerAccent.dll",
@@ -201,6 +202,7 @@
"PowerToys.WorkspacesLauncherUI.exe",
"PowerToys.WorkspacesLauncherUI.dll",
"PowerToys.WorkspacesModuleInterface.dll",
"PowerToys.WorkspacesCsharpLibrary.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewExt.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll",
@@ -321,6 +323,10 @@
"WinUI3Apps\\ReverseMarkdown.dll",
"WinUI3Apps\\SharpCompress.dll",
"WinUI3Apps\\ZstdSharp.dll",
"TestableIO.System.IO.Abstractions.dll",
"WinUI3Apps\\TestableIO.System.IO.Abstractions.dll",
"TestableIO.System.IO.Abstractions.Wrappers.dll",
"WinUI3Apps\\TestableIO.System.IO.Abstractions.Wrappers.dll",
"ColorCode.Core.dll",
"ColorCode.UWP.dll",
"UnitsNet.dll",

View File

View File

@@ -37,6 +37,10 @@ parameters:
type: boolean
displayName: "Run Tests"
default: true
- name: useVSPreview
type: boolean
displayName: "Build Using Visual Studio Preview"
default: false
extends:
template: templates/pipeline-ci-build.yml
@@ -44,3 +48,4 @@ extends:
buildPlatforms: ${{ parameters.buildPlatforms }}
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}

View File

@@ -33,6 +33,11 @@ parameters:
- x64
- arm64
- name: useVSPreview
type: boolean
displayName: "Build Using Visual Studio Preview"
default: false
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
extends:
@@ -42,7 +47,10 @@ extends:
- 1ES.PT.ViaStartRight
pool:
name: SHINE-INT-S
image: SHINE-VS17-Latest
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
${{ else }}:
image: SHINE-VS17-Latest
os: windows
sdl:
tsa:
@@ -58,7 +66,10 @@ extends:
parameters:
pool:
name: SHINE-INT-L
image: SHINE-VS17-Latest
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
${{ else }}:
image: SHINE-VS17-Latest
os: windows
variables:
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations

View File

@@ -50,7 +50,9 @@ parameters:
- name: runTests
type: boolean
default: true
- name: useVSPreview
type: boolean
default: false
- name: versionNumber
type: string
default: '0.0.1'
@@ -135,7 +137,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '8.0'
version: '9.0'
- ${{ if eq(parameters.runTests, true) }}:
- task: VisualStudioTestPlatformInstaller@1
@@ -181,6 +183,9 @@ jobs:
- pwsh: |-
& "$(build.sourcesdirectory)\.pipelines\verifyAndSetLatestVCToolsVersion.ps1"
displayName: Work around DD-1541167 (VCToolsVersion)
${{ if eq(parameters.useVSPreview, true) }}:
env:
VCWhereExtraVersionTarget: '-prerelease'
- pwsh: |-
& "$(build.sourcesdirectory)\.pipelines\installWiX.ps1"
@@ -228,7 +233,10 @@ jobs:
inputs:
solution: '**\HostsUILib.csproj'
vsVersion: 17.0
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-hosts.binlog
${{ if eq(parameters.useVSPreview, true) }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-hosts.binlog /p:NoWarn=NU5104
${{ else }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-hosts.binlog
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
@@ -241,7 +249,10 @@ jobs:
inputs:
solution: '**\EnvironmentVariablesUILib.csproj'
vsVersion: 17.0
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-env-var-editor.binlog
${{ if eq(parameters.useVSPreview, true) }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-env-var-editor.binlog /p:NoWarn=NU5104
${{ else }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-env-var-editor.binlog
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
@@ -254,7 +265,10 @@ jobs:
inputs:
solution: '**\RegistryPreviewUILib.csproj'
vsVersion: 17.0
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-registry-preview.binlog
${{ if eq(parameters.useVSPreview, true) }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-registry-preview.binlog /p:NoWarn=NU5104
${{ else }}:
msbuildArgs: /p:CIBuild=true;NoBuild=true -t:pack /bl:$(LogOutputDirectory)\build-registry-preview.binlog
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
@@ -376,6 +390,16 @@ jobs:
msbuildArchitecture: x64
maximumCpuCount: true
### HACK: On ARM64 builds, building an app with Windows App SDK copies the x64 WebView2 dll instead of the ARM64 one. This task makes sure the right dll is used.
- task: CopyFiles@2
displayName: HACK Copy core WebView2 ARM64 dll to output directory
condition: eq(variables['BuildPlatform'],'arm64')
inputs:
contents: packages/Microsoft.Web.WebView2.1.0.2739.15/runtimes/win-ARM64/native_uap/Microsoft.Web.WebView2.Core.dll
targetFolder: $(Build.SourcesDirectory)/ARM64/Release/WinUI3Apps/
flattenFolders: True
OverWrite: True
# Check if deps.json files don't reference different dll versions.
- pwsh: |-
& '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\$(BuildPlatform)\$(BuildConfiguration)'

View File

@@ -45,7 +45,7 @@ jobs:
- template: steps-ensure-dotnet-version.yml
parameters:
sdk: true
version: '8.0'
version: '9.0'
- task: VisualStudioTestPlatformInstaller@1
displayName: Ensure VSTest Platform

View File

@@ -19,6 +19,9 @@ parameters:
- name: runTests
type: boolean
default: true
- name: useVSPreview
type: boolean
default: false
stages:
# Allow manual builds to skip pre-check
@@ -43,12 +46,15 @@ stages:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
${{ if eq(parameters.useVSPreview, true) }}:
demands: ImageOverride -equals SHINE-VS17-Preview
buildPlatforms:
- ${{ platform }}
buildConfigurations: [Release]
enablePackageCaching: true
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}
- ${{ if eq(parameters.runTests, true) }}:
- stage: Test_${{ platform }}

View File

@@ -1,7 +1,7 @@
parameters:
- name: version
type: string
default: "8.0"
default: "9.0"
- name: sdk
type: boolean
default: false

View File

@@ -1,4 +1,4 @@
$LatestVCToolsVersion = (([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)).instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.CRT.Source" }).version;
$LatestVCToolsVersion = (([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest $env:VCWhereExtraVersionTarget -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)).instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.CRT.Source" }).version;
Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion"
Write-Output "Updating VCToolsVersion environment variable for job"

View File

@@ -1,53 +1,53 @@
# PowerToys Contributor's Guide
Below is our guidance for how to report issues, propose new features, and submit contributions via Pull Requests (PRs). Our philosophy is heavily based around understanding the problem and scenarios first, this is why we follow this pattern before work has started.
Below is our guidance for reporting issues, proposing new features, and submitting contributions via Pull Requests (PRs). Our philosophy is to understand the problem and scenarios first, which is why we follow this pattern before work starts.
1. There is an issue
2. There has been a conversation
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation)
1. There is an issue.
2. There has been a conversation.
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation).
## Filing an issue
## Filing an Issue
Please follow this simple rule to help us eliminate any unnecessary wasted effort & frustration, and ensure an efficient and effective use of everyone's time - yours, ours, and other community members':
**Importance of Filing an Issue First**
> 👉 If you have a question, think you've discovered an issue, would like to propose a new feature, etc., then find/file an issue **BEFORE** starting work to fix/implement it.
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyones time:
When requesting new features / enhancements, understanding problem and scenario around it is extremely important. Having additional evidence, data, tweets, blog posts, research, ... anything is extremely helpful. This information provides context to the scenario that may otherwise be lost.
> 👉 If you have a question, think you've discovered an issue, or would like to propose a new feature, please find/file an issue **BEFORE** starting work to fix/implement it.
* Don't know whether you're reporting an issue or requesting a feature? File an issue
* Have a question that you don't see answered in docs, videos, etc.? File an issue
* Want to know if we're planning on building a particular feature? File an issue
* Got a great idea for a new utility or feature? File an issue/request/idea
* Don't understand how to do something? File an issue/Community Guidance Request
* Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc.
When requesting new features or enhancements, providing additional evidence, data, tweets, blog posts, or research is extremely helpful. This information gives context to the scenario that may otherwise be lost.
A quick search before filing an issue also could be helpful. It is likely someone else has found the problem you're seeing, and someone may be working on or have already contributed a fix!
* Unsure whether its an issue or feature request? File an issue.
* Have a question that isn't answered in the docs, videos, etc.? File an issue.
* Want to know if were planning a particular feature? File an issue.
* Got a great idea for a new utility or feature? File an issue/request/idea.
* Dont understand how to do something? File an issue/Community Guidance Request.
* Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
### How to tell the PowerToys team this is an interesting thing to focus on
A quick search before filing an issue could be helpful. Its likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
Upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon or a different one. This way allows us to measure how impactful different issues are compared to others. The issue with comments like "+1", "me too", or similar is they actually make it harder to have a conversation and harder to quickly determine trending important requests.
### Indicating Interest in Issues
To let the team know which issues are important, upvote by clicking the [+😊] button and the 👍 icon on the original issue post. Avoid comments like "+1" or "me too" as they clutter the discussion and make it harder to prioritize requests.
---
## Contributing fixes / features
## Contributing Fixes/Features
Please comment on [our "Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769) to let us know you're interested in working on something before you start the work. Not only does this avoid multiple people unexpectedly working on the same thing at the same time but it enables us to make sure everyone is clear on what should be done to implement any new functionality. It's less work for everyone, in the long run, to establish this up front.
Please comment on our ["Would you like to contribute to PowerToys?"](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
### Localization issues
### Localization Issues
Please file localization issues, so our internal localization team can identify and fix them. However we currently don't accept community Pull Requests fixing localization issues. Localization is handled by the internal Microsoft team only.
For localization issues, please file an issue to notify our internal localization team, as community PRs for localization aren't accepted. Localization is handled exclusively by the internal Microsoft team.
### To Spec or not to Spec
### To Spec or Not to Spec
A key point is for everyone to understand the approach that will be taken. We want to be sure if anyone does work, we will accept it in. Items that are larger in scope we'll want some type of spec to understand what is being planned and have a discussion. Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run.
A key point is for everyone to understand the approach that will be taken. We want to be sure that any work done will be accepted. Larger-scope items will require a spec to outline the approach and allow for discussion. Specs help collaborators consider different solutions, describe feature behavior, and plan for errors. Achieving agreement in a spec before writing code often results in simpler code and less wasted effort.
For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Development" section below.
Team members will be happy to help review specs and guide them to completion.
Once a team member has agreed with your approach, proceed to the "Development" section below. Team members are happy to help review specs and guide them to completion.
### Help Wanted
Once the team has approved an issue/spec approach to solving, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
Once the team has approved an issue/spec approach, development can proceed. If no developers are immediately available, the spec may be parked and labeled "Help Wanted," ready for a developer to get started. For development opportunities, visit [Issues labeled Help Wanted](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
---
@@ -55,18 +55,18 @@ Once the team has approved an issue/spec approach to solving, development can pr
Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md).
### Naming of features and functionality
### Naming Features and Functionality
Naming should be descriptive and straight forward. We want names to be clear about functionality and usefulness moving forward.
Names should be descriptive and straightforward, clearly reflecting functionality and usefulness.
### How can I become a collaborator on the PowerToys team
### Becoming a Collaborator on the PowerToys Team
Be a great community member. Just help out a lot and make useful additions, filing bugs/suggestions, help develop fixes and features, code reviews, and always, docs. Lets continue to make the PowerToys repository a great spot to learn and make a great set of utilities.
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
When the time comes, Microsoft will reach out and help make you a formal team member. Just make sure they can reach out to you :)
When the time comes, Microsoft will reach out to you about becoming a formal team member. Just make sure they have a way to contact you. 😊
---
## Thank you
## Thank You
Thank you in advance for your contribution!
Thank you in advance for your contribution! We appreciate your help in making PowerToys a better tool for everyone.

1020
DATA_AND_PRIVACY.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -26,19 +26,19 @@
<PackageVersion Include="Markdig.Signed" Version="0.34.0" />
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="2.5.187" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.7" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0-preview.24508.2" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2739.15" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="8.0.0" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="8.0.10" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.0" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.0" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
@@ -47,7 +47,7 @@
-->
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
@@ -63,24 +63,26 @@
<PackageVersion Include="StreamJsonRpc" Version="2.19.27" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.CodeDom" Version="8.0.0" />
<PackageVersion Include="System.CodeDom" Version="9.0.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="8.0.0" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
<PackageVersion Include="System.Data.OleDb" Version="8.0.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.0" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.0" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.0" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="8.0.1" />
<PackageVersion Include="System.Drawing.Common" Version="8.0.7" />
<PackageVersion Include="System.IO.Abstractions" Version="17.2.3" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="17.2.3" />
<PackageVersion Include="System.Management" Version="8.0.0" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.0" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.0" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.0" />
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
<PackageVersion Include="System.Management" Version="9.0.0" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="8.0.1" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="8.0.1" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.0" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
@@ -92,4 +94,4 @@
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1318,22 +1318,22 @@ EXHIBIT A -Mozilla Public License.
- Mages 2.0.2
- Markdig.Signed 0.34.0
- MessagePack 2.5.187
- Microsoft.CodeAnalysis.NetAnalyzers 8.0.0
- Microsoft.Data.Sqlite 8.0.7
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0-preview.24508.2
- Microsoft.Data.Sqlite 9.0.0
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.Extensions.DependencyInjection 8.0.0
- Microsoft.Extensions.Hosting 8.0.0
- Microsoft.Extensions.Hosting.WindowsServices 8.0.0
- Microsoft.Extensions.Logging 8.0.0
- Microsoft.Extensions.Logging.Abstractions 8.0.0
- Microsoft.Extensions.DependencyInjection 9.0.0
- Microsoft.Extensions.Hosting 9.0.0
- Microsoft.Extensions.Hosting.WindowsServices 9.0.0
- Microsoft.Extensions.Logging 9.0.0
- Microsoft.Extensions.Logging.Abstractions 9.0.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2739.15
- Microsoft.Win32.SystemEvents 8.0.0
- Microsoft.Windows.Compatibility 8.0.10
- Microsoft.Win32.SystemEvents 9.0.0
- Microsoft.Windows.Compatibility 9.0.0
- Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.1.5
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
- Microsoft.WindowsAppSDK 1.6.240923002
- Microsoft.WindowsAppSDK 1.6.241114003
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
- ModernWpfUI 0.9.4
@@ -1346,22 +1346,23 @@ EXHIBIT A -Mozilla Public License.
- SharpCompress 0.37.2
- StreamJsonRpc 2.19.27
- StyleCop.Analyzers 1.2.0-beta.556
- System.CodeDom 8.0.0
- System.CodeDom 9.0.0
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 8.0.0
- System.Configuration.ConfigurationManager 8.0.1
- System.Data.OleDb 8.0.1
- System.ComponentModel.Composition 9.0.0
- System.Configuration.ConfigurationManager 9.0.0
- System.Data.OleDb 9.0.0
- System.Data.SqlClient 4.8.6
- System.Diagnostics.EventLog 8.0.1
- System.Drawing.Common 8.0.7
- System.IO.Abstractions 17.2.3
- System.IO.Abstractions.TestingHelpers 17.2.3
- System.Management 8.0.0
- System.Diagnostics.EventLog 9.0.0
- System.Diagnostics.PerformanceCounter 9.0.0
- System.Drawing.Common 9.0.0
- System.IO.Abstractions 21.0.29
- System.IO.Abstractions.TestingHelpers 21.0.29
- System.Management 9.0.0
- System.Reactive 6.0.1
- System.Runtime.Caching 8.0.1
- System.ServiceProcess.ServiceController 8.0.1
- System.Text.Encoding.CodePages 8.0.0
- System.Text.Json 8.0.5
- System.Runtime.Caching 9.0.0
- System.ServiceProcess.ServiceController 9.0.0
- System.Text.Encoding.CodePages 9.0.0
- System.Text.Json 9.0.0
- UnicodeInformation 2.6.0
- UnitsNet 5.56.0
- UTF.Unknown 2.5.1

View File

@@ -630,6 +630,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "src\common\Tele
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2246,6 +2250,30 @@ Global
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.Build.0 = Release|x64
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.ActiveCfg = Release|x64
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.Build.0 = Debug|ARM64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.ActiveCfg = Debug|x64
@@ -2646,30 +2674,6 @@ Global
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64
@@ -2778,6 +2782,30 @@ Global
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.ActiveCfg = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.Build.0 = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.ActiveCfg = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.Build.0 = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.ActiveCfg = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.Build.0 = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.ActiveCfg = Release|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.Build.0 = Release|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.Build.0 = Debug|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.ActiveCfg = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.Build.0 = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.ActiveCfg = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.Build.0 = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.ActiveCfg = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2956,6 +2984,8 @@ Global
{B5EB9FE9-37EF-47C3-B8B8-81AD3C2972C2} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B}
@@ -2995,8 +3025,6 @@ Global
{8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
{CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E}
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
@@ -3009,6 +3037,8 @@ Global
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

146
README.md
View File

@@ -34,19 +34,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.86%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.85%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysUserSetup-0.85.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysUserSetup-0.85.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysSetup-0.85.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysSetup-0.85.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.87%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.86%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysUserSetup-0.86.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysUserSetup-0.86.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysSetup-0.86.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysSetup-0.86.0-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.85.1-x64.exe][ptUserX64] | 5F287C34BF68972C55D7C26585EA5C449B0DBA7D458BF7039CFF448E1D7B732B |
| Per user - ARM64 | [PowerToysUserSetup-0.85.1-arm64.exe][ptUserArm64] | 6D5C3B24156E6E66FD38AD15076B8442F0A1C5CFCBBDC33AD478FB27E5E086AE |
| Machine wide - x64 | [PowerToysSetup-0.85.1-x64.exe][ptMachineX64] | 1CDD3C9602F6E5DDC19C66A4FDFE4231389C08E6A037DD22C0A6471F10C7BE02 |
| Machine wide - ARM64 | [PowerToysSetup-0.85.1-arm64.exe][ptMachineArm64] | 6F4DC0217495973B974B7AC1099FD01A2A0FCEE96E8719074EC97FBBC0ECAC4A |
| Per user - x64 | [PowerToysUserSetup-0.86.0-x64.exe][ptUserX64] | CFB9608B28B8FF12C9A7C9814A6EF981636EB5AB261DC278C28EC93FD959CCE2 |
| Per user - ARM64 | [PowerToysUserSetup-0.86.0-arm64.exe][ptUserArm64] | 861CEDBFDCDA993D1D1056E3280319D5EA45D142CA3C737AB1FB4FABD651A5F5 |
| Machine wide - x64 | [PowerToysSetup-0.86.0-x64.exe][ptMachineX64] | 857DE9DC5938D9602F82DFD6183DB5E6823B875A412AEC59B4BE93617E27E9CD |
| Machine wide - ARM64 | [PowerToysSetup-0.86.0-arm64.exe][ptMachineArm64] | 6F37192534C195A02A80AAE1E449DF61C894C50763096A06195581801943FA31 |
This is our preferred method.
@@ -92,113 +92,103 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
### 0.85 - September 2024 Update
### 0.86 - October 2024 Update
In this release, we focused on new features, stability, and improvements.
**Highlights**
- New utility: New+ - allows setting a personalized set of templates to quickly create files and folders from a File Explorer context menu. Thanks [@cgaarden](https://github.com/cgaarden)!
- Language selection - it's now possible to select which UI language should be used by PowerToys utilities.
- Lots of quality fixes for Workspaces, improving the number of supported applications.
- Reduced Peek memory usage by fixing image leaks. Thanks [@daverayment](https://github.com/daverayment)!
- Advanced Paste has new abilities: Image to text, and paste to file (text / png / html).
- In settings, we've adjusted the left navigation to group the utilities. As the number of utilities shipped with PowerToys keeps growing, we felt this was a needed adjustment. Thanks everyone for your feedback!
- Workspaces received many bug fixes, including the proper launching of many instances of the same application in the same workspace. Note, we are still actively looking at how to properly handle PWA detection.
- We've added a diagnostic data (telemetry) opt-in option in the Settings General tab. As it is off-by-default, we encourage users to turn it on as that helps direct our development efforts and their journeys. More information about the data we collect can be found in the [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation) and what each event does.
### General
- Added a general setting to select which UI language should be used in PowerToys utilities.
- Fixed internal code of some policies for Group Policy Objects, that were reading registry entries using the wrong internal functions, and structured code better to avoid future mistakes of the same kind. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added a setting for diagnostic data (telemetry) opt-in (off by default, however, see above for why we encourage you to opt-in!) and user controls to view data.
- Improved exception logging by adding the type of Exception and InnerException. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Advanced Paste
- Fixed some telemetry calls to signal Advanced Paste activation on the cases where a direct shortcut is being used without showing the UI.
- User-defined custom actions can only be used with AI turned on, so custom actions were disabled on Settings when AI is disabled and were hidden from the Advanced Paste UI.
- Added new built-in actions: Image to text, and paste txt, png or html as a file.
### Awake
### Mouse Jump
- Fixed tray icon behaviors, not appearing and showing incorrect time. Thanks [@dend](https://github.com/dend)!
- Refactored the common classes into a separate project. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- Brought back the telemetry events that were deleted across previous refactoring efforts.
### Environment Variables Editor
### Mouse Without Borders
- Added the `_NT_SYMBOL_PATH`, `_NT_ALT_SYMBOL_PATH` and `_NT_SYMCACHE_PATH` as variables that are shown as lists. Thanks [@chwarr](https://github.com/chwarr)!
### FancyZones
- Allow snapping applications that were launched by Workspaces.
### File Locksmith
- Fixed an issue causing File Locksmith to be triggered by unrelated verbs in the context menu.
### Mouse Pointer Crosshairs
- Allow crosshairs radius to be 0 pixels. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
- Refactored the Logger common classes. Thanks [@mikeclayton](https://github.com/mikeclayton)!
### New+
- New utility - Allows setting a personalized set of templates to quickly create files and folders from a File Explorer context menu. Thanks [@cgaarden](https://github.com/cgaarden)!
- Added missing entry for New+ policy state reporting in the Bug Report tool. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added a policy for enabling/disabling whether filename extensions should be shown. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed the telemetry event for when the modules is enabled or disabled. (This was a hotfix for 0.85)
- Fixed bug when creating folders or files that contain Unicode characters. Thanks [@cgaarden](https://github.com/cgaarden)!
- Fixed bug when the name of a new folder collided with an already existing folder. Thanks [@cgaarden](https://github.com/cgaarden)!
- Updated the New+ icons to the fluent style.
### Peek
- Properly show file's modified date instead of creation date in the file previewer. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed memory leak caused by unmanaged bitmap images not being freed. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed an issue causing Peek to not be displayed the first time when using a preview handler to display files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Prevent tooltip in file previewer from overlapping with title bar controls. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed memory leaks in thumbnails and refactored image previewer. Thanks [@daverayment](https://github.com/daverayment)!
- Folder preview enumeration of size and number of files is now more responsive and faster. Thanks [@daverayment](https://github.com/daverayment)!
### PowerToys Run
- Improved the message boxes to be more specific when PowerToys Run failed to initialize itself or any plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Use capital letters when showing degree results in the Unit Converter plugin. Thanks [@PesBandi](https://github.com/PesBandi)!
- Handled a culture not found error when checking for right-to-left languages.
- Fixed the WebSearch plugin results title being trimmed in the UI. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
- The Unit Converter plugin will now show more significant digits. Thanks [@PesBandi](https://github.com/PesBandi)!
- Improved error handling when copying to the clipboard results in an error. Thanks [@PesBandi](https://github.com/PesBandi)!
### Quick Accent
- Add the Middle Eastern Romanization character set. Thanks [@PesBandi](https://github.com/PesBandi)!
- Add the degree sign, integral and vertical ellipsis when "All Languages" is selected. Thanks [@rddunphy](https://github.com/rddunphy)!
- Added support for the Serbian Cyrillic character set. Thanks [@Sirozha1337](https://github.com/Sirozha1337)!
### Registry Preview
- Adopted the Monaco Editor as the UI text editor. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Settings
- Fixed the link to the Workspaces documentation. (This was a hotfix for 0.84)
- Fixed flyout issues after the Windows App SDK upgrade. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed initialization for the New+ settings page. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed enabled state of a control on the New+ settings page if the module is enabled by policy. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed a crash when cancelling the template folder selection in the New+ settings page.
- Fixed a crash when trying to access a non-existing templates folder from the New+ page. (This was a hotfix for 0.85)
- Added a navigation tree to group utilities in the left navigation menu.
- Sorted the list of languages in the language selection combo box in the General tab. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed the state of the info bar about templates not being backed up to not close and react to the module's enabled state in the New+ page. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed a crash caused by a dangling thread.
- Clicking a notification about there being an update available should now correctly open the Settings application in the General tab.
- Fixed a UI freeze when trying to access the Diagnostic Data Viewer files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Workspaces
- Fixed detecting and snapping applications like Discord. (This was a hotfix for 0.84)
- Fixed detecting and snapping applications like Steam. (This was a hotfix for 0.84)
- Fixed button visibility in the UI. (This was a hotfix for 0.84)
- Fixed an issue launching the wrong project when the editor was closed without saving or cancelling a new project.
- Properly handle repositioning windows running as administrator.
- Properly handle cases where the monitor where a workspace was saved is no longer present.
- Fixed the workspace launcher restarting itself in a loop without success.
- Properly handle standalone applications.
- Fixed issues causing icons to not show.
- Fixed launching the incorrect workspace when launching many workspaces quickly through shortcuts. (This was a hotfix for 0.85)
- Fixed launching many instances of the same application in a workspace.
- Fixed a crash when a previously captured monitor ID no longer existed.
- Fixed an issue causing the wrong coordinates to be saved for minimized applications.
- Fixed an issue causing a crash when stress testing workspace launching.
- Fixed application launching when UAC is off and every application always runs elevated.
### Documentation
- Fixed the thirdPartyRunPlugins.md entry for the RDP plugin. Thanks [@YisroelTech](https://github.com/YisroelTech)!
- Added HackMD plugin mention to thirdPartyRunPlugins.md. Thanks [@8LWXpg](https://github.com/8LWXpg)!
- Added SSH plugin mention to thirdPartyRunPlugins.md. Thanks [@8LWXpg](https://github.com/8LWXpg)!
- Added the [Data and Privacy documentation](https://github.com/microsoft/PowerToys/blob/main/DATA_AND_PRIVACY.md) to the repo.
### Development
- Upgraded Windows App SDK to 1.6.
- Upgraded the Target Platform Version to 10.0.22621.0.
- Added a bot trigger to automatically add a label to Workspaces issues. Thanks [@plante-msft](https://github.com/plante-msft)!
- Fixed a regular expression in the bot triggers for wanting to submit community contributions. Thanks [@PesBandi](https://github.com/PesBandi)!
- Fixed analyzer errors after the Visual Studio 17.12 update. Thanks [@snickler](https://github.com/snickler)!
- Fixed the TSA configuration for release CI builds.
- Refactored automated file component generation during installer builds.
- Rewrote the Azure Devops build system to be more modular and share more definitions between PR CI and Release CI.
- Fixed debugging of the New+ page of the Settings application when a settings file was not present.
- Fixed setting the version of the App Manifest in the File Locksmith and New+ context menu app packages.
- Fixed abstracted UI library nuget package signing on release CI.
- Removed build status from GitHub README.
- Fixed the CI precheck action to take into account the recent changes in CI actions.
- Added the new Microsoft org issue types to the issue templates. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Updated System.Text.Json to 8.0.5 and System.Runtime.Caching to 8.0.1 and related dependencies to the latest to address security reports. Thanks [@snickler](https://github.com/snickler)!
- Updated WinAppSDK to 1.6.1 and CsWinRT to 2.1.5. Thanks [@snickler](https://github.com/snickler)!
- Upgraded the WpfUI dependency to 3.0.5.
- Updated MessagePack to 2.5.187 and StreamJsonRpc to 2.19.27 to address security reports.
- Removed some of the hacks that are no longer needed that tried to force same dependency versions in .csproj files.
- Removed the Markdown file exclusions from the conditions that trigger a full CI test.
- CI fails again when there are XAML style errors in a PR.
- Fixed CI actions that were not failing when one of the powershell scripts they tried to run was failing.
- Fixed analyzer violations to allow fully building PowerToys on Visual Studio 17.12. Thanks [@snickler](https://github.com/snickler)!
#### What is being planned for version 0.86
#### What is being planned for version 0.87
For [v0.86][github-next-release-work], we'll work on the items below:
For [v0.87][github-next-release-work], we'll work on the items below:
- Stability / bug fixes
- New module: File Actions Menu
@@ -214,7 +204,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct][oss-conduct
## Privacy Statement
The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has the trends from the telemetry. Please read the [Microsoft privacy statement][privacy-link] for more information.
The application logs basic diagnostic data (telemetry). For more information on privacy and what we collect, see our [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation).
[oss-CLA]: https://cla.opensource.microsoft.com
[oss-conduct-code]: CODE_OF_CONDUCT.md

View File

@@ -39,6 +39,9 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [GitHubRepo](https://github.com/8LWXpg/PowerToysRun-GitHubRepo) | [8LWXpg](https://github.com/8LWXpg) | Search and open GitHub repositories |
| [ProcessKiller](https://github.com/8LWXpg/PowerToysRun-ProcessKiller) | [8LWXpg](https://github.com/8LWXpg) | Search and kill processes |
| [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT |
| [CanIUse](https://github.com/skttl/ptrun-caniuse) | [skttl](https://github.com/skttl) | Look up browser feature support with caniuse.com |
| [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS |
| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes |
## Extending software plugins
@@ -58,3 +61,4 @@ Below are community created plugins that target a website or software. They are
| [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords |
| [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes |
| [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients |
| [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili |

View File

@@ -18,6 +18,19 @@
<DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--NewPlusAssetsFiles_Component_Def-->
<!-- NewPlus Shell Extension for Win10 registration -->
<Component Id="NewPlus_ShellExtension_win10" Guid="D5456D4A-6EEC-4B85-944D-6A6A4A74FFA6" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{FF90D477-E32A-4BE8-8CC5-A502A97F5401}">
<RegistryValue Type="string" Value="NewPlus Shell Extension Win10" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.NewPlus.ShellExtension.win10.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\NewPlusShellExtensionWin10">
<RegistryValue Type="string" Value="{FF90D477-E32A-4BE8-8CC5-A502A97F5401}"/>
</RegistryKey>
</Component>
</DirectoryRef>
<ComponentGroup Id="NewPlusComponentGroup">
@@ -27,6 +40,7 @@
</RegistryKey>
<RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/>
</Component>
<ComponentRef Id="NewPlus_ShellExtension_win10" />
</ComponentGroup>

View File

@@ -3,7 +3,7 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WindowsSdkPackageVersion>10.0.22621.48</WindowsSdkPackageVersion>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
@@ -14,7 +14,7 @@
<WarningLevel>4</WarningLevel>
<NoWarn></NoWarn>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<WarningsNotAsErrors>CA1720</WarningsNotAsErrors>
<WarningsNotAsErrors>CA1720;CA1859;CA2263;CA2022</WarningsNotAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">

View File

@@ -7,7 +7,7 @@ export async function registerAdditionalLanguages(monaco){
await languageDefinitions();
registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco)
registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco)
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt"], "txt", monaco)
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt", ".ahk"], "txt", monaco)
registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco)
registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco)
registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco)

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@
<PropertyGroup>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<AssemblyName>PowerToys.Common.UI</AssemblyName>
</PropertyGroup>

View File

@@ -29,34 +29,28 @@ namespace Microsoft.PowerToys.FilePreviewCommon
new XmlFormatter(),
}.AsReadOnly();
private static string? _monacoDirectory;
private static readonly Lazy<string> _monacoDirectory = new(GetRuntimeMonacoDirectory);
public static string GetRuntimeMonacoDirectory()
/// <summary>
/// Gets the path of the Monaco assets folder.
/// </summary>
public static string MonacoDirectory => _monacoDirectory.Value;
private static string GetRuntimeMonacoDirectory()
{
string codeBase = Assembly.GetExecutingAssembly().Location;
string path = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "Assets", "Monaco"));
if (Path.Exists(path))
{
return path;
}
else
{
// We're likely in WinUI3Apps directory and need to go back to the base directory.
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "..", "Assets", "Monaco"));
}
}
string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
public static string MonacoDirectory
{
get
// If the executable is within "WinUI3Apps", correct the path first.
if (Path.GetFileName(exePath) == "WinUI3Apps")
{
if (string.IsNullOrEmpty(_monacoDirectory))
{
_monacoDirectory = GetRuntimeMonacoDirectory();
}
return _monacoDirectory;
exePath = Path.Combine(exePath, "..");
}
string monacoPath = Path.Combine(exePath, "Assets", "Monaco");
return Directory.Exists(monacoPath) ?
monacoPath :
throw new DirectoryNotFoundException($"Monaco assets directory not found at {monacoPath}");
}
public static JsonDocument GetLanguages()

View File

@@ -4,7 +4,6 @@
using System;
using System.IO;
using System.IO.Abstractions;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -23,15 +22,14 @@ namespace ManagedCommon
public static string LoadLanguage()
{
FileSystem fileSystem = new FileSystem();
var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var file = localAppDataDir + SettingsFilePath + SettingsFile;
if (fileSystem.File.Exists(file))
if (File.Exists(file))
{
try
{
Stream inputStream = fileSystem.File.Open(file, FileMode.Open);
var inputStream = File.Open(file, FileMode.Open);
StreamReader reader = new StreamReader(inputStream);
string data = reader.ReadToEnd();
inputStream.Close();

View File

@@ -5,8 +5,9 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO.Abstractions;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using PowerToys.Interop;
@@ -14,7 +15,6 @@ namespace ManagedCommon
{
public static class Logger
{
private static readonly IFileSystem _fileSystem = new FileSystem();
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location).ProductVersion;
@@ -41,28 +41,30 @@ namespace ManagedCommon
applicationLogPath = Constants.AppDataPath() + applicationLogPath + "\\" + Version;
}
if (!_fileSystem.Directory.Exists(applicationLogPath))
if (!Directory.Exists(applicationLogPath))
{
_fileSystem.Directory.CreateDirectory(applicationLogPath);
Directory.CreateDirectory(applicationLogPath);
}
var logFilePath = _fileSystem.Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
Trace.AutoFlush = true;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogError(string message)
{
Log(message, Error);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogError(string message, Exception ex)
{
if (ex == null)
{
LogError(message);
Log(message, Error);
}
else
{
@@ -85,26 +87,31 @@ namespace ManagedCommon
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogWarning(string message)
{
Log(message, Warning);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogInfo(string message)
{
Log(message, Info);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogDebug(string message)
{
Log(message, Debug);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogTrace()
{
Log(string.Empty, TraceFlag);
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Log(string message, string type)
{
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo());
@@ -117,13 +124,49 @@ namespace ManagedCommon
Trace.Unindent();
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static string GetCallerInfo()
{
StackTrace stackTrace = new();
var methodName = stackTrace.GetFrame(3)?.GetMethod();
var className = methodName?.DeclaringType.Name;
return className + "::" + methodName?.Name;
var callerMethod = GetCallerMethod(stackTrace);
return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}";
}
private static MethodBase GetCallerMethod(StackTrace stackTrace)
{
const int topFrame = 3;
var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod();
try
{
if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType))
{
// Async method; return actual method as determined by heuristic:
// "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type".
// There are tighter ways of determining the actual method, but this is good enough and probably faster.
for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++)
{
var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod();
if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace)
{
return deepMethod;
}
}
}
}
catch (Exception)
{
// Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app.
#if DEBUG
throw;
#endif
}
return topMethod;
}
}
}

View File

@@ -15,7 +15,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="System.Management" />
</ItemGroup>

View File

@@ -26,7 +26,7 @@ namespace Microsoft.PowerToys.Telemetry
private readonly bool telemetryEnabled = DataDiagnosticsSettings.GetEnabledValue(); // This is the global telemetry setting on whether to log events
private readonly bool telemetryRecordingEnabled = DataDiagnosticsSettings.GetViewEnabledValue(); // This is the setting for recording telemetry events to disk for viewing
private readonly string etwFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\", "etw");
private string etwFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\", "etw");
private bool disposedValue;
private string sessionName;
private string etwFilePath;
@@ -49,6 +49,18 @@ namespace Microsoft.PowerToys.Telemetry
/// Initializes a new instance of the <see cref="ETWTrace"/> class.
/// </summary>
public ETWTrace()
{
Init();
}
public ETWTrace(string etwPath)
{
this.etwFolderPath = etwPath;
Init();
}
private void Init()
{
if (File.Exists(etwFolderPath))
{

View File

@@ -21,7 +21,7 @@ namespace AdvancedPaste.Settings
private readonly SettingsUtils _settingsUtils;
private readonly TaskScheduler _taskScheduler;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new();
private readonly Lock _loadingSettingsLock = new();
private readonly List<PasteFormats> _additionalActions;
private readonly List<AdvancedPasteCustomAction> _customActions;

View File

@@ -5,7 +5,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<PublishDir>$(PowerToysRoot)\$(Platform)\$(Configuration)\WinUI3Apps</PublishDir>

View File

@@ -276,7 +276,7 @@ namespace Hosts.Tests
service.RemoveReadOnlyAttribute();
var readOnly = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(FileAttributes.ReadOnly);
var readOnly = fileSystem.FileInfo.New(service.HostsFilePath).Attributes.HasFlag(FileAttributes.ReadOnly);
Assert.IsFalse(readOnly);
}
@@ -295,7 +295,7 @@ namespace Hosts.Tests
await service.WriteAsync("# Empty hosts file", Enumerable.Empty<Entry>());
var hidden = fileSystem.FileInfo.FromFileName(service.HostsFilePath).Attributes.HasFlag(FileAttributes.Hidden);
var hidden = fileSystem.FileInfo.New(service.HostsFilePath).Attributes.HasFlag(FileAttributes.Hidden);
Assert.IsTrue(hidden);
}
}

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
@@ -9,7 +10,7 @@ using System.IO.Abstractions;
namespace Hosts.Tests.Mocks
{
public class MockFileSystemWatcher : FileSystemWatcherBase
public partial class MockFileSystemWatcher : FileSystemWatcherBase
{
public override bool IncludeSubdirectories { get; set; }
@@ -27,26 +28,35 @@ namespace Hosts.Tests.Mocks
public override ISynchronizeInvoke SynchronizingObject { get; set; }
public override Collection<string> Filters => throw new System.NotImplementedException();
public override Collection<string> Filters => throw new NotImplementedException();
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => default;
public override IFileSystem FileSystem => throw new NotImplementedException();
public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => default;
public override IContainer Container => throw new NotImplementedException();
public MockFileSystemWatcher(string path) => Path = path;
public override void BeginInit() => throw new NotImplementedException();
public override void EndInit() => throw new NotImplementedException();
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout) => throw new NotImplementedException();
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => throw new NotImplementedException();
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => throw new NotImplementedException();
public MockFileSystemWatcher()
{
}
public MockFileSystemWatcher(string path)
{
Path = path;
}
public MockFileSystemWatcher(string path, string filter)
{
Path = path;
Filter = filter;
}
public override void BeginInit()
{
}
public override void EndInit()
{
}
}
}

View File

@@ -2,18 +2,22 @@
// 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.IO.Abstractions;
namespace Hosts.Tests.Mocks
{
public class MockFileSystemWatcherFactory : IFileSystemWatcherFactory
{
public IFileSystemWatcher CreateNew() => new MockFileSystemWatcher(null);
public IFileSystem FileSystem => throw new NotImplementedException();
public IFileSystemWatcher CreateNew(string path) => new MockFileSystemWatcher(path);
public IFileSystemWatcher New() => new MockFileSystemWatcher();
public IFileSystemWatcher CreateNew(string path, string filter) => new MockFileSystemWatcher(path, filter);
public IFileSystemWatcher New(string path) => new MockFileSystemWatcher(path);
public IFileSystemWatcher FromPath(string path) => new MockFileSystemWatcher(path);
public IFileSystemWatcher New(string path, string filter) => new MockFileSystemWatcher(path, filter);
public IFileSystemWatcher Wrap(FileSystemWatcher fileSystemWatcher) => throw new NotImplementedException();
}
}

View File

@@ -14,7 +14,7 @@ namespace Hosts.Helpers
public static void WaitForEventLoop(string eventName, Action callback)
{
var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
new Thread(() =>
var t = new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
@@ -24,7 +24,10 @@ namespace Hosts.Helpers
dispatcherQueue.TryEnqueue(() => callback());
}
}
}).Start();
});
t.IsBackground = true;
t.Start();
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Hosts.Settings
private readonly SettingsUtils _settingsUtils;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new object();
private readonly Lock _loadingSettingsLock = new Lock();
public bool ShowStartupWarning { get; private set; }

View File

@@ -216,6 +216,12 @@ public:
m_hShowAdminEvent = nullptr;
}
if (m_hTerminateEvent)
{
CloseHandle(m_hTerminateEvent);
m_hTerminateEvent = nullptr;
}
delete this;
}
@@ -280,6 +286,7 @@ public:
SetEvent(m_hTerminateEvent);
WaitForSingleObject(m_hProcess, 1500);
TerminateProcess(m_hProcess, 1);
ResetEvent(m_hTerminateEvent);
}
m_enabled = false;

View File

@@ -52,7 +52,7 @@ namespace HostsUILib.Helpers
_hostsFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), @"System32\drivers\etc\hosts");
_fileSystemWatcher = _fileSystem.FileSystemWatcher.CreateNew();
_fileSystemWatcher = _fileSystem.FileSystemWatcher.New();
_fileSystemWatcher.Path = _fileSystem.Path.GetDirectoryName(HostsFilePath);
_fileSystemWatcher.Filter = _fileSystem.Path.GetFileName(HostsFilePath);
_fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
@@ -130,7 +130,7 @@ namespace HostsUILib.Helpers
throw new NotRunningElevatedException();
}
if (_fileSystem.FileInfo.FromFileName(HostsFilePath).IsReadOnly)
if (_fileSystem.FileInfo.New(HostsFilePath).IsReadOnly)
{
throw new ReadOnlyHostsException();
}
@@ -200,7 +200,7 @@ namespace HostsUILib.Helpers
}
// FileMode.OpenOrCreate is necessary to prevent UnauthorizedAccessException when the hosts file is hidden
using var stream = _fileSystem.FileStream.Create(HostsFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, _defaultBufferSize, FileOptions.Asynchronous);
using var stream = _fileSystem.FileStream.New(HostsFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read, _defaultBufferSize, FileOptions.Asynchronous);
using var writer = new StreamWriter(stream, Encoding);
foreach (var line in lines)
{
@@ -305,7 +305,7 @@ namespace HostsUILib.Helpers
public void RemoveReadOnlyAttribute()
{
var fileInfo = _fileSystem.FileInfo.FromFileName(HostsFilePath);
var fileInfo = _fileSystem.FileInfo.New(HostsFilePath);
if (fileInfo.IsReadOnly)
{
fileInfo.IsReadOnly = false;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
@@ -141,7 +141,7 @@
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -153,7 +153,7 @@
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets'))" />
</Target>
</Project>

View File

@@ -4,5 +4,5 @@
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.6.240923002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.6.241114003" targetFramework="native" />
</packages>

View File

@@ -2,8 +2,9 @@
// 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.Globalization;
using System.Reflection;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MouseJump.Common.Helpers;
using MouseJump.Common.Imaging;
@@ -16,7 +17,7 @@ namespace MouseJump.Common.UnitTests.Helpers;
public static class DrawingHelperTests
{
[TestClass]
public sealed class GetPreviewLayoutTests
public sealed class RenderPreviewTests
{
public sealed class TestCase
{
@@ -46,7 +47,7 @@ public static class DrawingHelperTests
yield return new object[]
{
new TestCase(
previewStyle: StyleHelper.DefaultPreviewStyle,
previewStyle: StyleHelper.BezelledPreviewStyle,
screens: new List<RectangleInfo>()
{
new(0, 0, 500, 500),
@@ -62,7 +63,7 @@ public static class DrawingHelperTests
yield return new object[]
{
new TestCase(
previewStyle: StyleHelper.DefaultPreviewStyle,
previewStyle: StyleHelper.BezelledPreviewStyle,
screens: new List<RectangleInfo>()
{
new(5120, 349, 1920, 1080),
@@ -79,7 +80,7 @@ public static class DrawingHelperTests
public void RunTestCases(TestCase data)
{
// load the fake desktop image
using var desktopImage = GetPreviewLayoutTests.LoadImageResource(data.DesktopImageFilename);
using var desktopImage = RenderPreviewTests.LoadImageResource(data.DesktopImageFilename);
// draw the preview image
var previewLayout = LayoutHelper.GetPreviewLayout(
@@ -90,28 +91,29 @@ public static class DrawingHelperTests
using var actual = DrawingHelper.RenderPreview(previewLayout, imageCopyService);
// load the expected image
var expected = GetPreviewLayoutTests.LoadImageResource(data.ExpectedImageFilename);
var expected = RenderPreviewTests.LoadImageResource(data.ExpectedImageFilename);
// compare the images
var screens = System.Windows.Forms.Screen.AllScreens;
AssertImagesEqual(expected, actual);
}
private static Bitmap LoadImageResource(string filename)
{
// assume embedded resources are in the same source folder as this
// class, and the namespace hierarchy matches the folder structure.
// that way we can build resource names from the current namespace
var resourcePrefix = typeof(DrawingHelperTests).Namespace;
var resourceName = $"{resourcePrefix}.{filename}";
var assembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
var resourceName = $"{typeof(DrawingHelperTests).Namespace}.{filename.Replace("/", ".")}";
var resourceNames = assembly.GetManifestResourceNames();
if (!resourceNames.Contains(resourceName))
{
var message = $"Embedded resource '{resourceName}' does not exist. " +
"Valid resource names are: \r\n" + string.Join("\r\n", resourceNames);
throw new InvalidOperationException(message);
var message = new StringBuilder();
message.AppendLine(CultureInfo.InvariantCulture, $"Embedded resource '{resourceName}' does not exist.");
message.AppendLine($"Known resources:");
foreach (var name in resourceNames)
{
message.AppendLine(name);
}
throw new InvalidOperationException(message.ToString());
}
var stream = assembly.GetManifestResourceStream(resourceName)
@@ -121,7 +123,7 @@ public static class DrawingHelperTests
}
/// <summary>
/// Naive / brute force image comparison - we can optimise this later :-)
/// Naive / brute force image comparison - we can optimize this later :-)
/// </summary>
private static void AssertImagesEqual(Bitmap expected, Bitmap actual)
{

View File

@@ -129,7 +129,7 @@ public static class LayoutHelperTests
public static IEnumerable<object[]> GetTestCases()
{
// happy path - single screen with 50% scaling,
// *has* a preview borders but *no* screenshot borders
// *has* a preview border but *no* screenshot borders
//
// +----------------+
// | |
@@ -160,7 +160,7 @@ public static class LayoutHelperTests
new(0, 0, 1024, 768),
};
var activatedLocation = new PointInfo(512, 384);
var previewLayout = new PreviewLayout(
var expectedResult = new PreviewLayout(
virtualScreen: new(0, 0, 1024, 768),
screens: screens,
activatedScreenIndex: 0,
@@ -183,7 +183,7 @@ public static class LayoutHelperTests
contentBounds: new(6, 6, 512, 384)
),
});
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
// happy path - single screen with 50% scaling,
// *no* preview borders but *has* screenshot borders
@@ -217,7 +217,7 @@ public static class LayoutHelperTests
new(0, 0, 1024, 768),
};
activatedLocation = new PointInfo(512, 384);
previewLayout = new PreviewLayout(
expectedResult = new PreviewLayout(
virtualScreen: new(0, 0, 1024, 768),
screens: screens,
activatedScreenIndex: 0,
@@ -240,7 +240,59 @@ public static class LayoutHelperTests
contentBounds: new(6, 6, 500, 372)
),
});
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
// rounding error check - single screen with 33% scaling,
// no borders, check to make sure form scales to exactly
// fill the canvas size with no rounding errors.
//
// in this test the preview width is 300 and the desktop is
// 900, so the scaling factor is 1/3, but this gets rounded
// to 0.3333333333333333333333333333, and 900 times this value
// is 299.99999999999999999999999997. if we don't scale correctly
// the resulting form width might only be 299 pixels instead of 300
//
// +----------------+
// | |
// | 0 |
// | |
// +----------------+
previewStyle = new PreviewStyle(
canvasSize: new(
width: 300,
height: 200
),
canvasStyle: BoxStyle.Empty,
screenStyle: BoxStyle.Empty);
screens = new List<RectangleInfo>
{
new(0, 0, 900, 200),
};
activatedLocation = new PointInfo(450, 100);
expectedResult = new PreviewLayout(
virtualScreen: new(0, 0, 900, 200),
screens: screens,
activatedScreenIndex: 0,
formBounds: new(300, 66.5m, 300, 67),
previewStyle: previewStyle,
previewBounds: new(
outerBounds: new(0, 0, 300, 67),
marginBounds: new(0, 0, 300, 67),
borderBounds: new(0, 0, 300, 67),
paddingBounds: new(0, 0, 300, 67),
contentBounds: new(0, 0, 300, 67)
),
screenshotBounds: new()
{
new(
outerBounds: new(0, 0, 300, 67),
marginBounds: new(0, 0, 300, 67),
borderBounds: new(0, 0, 300, 67),
paddingBounds: new(0, 0, 300, 67),
contentBounds: new(0, 0, 300, 67)
),
});
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
// primary monitor not topmost / leftmost - if there are screens
// that are further left or higher up than the primary monitor
@@ -291,7 +343,7 @@ public static class LayoutHelperTests
new(0, 0, 5120, 1440),
};
activatedLocation = new(-960, 60);
previewLayout = new PreviewLayout(
expectedResult = new PreviewLayout(
virtualScreen: new(-1920, -480, 7040, 1920),
screens: screens,
activatedScreenIndex: 0,
@@ -321,7 +373,7 @@ public static class LayoutHelperTests
contentBounds: new(204, 60, 500, 132)
),
});
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
}
[TestMethod]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

After

Width:  |  Height:  |  Size: 202 KiB

View File

@@ -15,45 +15,49 @@ public static class SizeInfoTests
{
public sealed class TestCase
{
public TestCase(SizeInfo obj, SizeInfo bounds, SizeInfo expectedResult)
public TestCase(SizeInfo source, SizeInfo bounds, SizeInfo expectedResult, decimal scalingRatio)
{
this.Obj = obj;
this.Source = source;
this.Bounds = bounds;
this.ExpectedResult = expectedResult;
this.ScalingRatio = scalingRatio;
}
public SizeInfo Obj { get; }
public SizeInfo Source { get; }
public SizeInfo Bounds { get; }
public SizeInfo ExpectedResult { get; }
public decimal ScalingRatio { get; }
}
public static IEnumerable<object[]> GetTestCases()
{
// identity tests
yield return new object[] { new TestCase(new(512, 384), new(512, 384), new(512, 384)), };
yield return new object[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768)), };
yield return new object[] { new TestCase(new(512, 384), new(512, 384), new(512, 384), 1), };
yield return new object[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768), 1), };
// general tests
yield return new object[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536)), };
yield return new object[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768)), };
yield return new object[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536), 4), };
yield return new object[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768), 0.5m), };
// scale to fit width
yield return new object[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536)), };
yield return new object[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536), 4), };
// scale to fit height
yield return new object[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536)), };
yield return new object[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536), 4), };
}
[TestMethod]
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
public void RunTestCases(TestCase data)
{
var actual = data.Obj.ScaleToFit(data.Bounds);
var actual = data.Source.ScaleToFit(data.Bounds, out var scalingRatio);
var expected = data.ExpectedResult;
Assert.AreEqual(expected.Width, actual.Width);
Assert.AreEqual(expected.Height, actual.Height);
Assert.AreEqual(scalingRatio, data.ScalingRatio);
}
}

View File

@@ -0,0 +1,91 @@
// 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.Globalization;
namespace MouseJump.Common.Helpers;
public static class ConfigHelper
{
public static Color? ToUnnamedColor(Color? value)
{
if (!value.HasValue)
{
return null;
}
var color = value.Value;
return Color.FromArgb(color.A, color.R, color.G, color.B);
}
public static string? SerializeToConfigColorString(Color? value)
{
if (!value.HasValue)
{
return null;
}
var color = value.Value;
return color switch
{
Color { IsNamedColor: true } =>
$"{nameof(Color)}.{color.Name}",
Color { IsSystemColor: true } =>
$"{nameof(SystemColors)}.{color.Name}",
_ =>
$"#{color.R:X2}{color.G:X2}{color.B:X2}",
};
}
public static Color? DeserializeFromConfigColorString(string? value)
{
if (string.IsNullOrEmpty(value))
{
return null;
}
// e.g. "#AABBCC"
if (value.StartsWith('#'))
{
var culture = CultureInfo.InvariantCulture;
if ((value.Length == 7)
&& int.TryParse(value[1..3], NumberStyles.HexNumber, culture, out var r)
&& int.TryParse(value[3..5], NumberStyles.HexNumber, culture, out var g)
&& int.TryParse(value[5..7], NumberStyles.HexNumber, culture, out var b))
{
return Color.FromArgb(0xFF, r, g, b);
}
}
const StringComparison comparison = StringComparison.InvariantCulture;
// e.g. "Color.Red"
const string colorPrefix = $"{nameof(Color)}.";
if (value.StartsWith(colorPrefix, comparison))
{
var colorName = value[colorPrefix.Length..];
var property = typeof(Color).GetProperties()
.SingleOrDefault(property => property.Name == colorName);
if (property is not null)
{
return (Color?)property.GetValue(null, null);
}
}
// e.g. "SystemColors.Highlight"
const string systemColorPrefix = $"{nameof(SystemColors)}.";
if (value.StartsWith(systemColorPrefix, comparison))
{
var colorName = value[systemColorPrefix.Length..];
var property = typeof(SystemColors).GetProperties()
.SingleOrDefault(property => property.Name == colorName);
if (property is not null)
{
return (Color?)property.GetValue(null, null);
}
}
return null;
}
}

View File

@@ -102,8 +102,13 @@ public static class DrawingHelper
return;
}
if (borderStyle.Color is null)
{
return;
}
// draw the main box border
using var borderBrush = new SolidBrush(borderStyle.Color);
using var borderBrush = new SolidBrush(borderStyle.Color.Value);
var borderRegion = new Region(boxBounds.BorderBounds.ToRectangle());
borderRegion.Exclude(boxBounds.PaddingBounds.ToRectangle());
graphics.FillRegion(borderBrush, borderRegion);

View File

@@ -46,16 +46,13 @@ public static class LayoutHelper
.Shrink(previewStyle.CanvasStyle.BorderStyle)
.Shrink(previewStyle.CanvasStyle.PaddingStyle);
// scale the virtual screen to fit inside the content area
var screenScalingRatio = builder.VirtualScreen.Size
.ScaleToFitRatio(maxContentSize);
// work out the actual size of the "content area" by scaling the virtual screen
// to fit inside the maximum content area while maintaining its aspect ration.
// we'll also offset it to allow for any margins, borders and padding
var contentBounds = builder.VirtualScreen.Size
.Scale(screenScalingRatio)
.Floor()
.ScaleToFit(maxContentSize, out var scalingRatio)
.Round()
.Clamp(maxContentSize)
.PlaceAt(0, 0)
.Offset(previewStyle.CanvasStyle.MarginStyle.Left, previewStyle.CanvasStyle.MarginStyle.Top)
.Offset(previewStyle.CanvasStyle.BorderStyle.Left, previewStyle.CanvasStyle.BorderStyle.Top)
@@ -82,16 +79,16 @@ public static class LayoutHelper
screen => LayoutHelper.GetBoxBoundsFromOuterBounds(
screen
.Offset(builder.VirtualScreen.Location.ToSize().Invert())
.Scale(screenScalingRatio)
.Scale(scalingRatio)
.Offset(builder.PreviewBounds.ContentBounds.Location.ToSize())
.Truncate(),
.Round(),
previewStyle.ScreenStyle))
.ToList();
return builder.Build();
}
internal static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
public static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
{
return screens.Skip(1).Aggregate(
seed: screens.First(),

View File

@@ -102,7 +102,7 @@ public static class MouseHelper
/// See https://github.com/microsoft/PowerToys/issues/24523
/// https://github.com/microsoft/PowerToys/pull/24527
/// </remarks>
internal static void SimulateMouseMovementEvent(PointInfo location)
private static void SimulateMouseMovementEvent(PointInfo location)
{
var inputs = new User32.INPUT[]
{

View File

@@ -10,49 +10,9 @@ namespace MouseJump.Common.Helpers;
public static class StyleHelper
{
/// <summary>
/// Default v2 preview style
/// Compact (legacy) preview style
/// </summary>
public static readonly PreviewStyle DefaultPreviewStyle = new(
canvasSize: new(
width: 1600,
height: 1200
),
canvasStyle: new(
marginStyle: MarginStyle.Empty,
borderStyle: new(
color: SystemColors.Highlight,
all: 6,
depth: 0
),
paddingStyle: new(
all: 4
),
backgroundStyle: new(
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
)
),
screenStyle: new(
marginStyle: new(
all: 4
),
borderStyle: new(
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
all: 12,
depth: 4
),
paddingStyle: PaddingStyle.Empty,
backgroundStyle: new(
color1: Color.MidnightBlue,
color2: Color.MidnightBlue
)
)
);
/// <summary>
/// Legacy preview style
/// </summary>
public static readonly PreviewStyle LegacyPreviewStyle = new(
public static readonly PreviewStyle CompactPreviewStyle = new(
canvasSize: new(
width: 1600,
height: 1200
@@ -89,6 +49,46 @@ public static class StyleHelper
)
);
/// <summary>
/// Bezelled preview style
/// </summary>
public static readonly PreviewStyle BezelledPreviewStyle = new(
canvasSize: new(
width: 1600,
height: 1200
),
canvasStyle: new(
marginStyle: MarginStyle.Empty,
borderStyle: new(
color: SystemColors.Highlight,
all: 6,
depth: 0
),
paddingStyle: new(
all: 4
),
backgroundStyle: new(
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
)
),
screenStyle: new(
marginStyle: new(
all: 4
),
borderStyle: new(
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
all: 12,
depth: 4
),
paddingStyle: PaddingStyle.Empty,
backgroundStyle: new(
color1: Color.MidnightBlue,
color2: Color.MidnightBlue
)
)
);
public static PreviewStyle WithCanvasSize(this PreviewStyle previewStyle, SizeInfo canvasSize)
{
ArgumentNullException.ThrowIfNull(previewStyle);

View File

@@ -2,6 +2,8 @@
// 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.Drawing.Drawing2D;
using MouseJump.Common.Models.Drawing;
namespace MouseJump.Common.Imaging;
@@ -31,6 +33,11 @@ public sealed class StaticImageRegionCopyService : IImageRegionCopyService
RectangleInfo sourceBounds,
RectangleInfo targetBounds)
{
// prevent the background bleeding through into screen images
// (see https://github.com/mikeclayton/FancyMouse/issues/44)
targetGraphics.PixelOffsetMode = PixelOffsetMode.Half;
targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
targetGraphics.DrawImage(
image: this.SourceImage,
destRect: targetBounds.ToRectangle(),

View File

@@ -203,6 +203,15 @@ public sealed class RectangleInfo
public RectangleInfo Offset(decimal dx, decimal dy) =>
new(this.X + dx, this.Y + dy, this.Width, this.Height);
public RectangleInfo Round() =>
this.Round(0);
public RectangleInfo Round(int decimals) => new(
Math.Round(this.X, decimals),
Math.Round(this.Y, decimals),
Math.Round(this.Width, decimals),
Math.Round(this.Height, decimals));
/// <summary>
/// Returns a new <see cref="RectangleInfo"/> that is a scaled version of the current rectangle.
/// The dimensions of the new rectangle are calculated by multiplying the current rectangle's dimensions by the scaling factor.

View File

@@ -12,6 +12,8 @@ public sealed class ScreenInfo
{
public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
{
// this.Handle is a HMONITOR that has been cast to an int because we don't want
// to expose the HMONITOR type outside the current assembly.
this.Handle = handle;
this.Primary = primary;
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));

View File

@@ -33,6 +33,20 @@ public sealed class SizeInfo
get;
}
public SizeInfo Clamp(SizeInfo max)
{
return new(
width: Math.Clamp(this.Width, 0, max.Width),
height: Math.Clamp(this.Height, 0, max.Height));
}
public SizeInfo Clamp(decimal maxWidth, decimal maxHeight)
{
return new(
width: Math.Clamp(this.Width, 0, maxWidth),
height: Math.Clamp(this.Height, 0, maxHeight));
}
public SizeInfo Enlarge(BorderStyle border) =>
new(
this.Width + border.Horizontal,
@@ -43,6 +57,17 @@ public sealed class SizeInfo
this.Width + padding.Horizontal,
this.Height + padding.Vertical);
/// <summary>
/// Rounds down the width and height of this size to the nearest whole number.
/// </summary>
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
public SizeInfo Floor()
{
return new SizeInfo(
Math.Floor(this.Width),
Math.Floor(this.Height));
}
/// <summary>
/// Calculates the intersection of this size with another size, resulting in a size that represents
/// the overlapping dimensions. Both sizes must be non-negative.
@@ -69,19 +94,6 @@ public sealed class SizeInfo
public SizeInfo Invert() =>
new(-this.Width, -this.Height);
public SizeInfo Scale(decimal scalingFactor) => new(
this.Width * scalingFactor,
this.Height * scalingFactor);
public SizeInfo Shrink(BorderStyle border) =>
new(this.Width - border.Horizontal, this.Height - border.Vertical);
public SizeInfo Shrink(MarginStyle margin) =>
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
public SizeInfo Shrink(PaddingStyle padding) =>
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
/// <summary>
/// Creates a new <see cref="RectangleInfo"/> instance representing a rectangle with this size,
/// positioned at the specified coordinates.
@@ -92,32 +104,39 @@ public sealed class SizeInfo
public RectangleInfo PlaceAt(decimal x, decimal y) =>
new(x, y, this.Width, this.Height);
public SizeInfo Round() =>
this.Round(0);
public SizeInfo Round(int decimals) => new(
Math.Round(this.Width, decimals),
Math.Round(this.Height, decimals));
public SizeInfo Scale(decimal scalingFactor) => new(
this.Width * scalingFactor,
this.Height * scalingFactor);
/// <summary>
/// Scales this size to fit within the bounds of another size, while maintaining the aspect ratio.
/// </summary>
/// <param name="bounds">The size to fit this size into.</param>
/// <returns>A new <see cref="SizeInfo"/> instance representing the scaled size.</returns>
public SizeInfo ScaleToFit(SizeInfo bounds)
public SizeInfo ScaleToFit(SizeInfo bounds, out decimal scalingRatio)
{
var widthRatio = bounds.Width / this.Width;
var heightRatio = bounds.Height / this.Height;
return widthRatio.CompareTo(heightRatio) switch
switch (widthRatio.CompareTo(heightRatio))
{
< 0 => new(bounds.Width, this.Height * widthRatio),
0 => bounds,
> 0 => new(this.Width * heightRatio, bounds.Height),
};
}
/// <summary>
/// Rounds down the width and height of this size to the nearest whole number.
/// </summary>
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
public SizeInfo Floor()
{
return new SizeInfo(
Math.Floor(this.Width),
Math.Floor(this.Height));
case < 0:
scalingRatio = widthRatio;
return new(bounds.Width, this.Height * widthRatio);
case 0:
// widthRatio and heightRatio are the same, so just pick one
scalingRatio = widthRatio;
return bounds;
case > 0:
scalingRatio = heightRatio;
return new(this.Width * heightRatio, bounds.Height);
}
}
/// <summary>
@@ -140,6 +159,15 @@ public sealed class SizeInfo
return scalingRatio;
}
public SizeInfo Shrink(BorderStyle border) =>
new(this.Width - border.Horizontal, this.Height - border.Vertical);
public SizeInfo Shrink(MarginStyle margin) =>
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
public SizeInfo Shrink(PaddingStyle padding) =>
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
public Size ToSize() => new((int)this.Width, (int)this.Height);
public Point ToPoint() => new((int)this.Width, (int)this.Height);

View File

@@ -0,0 +1,12 @@
// 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 MouseJump.Common.Models.Settings;
public enum PreviewType
{
Custom = 0,
Compact = 1,
Bezelled = 2,
}

View File

@@ -9,14 +9,14 @@ namespace MouseJump.Common.Models.Styles;
/// </summary>
public sealed class BorderStyle
{
public static readonly BorderStyle Empty = new(Color.Transparent, 0, 0);
public static readonly BorderStyle Empty = new(null, 0, 0);
public BorderStyle(Color color, decimal all, decimal depth)
public BorderStyle(Color? color, decimal all, decimal depth)
: this(color, all, all, all, all, depth)
{
}
public BorderStyle(Color color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
public BorderStyle(Color? color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
{
this.Color = color;
this.Left = left;
@@ -26,7 +26,7 @@ public sealed class BorderStyle
this.Depth = depth;
}
public Color Color
public Color? Color
{
get;
}

View File

@@ -10,6 +10,10 @@ using System.Threading;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using MouseJump.Common.Helpers;
using MouseJump.Common.Models.Drawing;
using MouseJump.Common.Models.Settings;
using MouseJump.Common.Models.Styles;
namespace MouseJumpUI.Helpers;
@@ -93,4 +97,65 @@ internal sealed class SettingsHelper
{
this.CurrentSettings = this.LoadSettings();
}
public static PreviewStyle GetActivePreviewStyle(MouseJumpSettings settings)
{
var previewType = Enum.TryParse<PreviewType>(settings.Properties.PreviewType, true, out var previewTypeResult)
? previewTypeResult
: PreviewType.Bezelled;
var canvasSize = new SizeInfo(
settings.Properties.ThumbnailSize.Width,
settings.Properties.ThumbnailSize.Height);
var properties = settings.Properties;
var previewStyle = previewType switch
{
PreviewType.Compact => StyleHelper.CompactPreviewStyle.WithCanvasSize(canvasSize),
PreviewType.Bezelled => StyleHelper.BezelledPreviewStyle.WithCanvasSize(canvasSize),
PreviewType.Custom => new PreviewStyle(
canvasSize: canvasSize,
canvasStyle: new(
marginStyle: new(0),
borderStyle: new(
color: ConfigHelper.DeserializeFromConfigColorString(
properties.BorderColor),
all: properties.BorderThickness,
depth: properties.Border3dDepth
),
paddingStyle: new(
all: properties.BorderPadding
),
backgroundStyle: new(
color1: ConfigHelper.DeserializeFromConfigColorString(
properties.BackgroundColor1),
color2: ConfigHelper.DeserializeFromConfigColorString(
properties.BackgroundColor2)
)
),
screenStyle: new(
marginStyle: new(
all: properties.ScreenMargin
),
borderStyle: new(
color: ConfigHelper.DeserializeFromConfigColorString(
properties.BezelColor),
all: properties.BezelThickness,
depth: properties.Bezel3dDepth
),
paddingStyle: new(0),
backgroundStyle: new(
color1: ConfigHelper.DeserializeFromConfigColorString(
properties.ScreenColor1),
color2: ConfigHelper.DeserializeFromConfigColorString(
properties.ScreenColor2)
)
)),
_ => throw new InvalidOperationException(
$"Unhandled {nameof(PreviewType)} '{previewType}'"),
};
return previewStyle;
}
}

View File

@@ -3,13 +3,14 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using System.Windows.Threading;
namespace MouseJumpUI.Helpers;
internal sealed class ThrottledActionInvoker
{
private readonly object _invokerLock = new();
private readonly Lock _invokerLock = new();
private readonly DispatcherTimer _timer;
private Action? _actionToRun;

View File

@@ -183,12 +183,9 @@ internal sealed partial class MainForm : Form
var appSettings = this.SettingsHelper.CurrentSettings ?? throw new InvalidOperationException();
var screens = ScreenHelper.GetAllScreens().Select(screen => screen.DisplayArea).ToList();
var activatedLocation = MouseHelper.GetCursorPosition();
this.PreviewLayout = LayoutHelper.GetPreviewLayout(
previewStyle: StyleHelper.LegacyPreviewStyle.WithCanvasSize(
new(
appSettings.Properties.ThumbnailSize.Width,
appSettings.Properties.ThumbnailSize.Height
)),
previewStyle: SettingsHelper.GetActivePreviewStyle(appSettings),
screens: screens,
activatedLocation: activatedLocation);

View File

@@ -1,60 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Windows.Forms;
namespace MouseJumpUI.Models.Drawing;
/// <summary>
/// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods.
/// </summary>
public sealed class PaddingInfo
{
public PaddingInfo(decimal all)
: this(all, all, all, all)
{
}
public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
public decimal Left
{
get;
}
public decimal Top
{
get;
}
public decimal Right
{
get;
}
public decimal Bottom
{
get;
}
public decimal Horizontal => this.Left + this.Right;
public decimal Vertical => this.Top + this.Bottom;
public override string ToString()
{
return "{" +
$"{nameof(this.Left)}={this.Left}," +
$"{nameof(this.Top)}={this.Top}," +
$"{nameof(this.Right)}={this.Right}," +
$"{nameof(this.Bottom)}={this.Bottom}" +
"}";
}
}

View File

@@ -1,53 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Drawing;
namespace MouseJumpUI.Models.Drawing;
/// <summary>
/// Immutable version of a System.Drawing.Point object with some extra utility methods.
/// </summary>
public sealed class PointInfo
{
public PointInfo(decimal x, decimal y)
{
this.X = x;
this.Y = y;
}
public PointInfo(Point point)
: this(point.X, point.Y)
{
}
public decimal X
{
get;
}
public decimal Y
{
get;
}
public SizeInfo ToSize()
{
return new((int)this.X, (int)this.Y);
}
public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor);
public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y);
public Point ToPoint() => new((int)this.X, (int)this.Y);
public override string ToString()
{
return "{" +
$"{nameof(this.X)}={this.X}," +
$"{nameof(this.Y)}={this.Y}" +
"}";
}
}

View File

@@ -1,131 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
namespace MouseJumpUI.Models.Drawing;
/// <summary>
/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods.
/// </summary>
public sealed class RectangleInfo
{
public RectangleInfo(decimal x, decimal y, decimal width, decimal height)
{
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
public RectangleInfo(Rectangle rectangle)
: this(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)
{
}
public RectangleInfo(Point location, SizeInfo size)
: this(location.X, location.Y, size.Width, size.Height)
{
}
public RectangleInfo(SizeInfo size)
: this(0, 0, size.Width, size.Height)
{
}
public decimal X
{
get;
}
public decimal Y
{
get;
}
public decimal Width
{
get;
}
public decimal Height
{
get;
}
public decimal Left => this.X;
public decimal Top => this.Y;
public decimal Right => this.X + this.Width;
public decimal Bottom => this.Y + this.Height;
public SizeInfo Size => new(this.Width, this.Height);
public PointInfo Location => new(this.X, this.Y);
public decimal Area => this.Width * this.Height;
/// <remarks>
/// Adapted from https://github.com/dotnet/runtime
/// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs
/// </remarks>
public bool Contains(RectangleInfo rect) =>
(this.X <= rect.X) && (rect.X + rect.Width <= this.X + this.Width) &&
(this.Y <= rect.Y) && (rect.Y + rect.Height <= this.Y + this.Height);
public RectangleInfo Enlarge(PaddingInfo padding) => new(
this.X + padding.Left,
this.Y + padding.Top,
this.Width + padding.Horizontal,
this.Height + padding.Vertical);
public RectangleInfo Offset(SizeInfo amount) => this.Offset(amount.Width, amount.Height);
public RectangleInfo Offset(decimal dx, decimal dy) => new(this.X + dx, this.Y + dy, this.Width, this.Height);
public RectangleInfo Scale(decimal scalingFactor) => new(
this.X * scalingFactor,
this.Y * scalingFactor,
this.Width * scalingFactor,
this.Height * scalingFactor);
public RectangleInfo Center(PointInfo point) => new(
x: point.X - (this.Width / 2),
y: point.Y - (this.Height / 2),
width: this.Width,
height: this.Height);
public PointInfo Midpoint => new(
x: this.X + (this.Width / 2),
y: this.Y + (this.Height / 2));
public RectangleInfo Clamp(RectangleInfo outer)
{
if ((this.Width > outer.Width) || (this.Height > outer.Height))
{
throw new ArgumentException($"Value cannot be larger than {nameof(outer)}.");
}
return new(
x: Math.Clamp(this.X, outer.X, outer.Right - this.Width),
y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height),
width: this.Width,
height: this.Height);
}
public Rectangle ToRectangle() => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height);
public override string ToString()
{
return "{" +
$"{nameof(this.Left)}={this.Left}," +
$"{nameof(this.Top)}={this.Top}," +
$"{nameof(this.Width)}={this.Width}," +
$"{nameof(this.Height)}={this.Height}" +
"}";
}
}

View File

@@ -1,87 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
namespace MouseJumpUI.Models.Drawing;
/// <summary>
/// Immutable version of a System.Drawing.Size object with some extra utility methods.
/// </summary>
public sealed class SizeInfo
{
public SizeInfo(decimal width, decimal height)
{
this.Width = width;
this.Height = height;
}
public SizeInfo(Size size)
: this(size.Width, size.Height)
{
}
public decimal Width
{
get;
}
public decimal Height
{
get;
}
public SizeInfo Negate() => new(-this.Width, -this.Height);
public SizeInfo Shrink(PaddingInfo padding) => new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
public SizeInfo Intersect(SizeInfo size) => new(
Math.Min(this.Width, size.Width),
Math.Min(this.Height, size.Height));
public RectangleInfo PlaceAt(decimal x, decimal y) => new(x, y, this.Width, this.Height);
public SizeInfo ScaleToFit(SizeInfo bounds)
{
var widthRatio = bounds.Width / this.Width;
var heightRatio = bounds.Height / this.Height;
return widthRatio.CompareTo(heightRatio) switch
{
< 0 => new(bounds.Width, this.Height * widthRatio),
0 => bounds,
> 0 => new(this.Width * heightRatio, bounds.Height),
};
}
/// <summary>
/// Get the scaling ratio to scale obj by so that it fits inside the specified bounds
/// without distorting the aspect ratio.
/// </summary>
public decimal ScaleToFitRatio(SizeInfo bounds)
{
if (bounds.Width == 0 || bounds.Height == 0)
{
throw new ArgumentException($"{nameof(bounds.Width)} or {nameof(bounds.Height)} cannot be zero", nameof(bounds));
}
var widthRatio = bounds.Width / this.Width;
var heightRatio = bounds.Height / this.Height;
var scalingRatio = Math.Min(widthRatio, heightRatio);
return scalingRatio;
}
public Size ToSize() => new((int)this.Width, (int)this.Height);
public Point ToPoint() => new((int)this.Width, (int)this.Height);
public override string ToString()
{
return "{" +
$"{nameof(this.Width)}={this.Width}," +
$"{nameof(this.Height)}={this.Height}" +
"}";
}
}

View File

@@ -1,123 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using MouseJumpUI.Models.Drawing;
using MouseJumpUI.Models.Screen;
namespace MouseJumpUI.Models.Layout;
/// <summary>
/// Represents a collection of values needed for calculating the MainForm layout.
/// </summary>
public sealed class LayoutConfig
{
public LayoutConfig(
RectangleInfo virtualScreenBounds,
List<ScreenInfo> screens,
PointInfo activatedLocation,
int activatedScreenIndex,
int activatedScreenNumber,
SizeInfo maximumFormSize,
PaddingInfo formPadding,
PaddingInfo previewPadding)
{
// make sure the virtual screen entirely contains all of the individual screen bounds
ArgumentNullException.ThrowIfNull(virtualScreenBounds);
ArgumentNullException.ThrowIfNull(screens);
if (screens.Any(screen => !virtualScreenBounds.Contains(screen.Bounds)))
{
throw new ArgumentException($"'{nameof(virtualScreenBounds)}' must contain all of the screens in '{nameof(screens)}'", nameof(virtualScreenBounds));
}
this.VirtualScreenBounds = virtualScreenBounds;
this.Screens = new(screens.ToList());
this.ActivatedLocation = activatedLocation;
this.ActivatedScreenIndex = activatedScreenIndex;
this.ActivatedScreenNumber = activatedScreenNumber;
this.MaximumFormSize = maximumFormSize;
this.FormPadding = formPadding;
this.PreviewPadding = previewPadding;
}
/// <summary>
/// Gets the coordinates of the entire virtual screen.
/// </summary>
/// <remarks>
/// The Virtual Screen is the bounding rectangle of all the monitors.
/// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen
/// </remarks>
public RectangleInfo VirtualScreenBounds
{
get;
}
/// <summary>
/// Gets a collection containing the individual screens connected to the system.
/// </summary>
public ReadOnlyCollection<ScreenInfo> Screens
{
get;
}
/// <summary>
/// Gets the point where the cursor was located when the form was activated.
/// </summary>
/// <summary>
/// The preview form will be centered on this location unless there are any
/// constraints such as being too close to edge of a screen, in which case
/// the form will be displayed centered as close as possible to this location.
/// </summary>
public PointInfo ActivatedLocation
{
get;
}
/// <summary>
/// Gets the index of the screen the cursor was on when the form was activated.
/// The value is an index into the ScreenBounds array and is 0-indexed as a result.
/// </summary>
public int ActivatedScreenIndex
{
get;
}
/// <summary>
/// Gets the screen number the cursor was on when the form was activated.
/// The value matches the screen numbering scheme in the "Display Settings" dialog
/// and is 1-indexed as a result.
/// </summary>
public int ActivatedScreenNumber
{
get;
}
/// <summary>
/// Gets the maximum size of the screen preview form.
/// </summary>
public SizeInfo MaximumFormSize
{
get;
}
/// <summary>
/// Gets the padding border around the screen preview form.
/// </summary>
public PaddingInfo FormPadding
{
get;
}
/// <summary>
/// Gets the padding border inside the screen preview image.
/// </summary>
public PaddingInfo PreviewPadding
{
get;
}
}

View File

@@ -1,113 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using MouseJumpUI.Models.Drawing;
namespace MouseJumpUI.Models.Layout;
public sealed class LayoutInfo
{
public sealed class Builder
{
public Builder()
{
this.ScreenBounds = new();
}
public LayoutConfig? LayoutConfig
{
get;
set;
}
public RectangleInfo? FormBounds
{
get;
set;
}
public RectangleInfo? PreviewBounds
{
get;
set;
}
public List<RectangleInfo> ScreenBounds
{
get;
set;
}
public RectangleInfo? ActivatedScreenBounds
{
get;
set;
}
public LayoutInfo Build()
{
return new LayoutInfo(
layoutConfig: this.LayoutConfig ?? throw new InvalidOperationException(),
formBounds: this.FormBounds ?? throw new InvalidOperationException(),
previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(),
screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(),
activatedScreenBounds: this.ActivatedScreenBounds ?? throw new InvalidOperationException());
}
}
public LayoutInfo(
LayoutConfig layoutConfig,
RectangleInfo formBounds,
RectangleInfo previewBounds,
IEnumerable<RectangleInfo> screenBounds,
RectangleInfo activatedScreenBounds)
{
this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig));
this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds));
this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds));
this.ScreenBounds = new(
(screenBounds ?? throw new ArgumentNullException(nameof(screenBounds)))
.ToList());
this.ActivatedScreenBounds = activatedScreenBounds ?? throw new ArgumentNullException(nameof(activatedScreenBounds));
}
/// <summary>
/// Gets the original LayoutConfig settings used to calculate coordinates.
/// </summary>
public LayoutConfig LayoutConfig
{
get;
}
/// <summary>
/// Gets the size and location of the preview form.
/// </summary>
public RectangleInfo FormBounds
{
get;
}
/// <summary>
/// Gets the size and location of the preview image.
/// </summary>
public RectangleInfo PreviewBounds
{
get;
}
public ReadOnlyCollection<RectangleInfo> ScreenBounds
{
get;
}
public RectangleInfo ActivatedScreenBounds
{
get;
}
}

View File

@@ -1,47 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using MouseJumpUI.Models.Drawing;
namespace MouseJumpUI.Models.Screen;
/// <summary>
/// Immutable version of a System.Windows.Forms.Screen object so we don't need to
/// take a dependency on WinForms just for screen info.
/// </summary>
public sealed class ScreenInfo
{
internal ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
{
this.Handle = handle;
this.Primary = primary;
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));
this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea));
}
public int Handle
{
get;
}
public bool Primary
{
get;
}
public RectangleInfo DisplayArea
{
get;
}
public RectangleInfo Bounds =>
this.DisplayArea;
public RectangleInfo WorkingArea
{
get;
}
}

View File

@@ -3,9 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Threading;
@@ -97,29 +95,4 @@ internal static class Program
cancellationTokenSource.Cancel();
Application.Exit();
}
private static MouseJumpSettings ReadSettings()
{
var settingsUtils = new SettingsUtils();
var settingsPath = settingsUtils.GetSettingsFilePath(MouseJumpSettings.ModuleName);
if (!File.Exists(settingsPath))
{
var scaffoldSettings = new MouseJumpSettings();
settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), MouseJumpSettings.ModuleName);
}
var settings = new MouseJumpSettings();
try
{
settings = settingsUtils.GetSettings<MouseJumpSettings>(MouseJumpSettings.ModuleName);
}
catch (Exception ex)
{
var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}";
Logger.LogInfo(errorMessage);
Logger.LogDebug(errorMessage);
}
return settings;
}
}

View File

@@ -368,7 +368,7 @@ namespace MouseWithoutBorders
}
}
private static readonly object ClipboardThreadOldLock = new();
private static readonly Lock ClipboardThreadOldLock = new();
private static System.Threading.Thread clipboardThreadOld;
internal static void GetRemoteClipboard(string postAction)

View File

@@ -32,7 +32,7 @@ namespace MouseWithoutBorders
{
public class Thread
{
private static readonly object ThreadsLock = new();
private static readonly Lock ThreadsLock = new();
private static List<System.Threading.Thread> threads;
private readonly System.Threading.Thread thread;

View File

@@ -43,7 +43,7 @@ namespace MouseWithoutBorders
internal partial class Common
{
private static readonly object McMatrixLock = new();
private static readonly Lock McMatrixLock = new();
internal const byte MAX_MACHINE = 4;
internal const byte MAX_SOCKET = MAX_MACHINE * 2;

View File

@@ -474,7 +474,7 @@ namespace MouseWithoutBorders
}
}
private static readonly object InputSimulationLock = new();
private static readonly Lock InputSimulationLock = new();
internal static void DoSomethingInTheInputSimulationThread(ThreadStart target)
{

View File

@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace MouseWithoutBorders.Class
{
@@ -36,12 +37,12 @@ namespace MouseWithoutBorders.Class
/// </remarks>
internal class MachinePool
{
private readonly object @lock;
private readonly Lock @lock;
private readonly List<MachineInf> list;
public MachinePool()
{
@lock = new object();
@lock = new Lock();
list = new List<MachineInf>();
}

View File

@@ -13,6 +13,7 @@ using System.IO.Abstractions;
using System.Linq;
using System.Security.Cryptography;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -44,7 +45,7 @@ namespace MouseWithoutBorders.Class
internal bool Changed;
private readonly SettingsUtils _settingsUtils;
private readonly object _loadingSettingsLock = new object();
private readonly Lock _loadingSettingsLock = new Lock();
private readonly IFileSystemWatcher _watcher;
private MouseWithoutBordersProperties _properties;

View File

@@ -826,7 +826,7 @@ namespace MouseWithoutBorders.Class
}
private static readonly Dictionary<string, List<IPAddress>> BadIPs = new();
private static readonly object BadIPsLock = new();
private static readonly Lock BadIPsLock = new();
private static bool IsBadIP(string machineName, IPAddress ip)
{

View File

@@ -19,6 +19,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The thickness of the border around the field")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int BorderSize
{
get => _borderSize;
@@ -33,6 +34,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The color of the border around the field")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color BorderColor
{
get => _borderColor;
@@ -47,6 +49,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The color of the border around the field when it has focus")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Color FocusColor
{
get => _focusColor;
@@ -59,12 +62,14 @@ namespace MouseWithoutBorders
[Category("Behavior")]
[Description("The maximum number of characters that can be typed in the field")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int MaximumLength
{
get => InnerField.MaxLength;
set => InnerField.MaxLength = value;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get => InnerField.Text;

View File

@@ -19,12 +19,14 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when Mouse is pressed on button")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image DownImage { get; set; }
private Image _normalImage;
[Category("Appearance")]
[Description("Image to show when button is in normal state")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image NormalImage
{
get => _normalImage;
@@ -37,10 +39,12 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when Mouse hovers over button")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image HoverImage { get; set; }
[Category("Appearance")]
[Description("Image to show when button is disabled")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image DisabledImage { get; set; }
private bool _hovering;

View File

@@ -16,6 +16,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("The bounding rectangle of the check image in local co-ordinates")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Point ImageLocation
{
get => _imageLocation;
@@ -28,6 +29,7 @@ namespace MouseWithoutBorders
private Point _textLocation;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Point TextLocation
{
get => _textLocation;
@@ -47,6 +49,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when Mouse is pressed on button")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image CheckedImage
{
get => _checkedImage;
@@ -61,6 +64,7 @@ namespace MouseWithoutBorders
[Category("Appearance")]
[Description("Image to show when button is in normal state")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Image UncheckedImage
{
get => _uncheckedImage;

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Windows.Forms;
// <summary>
@@ -34,12 +35,14 @@ namespace MouseWithoutBorders
MachineEnabled = false;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal string MachineName
{
get => textBoxName.Text;
set => textBoxName.Text = value;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal bool MachineEnabled
{
get => checkBoxEnabled.Checked;
@@ -52,6 +55,7 @@ namespace MouseWithoutBorders
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal bool Editable
{
set => textBoxName.Enabled = value;
@@ -59,6 +63,7 @@ namespace MouseWithoutBorders
// get { return textBoxName.Enabled; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal bool CheckAble
{
set
@@ -73,6 +78,7 @@ namespace MouseWithoutBorders
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
internal bool LocalHost
{
// get { return localhost; }
@@ -165,6 +171,7 @@ namespace MouseWithoutBorders
return rv;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal SocketStatus StatusClient
{
get => statusClient;
@@ -182,6 +189,7 @@ namespace MouseWithoutBorders
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal SocketStatus StatusServer
{
get => statusServer;

View File

@@ -12,6 +12,7 @@ using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
// <summary>
@@ -30,7 +31,7 @@ namespace MouseWithoutBorders.Core;
internal static class Logger
{
internal static readonly string[] AllLogs = new string[MAX_LOG];
private static readonly object AllLogsLock = new();
private static readonly Lock AllLogsLock = new();
internal static readonly ConcurrentDictionary<string, int> LogCounter = new();
private const int MAX_LOG = 10000;
private static int allLogsIndex;

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
@@ -32,6 +33,7 @@ namespace MouseWithoutBorders
private int _animationFrame;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool ReturnToSettings { get; set; }
public SetupPage3a()

View File

@@ -58,12 +58,16 @@ namespace MouseWithoutBorders
private Timer helperTimer;
#pragma warning restore CA2213
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal int CurIcon { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal NotifyIcon NotifyIcon { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal System.Windows.Forms.ToolStripMenuItem MenuAllPC { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
internal System.Windows.Forms.ContextMenuStrip MainMenu { get; set; }
internal FrmScreen()

View File

@@ -19,7 +19,7 @@ namespace MouseWithoutBorders
public partial class FormHelper : System.Windows.Forms.Form
{
private readonly List<FocusArea> focusZone = new();
private readonly object bmScreenLock = new();
private readonly Lock bmScreenLock = new();
private long lastClipboardEventTime;
private IClipboardHelper remoteClipboardHelper;

View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h new.base.rc new.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{0db0f63a-d2f8-4da3-a650-2d0b8724218e}</ProjectGuid>
<RootNamespace>NewPlusShellExtensionWin10</RootNamespace>
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</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>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
<TargetName>PowerToys.NewPlus.ShellExtension.win10</TargetName>
<LinkIncremental />
<IgnoreImportLibrary />
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h" />
<ClInclude Include="dll_main.h" />
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="shell_context_menu_win10.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp" />
<ClCompile Include="dll_main.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="shell_context_menu_win10.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\new.rc" />
<None Include="new.base.rc" />
</ItemGroup>
<ItemGroup>
<None Include="resources.resx">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="dll.def" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{4cea4fff-ccef-4b62-9e46-f33da2b9a0cc}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shell_context_menu_win10.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dll_main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dll_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shell_context_menu_win10.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\new.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="dll.def">
<Filter>Source Files</Filter>
</None>
<None Include="new.base.rc">
<Filter>Resource Files</Filter>
</None>
<None Include="resources.resx">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
LIBRARY
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllGetActivationFactory PRIVATE

View File

@@ -0,0 +1,44 @@
#include "pch.h"
#include "shell_context_menu_win10.h"
#include "dll_main.h"
#include "trace.h"
#include <common/Telemetry/EtwTrace/EtwTrace.h>
HMODULE module_instance_handle = 0;
Shared::Trace::ETWTrace trace(L"NewPlusShellExtension_Win10");
BOOL APIENTRY DllMain(HMODULE module_handle, DWORD ul_reason_for_call, LPVOID reserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
module_instance_handle = module_handle;
Trace::RegisterProvider();
newplus::utilities::init_logger();
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
{
return Module<ModuleType::InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
}
STDAPI DllCanUnloadNow()
{
return Module<InProc>::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE;
}
STDAPI DllGetClassObject(_In_ REFCLSID ref_class_id, _In_ REFIID ref_interface_id, _Outptr_ LPVOID FAR* object)
{
return Module<InProc>::GetModule().GetClassObject(ref_class_id, ref_interface_id, object);
}
CoCreatableClass(shell_context_menu_win10)

View File

@@ -0,0 +1,6 @@
#pragma once
#include <common/Telemetry/EtwTrace/EtwTrace.h>
extern HMODULE module_instance_handle;
extern Shared::Trace::ETWTrace trace;

View File

@@ -0,0 +1,54 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -0,0 +1,3 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"

View File

@@ -0,0 +1,39 @@
// Precompiled header file.
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOHELP
#define NOCOMM
// Windows and STL
#include <Shobjidl.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <Windows.h>
#include <shlobj.h>
#include <vector>
#include <system_error>
#include <memory>
#include <iostream>
#include <atlbase.h>
#include <wrl.h>
#include <wrl/module.h>
#include <wrl/client.h>
#include <unknwn.h>
using namespace Microsoft::WRL;
// PowerToys project common
#include <ProjectTelemetry.h>
#include <common/utils/resources.h>
#include <common/logger/logger.h>
#include <common/logger/logger_settings.h>
#include <common/utils/logger_helper.h>
#include <common/Themes/theme_helpers.h>
// New project specific
#include "dll_main.h"
#include "template_folder.h"
#include "settings.h"
#include "new_utilities.h"

View File

@@ -0,0 +1,12 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys.New+"
#define INTERNAL_NAME "PowerToys.New+"
#define ORIGINAL_FILENAME "PowerToys.NewPlus.ShellExtension.win10.dll"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,132 @@
<?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="context_menu_item_new" xml:space="preserve">
<value>New+</value>
<comment>The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+</comment>
</data>
<data name="context_menu_item_open_templates" xml:space="preserve">
<value>Open templates</value>
<comment>The menu item in the context menu that enables user to open the folder that contains their templates.</comment>
</data>
<data name="default_template_sub_folder_name_where_templates_are_stored" xml:space="preserve">
<value>Templates</value>
<comment>Default subfolder name where templates are stored.</comment>
</data>
</root>

View File

@@ -0,0 +1,270 @@
#include "pch.h"
#include "shell_context_menu_win10.h"
#include "shell_context_sub_menu.h"
#include "shell_context_sub_menu_item.h"
#include "new_utilities.h"
#include "settings.h"
#include "trace.h"
#include "Generated Files/resource.h"
#include <common/Themes/icon_helpers.h>
#include <common/utils/winapi_error.h>
using namespace Microsoft::WRL;
using namespace newplus;
shell_context_menu_win10::~shell_context_menu_win10()
{
for (const auto& handle : bitmap_handles)
{
DeleteObject(handle);
}
}
#pragma region IShellExtInit
IFACEMETHODIMP shell_context_menu_win10::Initialize(PCIDLIST_ABSOLUTE, IDataObject*, HKEY)
{
return S_OK;
}
#pragma endregion
#pragma region IContextMenu
IFACEMETHODIMP shell_context_menu_win10::QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags)
{
if (!NewSettingsInstance().GetEnabled()
|| package::IsWin11OrGreater()
)
{
return E_FAIL;
}
if (menu_flags & CMF_DEFAULTONLY)
{
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
}
try
{
// Create the initial context popup menu containing the list of templates and open templates action
int menu_id = menu_first_cmd_id;
MENUITEMINFO newplus_main_context_menu_item;
HMENU sub_menu_of_templates = CreatePopupMenu();
int sub_menu_index = 0;
// Determine the New+ Template folder location
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
// Create the New+ Template folder location if it doesn't exist (very rare scenario)
utilities::create_folder_if_not_exist(template_folder_root);
// Scan the folder for any files and folders (the templates)
templates = new template_folder(template_folder_root);
templates->rescan_template_folder();
const auto number_of_templates = templates->list_of_templates.size();
// Create the New+ menu item and point to the initial context popup menu
static const std::wstring localized_context_menu_item =
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+");
wchar_t newplus_menu_name[20] = { 0 };
wcscpy_s(newplus_menu_name, ARRAYSIZE(newplus_menu_name), localized_context_menu_item.c_str());
newplus_main_context_menu_item.cbSize = sizeof(MENUITEMINFOW);
newplus_main_context_menu_item.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU;
newplus_main_context_menu_item.wID = menu_id;
newplus_main_context_menu_item.fType = MFT_STRING;
newplus_main_context_menu_item.dwTypeData = (PWSTR)newplus_menu_name;
newplus_main_context_menu_item.hSubMenu = sub_menu_of_templates;
const auto newplus_icon_index = 0;
if (bitmap_handles.size() == 0)
{
const std::wstring icon_file = utilities::get_new_icon_resource_filepath(
module_instance_handle, ThemeHelpers::GetAppTheme())
.c_str();
HICON local_icon_handle = static_cast<HICON>(
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
if (local_icon_handle)
{
bitmap_handles.push_back(CreateBitmapFromIcon(local_icon_handle));
DestroyIcon(local_icon_handle);
}
}
if (bitmap_handles.size() > newplus_icon_index && bitmap_handles[newplus_icon_index])
{
newplus_main_context_menu_item.fMask |= MIIM_BITMAP;
newplus_main_context_menu_item.hbmpItem = bitmap_handles[newplus_icon_index];
}
menu_id++;
// Add template items to context menu
int index = 0;
for (; index < number_of_templates; index++)
{
const auto template_item = templates->get_template_item(index);
add_template_item_to_context_menu(sub_menu_of_templates, sub_menu_index, template_item, menu_id, index);
menu_id++;
sub_menu_index++;
}
// Add separator to context menu
add_separator_to_context_menu(sub_menu_of_templates, sub_menu_index);
sub_menu_index++;
// Add "Open templates" item to context menu
add_open_templates_to_context_menu(sub_menu_of_templates, sub_menu_index, template_folder_root, menu_id, index);
menu_id++;
if (!InsertMenuItem(menu_handle, menu_index, TRUE, &newplus_main_context_menu_item))
{
Logger::error(L"QueryContextMenu() failed. {}", get_last_error_or_default(GetLastError()));
return HRESULT_FROM_WIN32(GetLastError());
}
else
{
// Return the amount if entries inserted
const auto number_of_items_inserted = menu_id - menu_first_cmd_id;
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, number_of_items_inserted);
}
}
catch (const std::exception& ex)
{
Logger::error(ex.what());
}
return E_FAIL;
}
void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index)
{
static const std::wstring localized_context_menu_item_open_templates =
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_OPEN_TEMPLATES, L"Open templates");
wchar_t menu_name_open[256] = { 0 };
wcscpy_s(menu_name_open, ARRAYSIZE(menu_name_open), localized_context_menu_item_open_templates.c_str());
const auto open_folder_item = Make<template_folder_context_menu_item>(template_folder_root);
MENUITEMINFO newplus_menu_item_open_templates;
newplus_menu_item_open_templates.cbSize = sizeof(MENUITEMINFO);
newplus_menu_item_open_templates.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
newplus_menu_item_open_templates.wID = menu_id;
newplus_menu_item_open_templates.fType = MFT_STRING;
newplus_menu_item_open_templates.dwTypeData = (PWSTR)menu_name_open;
const auto open_templates_icon_index = index + 1;
if (bitmap_handles.size() <= open_templates_icon_index)
{
const std::wstring icon_file = utilities::get_open_templates_icon_resource_filepath(
module_instance_handle, ThemeHelpers::GetAppTheme())
.c_str();
HICON open_template_icon_handle = static_cast<HICON>(
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
if (open_template_icon_handle)
{
bitmap_handles.push_back(CreateBitmapFromIcon(open_template_icon_handle));
DestroyIcon(open_template_icon_handle);
}
}
if (bitmap_handles.size() > open_templates_icon_index && bitmap_handles[open_templates_icon_index])
{
newplus_menu_item_open_templates.fMask |= MIIM_BITMAP;
newplus_menu_item_open_templates.hbmpItem = bitmap_handles[open_templates_icon_index];
}
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_open_templates);
}
void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index)
{
MENUITEMINFO menu_item_separator;
menu_item_separator.cbSize = sizeof(MENUITEMINFO);
menu_item_separator.fMask = MIIM_FTYPE;
menu_item_separator.fType = MFT_SEPARATOR;
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &menu_item_separator);
}
void shell_context_menu_win10::add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index)
{
wchar_t menu_name[256] = { 0 };
wcscpy_s(menu_name, ARRAYSIZE(menu_name), template_item->get_menu_title(!utilities::get_newplus_setting_hide_extension(), !utilities::get_newplus_setting_hide_starting_digits()).c_str());
MENUITEMINFO newplus_menu_item_template;
newplus_menu_item_template.cbSize = sizeof(MENUITEMINFO);
newplus_menu_item_template.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_DATA;
newplus_menu_item_template.wID = menu_id;
newplus_menu_item_template.fType = MFT_STRING;
newplus_menu_item_template.dwTypeData = (PWSTR)menu_name;
const auto current_template_icon_index = index + 1;
if (bitmap_handles.size() <= current_template_icon_index)
{
HICON template_icon_handle = template_item->get_explorer_icon_handle();
if (template_icon_handle)
{
bitmap_handles.push_back(CreateBitmapFromIcon(template_icon_handle));
DestroyIcon(template_icon_handle);
}
}
if (bitmap_handles.size() > current_template_icon_index && bitmap_handles[current_template_icon_index])
{
newplus_menu_item_template.fMask |= MIIM_BITMAP;
newplus_menu_item_template.hbmpItem = bitmap_handles[current_template_icon_index];
}
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_template);
}
IFACEMETHODIMP shell_context_menu_win10::InvokeCommand(CMINVOKECOMMANDINFO* params)
{
if (!params)
{
return E_FAIL;
}
// Get selected menu item (a template or the "Open templates" item)
const auto selected_menu_item_index = LOWORD(params->lpVerb) - 1;
if (selected_menu_item_index < 0)
{
return E_FAIL;
}
const auto number_of_templates = templates->list_of_templates.size();
const bool is_template_item = selected_menu_item_index < number_of_templates;
// Save how many item templates we have so it can be sent later when we do something with New+.
// It will be sent when the user does something, similar to Windows 11 context menu.
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
if (is_template_item)
{
// It's a template menu item
const auto template_entry = templates->get_template_item(selected_menu_item_index);
return newplus::utilities::copy_template(template_entry, site_of_folder);
}
else
{
// It's the "Open templates" menu item
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
return newplus::utilities::open_template_folder(template_folder_root);
}
return E_FAIL;
}
IFACEMETHODIMP shell_context_menu_win10::GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP shell_context_menu_win10::SetSite(_In_ IUnknown* site) noexcept
{
this->site_of_folder = site;
return S_OK;
}
IFACEMETHODIMP shell_context_menu_win10::GetSite(_In_ REFIID riid, _COM_Outptr_ void** returned_site) noexcept
{
return this->site_of_folder.CopyTo(riid, returned_site);
}
#pragma endregion

View File

@@ -0,0 +1,45 @@
#pragma once
#include "pch.h"
#include <template_folder.h>
using namespace Microsoft::WRL;
#define NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID "FF90D477-E32A-4BE8-8CC5-A502A97F5401"
// File Explorer context menu "New+" for Windows 10
class __declspec(uuid(NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID)) shell_context_menu_win10 :
public RuntimeClass<
RuntimeClassFlags<ClassicCom>,
IShellExtInit,
IContextMenu,
IObjectWithSite>
{
public:
~shell_context_menu_win10();
#pragma region IShellExtInit
IFACEMETHODIMP Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject*, HKEY);
#pragma endregion
#pragma region IContextMenu
IFACEMETHODIMP QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags);
IFACEMETHODIMP InvokeCommand(CMINVOKECOMMANDINFO* pici);
IFACEMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT);
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
#pragma endregion
protected:
void add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index);
void add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index);
void add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index);
HINSTANCE instance_handle = 0;
ComPtr<IUnknown> site_of_folder;
newplus::template_folder* templates = nullptr;
std::vector<HBITMAP> bitmap_handles;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 186 KiB

View File

@@ -128,6 +128,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
<ClInclude Include="template_item.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="new_utilities.cpp" />
<ClCompile Include="shell_context_menu.cpp" />
<ClCompile Include="shell_context_sub_menu.cpp" />
<ClCompile Include="shell_context_sub_menu_item.cpp" />
@@ -227,7 +228,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>

View File

@@ -31,6 +31,9 @@
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="new_utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="template_folder.h">
@@ -123,9 +126,6 @@
<None Include="AppxManifest.xml">
<Filter>Source Files</Filter>
</None>
<None Include="Assets\NewPlus\New.ico">
<Filter>Asset Files</Filter>
</None>
<None Include="Assets\NewPlus\SmallTile.png">
<Filter>Asset Files</Filter>
</None>
@@ -190,14 +190,8 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Text Include="TemplateExamples\Example folder\Another example txt file.txt">
<Filter>Template Examples\Example folder</Filter>
</Text>
<Text Include="TemplateExamples\Example folder\Example txt file.txt">
<Filter>Template Examples\Example folder</Filter>
</Text>
<Text Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt">
<Filter>Template Examples</Filter>
</Text>
<CopyFileToFolders Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt" />
<CopyFileToFolders Include="TemplateExamples\Example folder\Example txt file.txt" />
<CopyFileToFolders Include="TemplateExamples\Example folder\Another example txt file.txt" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
#include "pch.h"
#include "new_utilities.h"
// HACK: Store number of templates when generating the menu entries to send later.
size_t saved_number_of_templates = -1;
size_t newplus::utilities::get_saved_number_of_templates()
{
return saved_number_of_templates;
}
void newplus::utilities::set_saved_number_of_templates(size_t templates)
{
saved_number_of_templates = templates;
}

View File

@@ -7,11 +7,17 @@
#include "constants.h"
#include "settings.h"
#include "template_item.h"
#include "trace.h"
#pragma comment(lib, "Shlwapi.lib")
using namespace newplus;
namespace newplus::utilities
{
size_t get_saved_number_of_templates();
void set_saved_number_of_templates(size_t templates);
inline std::wstring get_explorer_icon(std::filesystem::path path)
{
@@ -39,6 +45,33 @@ namespace newplus::utilities
return icon_resource;
}
inline HICON get_explorer_icon_handle(std::filesystem::path path)
{
SHFILEINFO shell_file_info = { 0 };
const std::wstring filepath = path.wstring();
DWORD_PTR result = SHGetFileInfo(filepath.c_str(), 0, &shell_file_info, sizeof(shell_file_info), SHGFI_ICON);
if (shell_file_info.hIcon)
{
return shell_file_info.hIcon;
}
WCHAR icon_resource_specifier[MAX_PATH] = { 0 };
DWORD buffer_length = MAX_PATH;
const std::wstring extension = path.extension().wstring();
const HRESULT hr = AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN,
ASSOCSTR_DEFAULTICON,
extension.c_str(),
NULL,
icon_resource_specifier,
&buffer_length);
const std::wstring icon_resource = icon_resource_specifier;
const auto icon_x = GetSystemMetrics(SM_CXSMICON);
const auto icon_y = GetSystemMetrics(SM_CYSMICON);
HICON hIcon = static_cast<HICON>(LoadImage(NULL, icon_resource.c_str(), IMAGE_ICON, icon_x, icon_y, LR_LOADFROMFILE));
return hIcon;
}
inline bool is_hidden(const std::filesystem::path path)
{
const std::filesystem::path::string_type name = path.filename();
@@ -180,4 +213,227 @@ namespace newplus::utilities
return path;
}
inline bool is_desktop_folder(const std::filesystem::path target_fullpath)
{
TCHAR desktopPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)))
{
return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0;
}
return false;
}
inline void explorer_enter_rename_mode(const std::filesystem::path target_fullpath_of_new_instance)
{
const std::filesystem::path path_without_new_file_or_dir = target_fullpath_of_new_instance.parent_path();
const std::filesystem::path new_file_or_dir_without_path = target_fullpath_of_new_instance.filename();
ComPtr<IShellWindows> shell_windows;
HRESULT hr;
if (FAILED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_windows))))
{
return;
}
long window_handle;
ComPtr<IDispatch> shell_window;
const bool object_created_on_desktop = is_desktop_folder(path_without_new_file_or_dir.c_str());
if (object_created_on_desktop)
{
// Special handling for desktop folder
VARIANT empty_yet_needed_incl_init;
VariantInit(&empty_yet_needed_incl_init);
if (FAILED(shell_windows->FindWindowSW(&empty_yet_needed_incl_init, &empty_yet_needed_incl_init, SWC_DESKTOP, &window_handle, SWFO_NEEDDISPATCH, &shell_window)))
{
return;
}
}
else
{
long count_of_shell_windows = 0;
shell_windows->get_Count(&count_of_shell_windows);
for (long i = 0; i < count_of_shell_windows; ++i)
{
ComPtr<IWebBrowserApp> web_browser_app;
VARIANT v;
V_VT(&v) = VT_I4;
V_I4(&v) = i;
hr = shell_windows->Item(v, &shell_window);
if (SUCCEEDED(hr) && shell_window)
{
hr = shell_window.As(&web_browser_app);
if (SUCCEEDED(hr))
{
BSTR folder_view_location;
hr = web_browser_app->get_LocationURL(&folder_view_location);
if (SUCCEEDED(hr) && folder_view_location)
{
wchar_t path[MAX_PATH];
DWORD pathLength = ARRAYSIZE(path);
hr = PathCreateFromUrl(folder_view_location, path, &pathLength, 0);
SysFreeString(folder_view_location);
if (SUCCEEDED(hr) && StrCmpIW(path_without_new_file_or_dir.c_str(), path) == 0)
{
break;
}
}
}
}
shell_window = nullptr;
}
}
if (!shell_window)
{
return;
}
ComPtr<IServiceProvider> service_provider;
shell_window.As(&service_provider);
ComPtr<IShellBrowser> shell_browser;
service_provider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shell_browser));
ComPtr<IShellView> shell_view;
shell_browser->QueryActiveShellView(&shell_view);
ComPtr<IFolderView> folder_view;
shell_view.As(&folder_view);
// Find the newly created object (file or folder)
// And put object into edit mode (SVSI_EDIT) and if desktop also reposition
int number_of_objects_in_view = 0;
bool done = false;
folder_view->ItemCount(SVGIO_ALLVIEW, &number_of_objects_in_view);
for (int i = 0; i < number_of_objects_in_view && !done; ++i)
{
std::wstring path_of_item(MAX_PATH, 0);
LPITEMIDLIST shell_item_ids;
folder_view->Item(i, &shell_item_ids);
SHGetPathFromIDList(shell_item_ids, &path_of_item[0]);
const std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
if (utilities::wstring_same_when_comparing_ignore_case(new_file_or_dir_without_path, current_filename))
{
const DWORD common_select_flags = SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED;
if (object_created_on_desktop)
{
// Newly created object is on the desktop -- reposition under mouse and enter rename mode
LPCITEMIDLIST shell_item_to_select_and_position[] = { shell_item_ids };
POINT mouse_position;
GetCursorPos(&mouse_position);
mouse_position.x -= GetSystemMetrics(SM_CXMENUSIZE);
mouse_position.x = max(mouse_position.x, 20);
mouse_position.y -= GetSystemMetrics(SM_CXMENUSIZE)/2;
mouse_position.y = max(mouse_position.y, 20);
POINT position[] = { mouse_position };
folder_view->SelectAndPositionItems(1, shell_item_to_select_and_position, position, common_select_flags | SVSI_POSITIONITEM);
}
else
{
// Enter rename mode
folder_view->SelectItem(i, common_select_flags);
}
done = true;
}
CoTaskMemFree(shell_item_ids);
}
}
inline HRESULT copy_template(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder)
{
HRESULT hr = S_OK;
try
{
Logger::info(L"Copying template");
if (newplus::utilities::get_saved_number_of_templates() >= 0)
{
// Log that context menu was shown and with how many items
trace.UpdateState(true);
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
trace.Flush();
trace.UpdateState(false);
}
// Determine target path of where context menu was displayed
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
// Determine initial filename
std::filesystem::path source_fullpath = template_entry->path;
std::filesystem::path target_fullpath = std::wstring(target_path_name);
// Only append name to target if source is not a directory
if (!utilities::is_directory(source_fullpath))
{
target_fullpath.append(template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
}
// Copy file and determine final filename
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
trace.UpdateState(true);
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
trace.Flush();
trace.UpdateState(false);
// Refresh folder items
template_entry->refresh_target(target_final_fullpath);
// Enter rename mode
template_entry->enter_rename_mode(target_final_fullpath);
}
catch (const std::exception& ex)
{
Logger::error(ex.what());
hr = S_FALSE;
}
trace.UpdateState(true);
Trace::EventCopyTemplateResult(hr);
trace.Flush();
trace.UpdateState(false);
return hr;
}
inline HRESULT open_template_folder(const std::filesystem::path template_folder)
{
HRESULT hr = S_OK;
try
{
Logger::info(L"Open templates folder");
if (newplus::utilities::get_saved_number_of_templates() >= 0)
{
// Log that context menu was shown and with how many items
trace.UpdateState(true);
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
trace.Flush();
trace.UpdateState(false);
}
const std::wstring verb_hardcoded_do_not_change = L"open";
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
trace.UpdateState(true);
Trace::EventOpenTemplates();
trace.Flush();
trace.UpdateState(false);
}
catch (const std::exception& ex)
{
Logger::error(ex.what());
hr = S_FALSE;
}
return hr;
}
}

View File

@@ -27,7 +27,6 @@ public:
#pragma region IObjectWithSite
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
#pragma endregion

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