diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 7b3543e06f56..0ebacd1b41bb 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -116,6 +116,7 @@ talynone TBM tilovell Triet +waaverecords ycv Yuniardi yuyoyuppe @@ -140,6 +141,7 @@ onenote Quickime regedit roslyn +Spotify Vanara WEX windowwalker diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index c4915c60f8cc..1ff3718721ca 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -8,6 +8,7 @@ accctrl Acceleratorkeys ACCEPTFILES ACCESSDENIED +ACCESSTOKEN aclapi AClient AColumn @@ -34,6 +35,7 @@ ALPHATYPE AModifier AMPROPERTY AMPROPSETID +animatedvisuals ANDSCANS ansicolor ANull @@ -160,7 +162,6 @@ CHANGECBCHAIN changecursor CHILDACTIVATE CHILDWINDOW -CHT cidl cim CImage @@ -181,6 +182,7 @@ clrcall CLSCTX Clusion cmder +CMDNOTFOUNDMODULEINTERFACE Cmds CMIC CMINVOKECOMMANDINFO @@ -218,7 +220,6 @@ CONFIGW CONFLICTINGMODIFIERKEY CONFLICTINGMODIFIERSHORTCUT CONOUT -constexpr consts contentdialog contentfiles @@ -318,7 +319,6 @@ DESKTOPABSOLUTEEDITING DESKTOPABSOLUTEPARSING desktopshorcutinstalled desktopwindowxamlsource -DEU devblogs devdocs devenum @@ -507,6 +507,7 @@ GETCLIENTAREAANIMATION GETDESKWALLPAPER GETDLGCODE GETDPISCALEDSIZE +getfilesiginforedist GETICON GETMINMAXINFO GETPROPERTYSTOREFLAGS @@ -537,7 +538,6 @@ Hanzi Hardlines hardlinks HARDWAREINPUT -hashcode Hashset hashtag HASHVAL @@ -556,7 +556,6 @@ hcwhite hdc hdrop hdwwiz -HEB Helpline helptext HGFE @@ -719,7 +718,6 @@ jif jjw jobject jpe -JPN jpnime Jsons jsonval @@ -745,12 +743,11 @@ killrunner Knownfolders KSPROPERTY Kybd -LAlt -Lambson languagesjson -langword +lastbuildstate lastcodeanalysissucceeded Lastdevice +LASTEXITCODE LAYOUTRTL LCIDTo lcl @@ -906,6 +903,8 @@ MOUSEHWHEEL MOUSEINPUT MOVESIZEEND MOVESIZESTART +MOZILLAPL +MOZPL mpmc MRM MRT @@ -921,6 +920,7 @@ MSIFASTINSTALL MSIHANDLE msiquery MSIRESTARTMANAGERCONTROL +msixbundle MSIXCA MSLLHOOKSTRUCT Mso @@ -971,6 +971,7 @@ newdev newitem newpath newrow +newsgroups NIF NLD NLog @@ -983,7 +984,6 @@ NOCLOSEPROCESS NOCOALESCE NOCOPYBITS nodeca -nodiscard nodoc NODRAWCAPTION NODRAWICON @@ -1092,7 +1092,6 @@ pcch pcelt pch PCIDLIST -pcs PCWSTR pdisp pdo @@ -1276,6 +1275,7 @@ reparented reparenting reparse reportbug +reportfileaccesses requery requerying rescap @@ -1303,7 +1303,6 @@ RKey RNumber roadmap rop -roundf ROUNDSMALL rpcrt RRF @@ -1319,12 +1318,11 @@ rundll rungameid RUNLEVEL runsettings +runspace runtimeclass runtimeobject runtimepack runtimes -RUS -RValue rvm rwin rwl @@ -1505,7 +1503,6 @@ subquery Superbar sut svchost -SVE SVGIn SVGIO svgz @@ -1571,6 +1568,8 @@ timediff timeunion timeutil Titlecase +tkcontrols +tkconverters TKey TLayout tlb @@ -1578,7 +1577,6 @@ tlbimp TMPVAR TNP toggleswitch -tonos toolkitcontrols toolkitconverters Toolset @@ -1609,6 +1607,7 @@ UAL uap udit uefi +uesc UHash UIA UIEx @@ -1617,6 +1616,7 @@ ums uncompilable UNCPRIORITY UNDNAME +unescape UNICODETEXT uninstantiated uniquifier @@ -1635,6 +1635,7 @@ UOI Updatelayout UPGRADINGPRODUCTCODE Uptool +urld Usb USEDEFAULT USEFILEATTRIBUTES @@ -1714,7 +1715,6 @@ wcsnicmp WDA wdp wdupenv -weakme webbrowsers webcam webpage @@ -1723,7 +1723,6 @@ wekyb Wevtapi wgpocpl WIC -wifi wil winapi winappdriver @@ -1851,9 +1850,6 @@ zonable zoneset Zoneszonabletester zzz -newsgroups -MOZILLAPL -MOZPL # FALSE POSITIVES diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index daf0220efe64..d48cec9abd85 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -118,6 +118,9 @@ aka\.ms/[a-zA-Z0-9]+ # YouTube url \b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]* +# power shell gallery website +\bpowershellgallery.com/[-_a-zA-Z0-9()=./%]* + # uuid: (or CompGUIDPrefix) L?(["']|[-<({>]|\b)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{10,12}(?:\g{-1}|[<})>]) diff --git a/.gitignore b/.gitignore index 14c1e397376b..f8390b9d56cc 100644 --- a/.gitignore +++ b/.gitignore @@ -348,3 +348,6 @@ src/common/Telemetry/*.etl # Generated installer file for Monaco source files. /installer/PowerToysSetup/MonacoSRC.wxs + +# MSBuildCache +/MSBuildCacheLogs/ diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 73bc45ed035c..a6350ec76434 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -28,7 +28,10 @@ "PowerToys.AlwaysOnTop.exe", "PowerToys.AlwaysOnTopModuleInterface.dll", - + + "PowerToys.CmdNotFoundModuleInterface.dll", + "PowerToys.CmdNotFound.dll", + "PowerToys.ColorPicker.dll", "PowerToys.ColorPickerUI.dll", "PowerToys.ColorPickerUI.exe", @@ -251,6 +254,7 @@ "Mages.Core.dll", "JetBrains.Annotations.dll", "NLog.Extensions.Logging.dll", + "getfilesiginforedist.dll", "concrt140_app.dll", "msvcp140_1_app.dll", "msvcp140_2_app.dll", diff --git a/.pipelines/ci/caching.yml b/.pipelines/ci/caching.yml new file mode 100644 index 000000000000..b802c9efae46 --- /dev/null +++ b/.pipelines/ci/caching.yml @@ -0,0 +1,41 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/azure-pipelines-vscode/main/service-schema.json +trigger: + batch: true + branches: + include: + - main + - stable + paths: + exclude: + - doc/* + - temp/* + - tools/* + - '**.md' + +pr: + branches: + include: + - main + - stable + paths: + exclude: + - '**.md' + - doc + +# 0.0.yyMM.dd## +# 0.0.1904.0900 +name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr) + +variables: + EnablePipelineCache: true + +jobs: + - template: ./templates/build-powertoys-precheck.yml + - template: ./templates/build-powertoys-ci.yml + parameters: + platform: x64 + enableCaching: true + - template: ./templates/build-powertoys-ci.yml + parameters: + platform: arm64 + enableCaching: true \ No newline at end of file diff --git a/.pipelines/ci/templates/build-powertoys-ci.yml b/.pipelines/ci/templates/build-powertoys-ci.yml index c5f4882cd832..2d39da65b991 100644 --- a/.pipelines/ci/templates/build-powertoys-ci.yml +++ b/.pipelines/ci/templates/build-powertoys-ci.yml @@ -1,7 +1,16 @@ parameters: - configuration: 'Release' - platform: '' - additionalBuildArguments: '/p:RestorePackagesConfig=true -m' + - name: configuration + type: string + default: 'Release' + - name: platform + type: string + default: '/p:RestorePackagesConfig=true -m' + - name: additionalBuildArguments + type: string + default: '/p:RestorePackagesConfig=true -m' + - name: enableCaching + type: boolean + default: false jobs: - job: Build${{ parameters.platform }}${{ parameters.configuration }} @@ -26,6 +35,7 @@ jobs: - template: build-powertoys-steps.yml parameters: additionalBuildArguments: ${{ parameters.additionalBuildArguments }} + enableCaching: ${{ parameters.enableCaching }} # It appears that the Component Governance build task that gets automatically injected stopped working # when we renamed our main branch. diff --git a/.pipelines/ci/templates/build-powertoys-steps.yml b/.pipelines/ci/templates/build-powertoys-steps.yml index d151a3925756..815292711191 100644 --- a/.pipelines/ci/templates/build-powertoys-steps.yml +++ b/.pipelines/ci/templates/build-powertoys-steps.yml @@ -1,5 +1,10 @@ parameters: - additionalBuildArguments: '' + - name: additionalBuildArguments + type: string + default: '' + - name: enableCaching + type: boolean + default: false steps: - checkout: self @@ -78,6 +83,13 @@ steps: - task: VisualStudioTestPlatformInstaller@1 displayName: Ensure VSTest Platform +- ${{ if eq(parameters.enableCaching, true) }}: + - task: NuGetToolInstaller@1 + displayName: Install NuGet + + - script: nuget restore packages.config -SolutionDirectory . + displayName: 'nuget restore packages.config' + - task: VSBuild@1 displayName: 'Build PowerToys.sln' inputs: @@ -85,8 +97,15 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} + ${{ if eq(parameters.enableCaching, true) }}: + msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -graph -reportfileaccesses -p:MSBuildCacheEnabled=true -p:MSBuildCacheLogDirectory=$(Build.ArtifactStagingDirectory)\logs\MSBuildCache -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false + ${{ else }}: + msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToys.binlog -ds:false + msbuildArchitecture: x64 maximumCpuCount: true + ${{ if eq(parameters.enableCaching, true) }}: + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: VSBuild@1 displayName: 'Build BugReportTool.sln' @@ -95,7 +114,8 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} + msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\BugReportTool.binlog -ds:false + msbuildArchitecture: x64 maximumCpuCount: true - task: VSBuild@1 @@ -105,7 +125,8 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} + msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\WebcamReportTool.binlog -ds:false + msbuildArchitecture: x64 maximumCpuCount: true - task: VSBuild@1 @@ -115,7 +136,8 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} + msbuildArgs: -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\StylesReportTool.binlog -ds:false + msbuildArchitecture: x64 maximumCpuCount: true - task: PowerShell@2 @@ -131,7 +153,8 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} + msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysInstaller.binlog -ds:false + msbuildArchitecture: x64 maximumCpuCount: true - task: VSBuild@1 @@ -141,8 +164,9 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} + msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysBootstrapper.binlog -ds:false clean: false + msbuildArchitecture: x64 maximumCpuCount: true - task: PowerShell@2 @@ -159,7 +183,8 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} /p:PerUser=true + msbuildArgs: /t:PowerToysInstaller -restore ${{ parameters.additionalBuildArguments }} /p:PerUser=true -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysInstaller-PerUser.binlog -ds:false + msbuildArchitecture: x64 maximumCpuCount: true - task: VSBuild@1 @@ -169,8 +194,9 @@ steps: vsVersion: 17.0 platform: '$(BuildPlatform)' configuration: '$(BuildConfiguration)' - msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} /p:PerUser=true + msbuildArgs: /t:PowerToysBootstrapper ${{ parameters.additionalBuildArguments }} /p:PerUser=true -bl:$(Build.ArtifactStagingDirectory)\logs\PowerToysSetup-PowerToysBootstrapper-PerUser.binlog -ds:false clean: false + msbuildArchitecture: x64 maximumCpuCount: true # Check if deps.json files don't reference different dll versions. @@ -268,3 +294,8 @@ steps: filePath: '$(build.sourcesdirectory)\.pipelines\verifyNoticeMdAgainstNugetPackages.ps1' arguments: -path '$(build.sourcesdirectory)\' pwsh: true + +- publish: $(Build.ArtifactStagingDirectory)\logs + displayName: Publish Logs + artifact: '$(System.JobDisplayName) logs' + condition: always() diff --git a/.pipelines/release.yml b/.pipelines/release.yml index 1993c2b3ce61..d28596e0f62e 100644 --- a/.pipelines/release.yml +++ b/.pipelines/release.yml @@ -8,7 +8,7 @@ resources: - repository: 1ESPipelineTemplates type: git name: 1ESPipelineTemplates/1ESPipelineTemplates - ref: refs/tags/release-2023-11-13-4 + ref: refs/tags/release parameters: - name: buildConfigurations diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 42b66ed8bc82..d5960efb642d 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -24,6 +24,7 @@ $versionExceptions = @( $nullVersionExceptions = @( "codicon.ttf", "e_sqlite3.dll", + "getfilesiginforedist.dll", "vcamp140_app.dll", "vcruntime140_app.dll", "vcruntime140_1_app.dll", diff --git a/Directory.Build.props b/Directory.Build.props index 9f8159a6d9da..14a45385d4d1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,9 +1,9 @@ - Copyright (C) 2023 Microsoft Corporation + Copyright (C) 2024 Microsoft Corporation Microsoft Corp. - Copyright (C) 2023 Microsoft Corporation + Copyright (C) 2024 Microsoft Corporation PowerToys Microsoft Corporation en-US @@ -44,4 +44,46 @@ + + + + false + + + Microsoft.MSBuildCache.AzurePipelines + Microsoft.MSBuildCache.Local + + + + + 202310210737 + + + $(MSBuildCacheAllowFileAccessAfterProjectFinishFilePatterns);packages\Microsoft.WindowsAppSDK.*\tools\net472\ApplicationInsights.config + + + $(MSBuildCacheIdenticalDuplicateOutputPatterns);** + + + $(MSBuildThisFileDirectory)packages.config + $(MSBuildCacheIgnoredInputPatterns);$(PackagesConfigFile) + + + + $([System.IO.File]::ReadAllText("$(PackagesConfigFile)")) + $([System.Text.RegularExpressions.Regex]::Match($(PackagesConfigContents), 'Microsoft.MSBuildCache.*?version="(.*?)"').Groups[1].Value) + $(MSBuildThisFileDirectory)packages\$(MSBuildCachePackageName).$(MSBuildCachePackageVersion) + $(MSBuildThisFileDirectory)packages\Microsoft.MSBuildCache.SharedCompilation.$(MSBuildCachePackageVersion) + + + + + + \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets index 66a8733ce6fd..cba7762d5f48 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,3 +1,6 @@ + + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 93f16ce64a7e..4439822c4e12 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,15 +4,15 @@ - - - - - - - - - + + + + + + + + + @@ -24,7 +24,7 @@ - + @@ -33,16 +33,18 @@ - + + + - + - + @@ -52,22 +54,27 @@ - - + + - + + - + + + + + @@ -75,8 +82,8 @@ - - + + diff --git a/NOTICE.md b/NOTICE.md index 59adbebaed5b..e0aeb9061b2c 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1296,14 +1296,15 @@ EXHIBIT A -Mozilla Public License. ## NuGet Packages used by PowerToys -- CommunityToolkit.Mvvm 8.2.0 -- CommunityToolkit.WinUI.Animations 8.0.230907 -- CommunityToolkit.WinUI.Controls.Primitives 8.0.230907 -- CommunityToolkit.WinUI.Controls.Segmented 8.0.230907 -- CommunityToolkit.WinUI.Controls.SettingsControls 8.0.230907 -- CommunityToolkit.WinUI.Controls.Sizers 8.0.230907 -- CommunityToolkit.WinUI.Converters 8.0.230907 -- CommunityToolkit.WinUI.Extensions 8.0.230907 +- CommunityToolkit.Mvvm 8.2.2 +- CommunityToolkit.WinUI.Animations 8.0.240109 +- CommunityToolkit.WinUI.Collections 8.0.240109 +- CommunityToolkit.WinUI.Controls.Primitives 8.0.240109 +- CommunityToolkit.WinUI.Controls.Segmented 8.0.240109 +- CommunityToolkit.WinUI.Controls.SettingsControls 8.0.240109 +- CommunityToolkit.WinUI.Controls.Sizers 8.0.240109 +- CommunityToolkit.WinUI.Converters 8.0.240109 +- CommunityToolkit.WinUI.Extensions 8.0.240109 - CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2 - CommunityToolkit.WinUI.UI.Controls.Markdown 7.1.2 - ControlzEx 6.0.0 @@ -1313,7 +1314,7 @@ EXHIBIT A -Mozilla Public License. - Interop.Microsoft.Office.Interop.OneNote 1.1.0.2 - LazyCache 2.4.0 - Mages 2.0.1 -- Markdig.Signed 0.27.0 +- Markdig.Signed 0.34.0 - Microsoft.CodeAnalysis.NetAnalyzers 8.0.0 - Microsoft.Data.Sqlite 8.0.0 - Microsoft.Extensions.DependencyInjection 8.0.0 @@ -1321,37 +1322,44 @@ EXHIBIT A -Mozilla Public License. - Microsoft.Extensions.Hosting.WindowsServices 8.0.0 - Microsoft.Extensions.Logging 8.0.0 - Microsoft.Extensions.Logging.Abstractions 8.0.0 -- Microsoft.NET.Test.Sdk 17.6.3 +- Microsoft.Extensions.ObjectPool 8.0.0 +- Microsoft.NET.Test.Sdk 17.8.0 - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.2088.41 +- Microsoft.Win32.SystemEvents 8.0.0 - Microsoft.Windows.Compatibility 8.0.0 - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.0.4 -- Microsoft.Windows.SDK.BuildTools 10.0.22621.756 +- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 - Microsoft.Windows.SDK.Contracts 10.0.19041.1 -- Microsoft.WindowsAppSDK 1.4.231115000 +- Microsoft.WindowsAppSDK 1.4.231219000 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 - Moq 4.18.4 - MSTest.TestAdapter 3.1.1 - MSTest.TestFramework 3.1.1 -- NLog.Extensions.Logging 5.0.4 -- NLog.Schema 5.0.4 +- NLog.Extensions.Logging 5.3.8 +- NLog.Schema 5.2.8 - ScipBe.Common.Office.OneNote 3.0.1 - SharpCompress 0.33.0 - StreamJsonRpc 2.14.24 -- StyleCop.Analyzers 1.2.0-beta.507 +- StyleCop.Analyzers 1.2.0-beta.556 +- System.CodeDom 8.0.0 - System.CommandLine 2.0.0-beta4.22272.1 - System.ComponentModel.Composition 8.0.0 - System.Configuration.ConfigurationManager 8.0.0 - System.Data.OleDb 8.0.0 -- System.Drawing.Common 8.0.0 +- System.Diagnostics.EventLog 8.0.0 +- System.Diagnostics.PerformanceCounter 8.0.0 +- System.Drawing.Common 8.0.1 - System.IO.Abstractions 17.2.3 - System.IO.Abstractions.TestingHelpers 17.2.3 - System.Management 8.0.0 +- System.Management.Automation 7.4.0 - System.Reactive 6.0.0-preview.9 - System.Runtime.Caching 8.0.0 +- System.Security.Cryptography.ProtectedData 8.0.0 - System.ServiceProcess.ServiceController 8.0.0 - System.Text.Encoding.CodePages 8.0.0 - UnicodeInformation 2.6.0 @@ -1360,5 +1368,4 @@ EXHIBIT A -Mozilla Public License. - Vanara.PInvoke.Shell32 3.4.11 - Vanara.PInvoke.User32 3.4.11 - WinUIEx 2.2.0 -- WPF-UI 3.0.0-preview.12 - +- WPF-UI 3.0.0-preview.13 diff --git a/PowerToys.sln b/PowerToys.sln index 7ae29e869a6e..cd14624e27a5 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -538,6 +538,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CropAndLock", "src\modules\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CropAndLockModuleInterface", "src\modules\CropAndLock\CropAndLockModuleInterface\CropAndLockModuleInterface.vcxproj", "{3157FA75-86CF-4EE2-8F62-C43F776493C6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmdNotFound", "src\modules\cmdNotFound\CmdNotFound\CmdNotFound.csproj", "{A37865FE-2881-449F-8ADB-B8CD373D6D79}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cmdNotFound", "cmdNotFound", "{4C0D0746-BE5B-49EE-BD5D-A7811628AE8B}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-FancyZonesEditor", "src\modules\fancyzones\UnitTests-FancyZonesEditor\UnitTests-FancyZonesEditor.csproj", "{FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EnvironmentVariables", "EnvironmentVariables", "{538ED0BB-B863-4B20-98CC-BCDF7FA0B68A}" @@ -558,6 +562,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-QoiPreviewHandler EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-QoiThumbnailProvider", "src\modules\previewpane\UnitTests-QoiThumbnailProvider\UnitTests-QoiThumbnailProvider.csproj", "{F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdNotFoundModuleInterface", "src\modules\cmdNotFound\CmdNotFoundModuleInterface\CmdNotFoundModuleInterface.vcxproj", "{0014D652-901F-4456-8D65-06FC5F997FB0}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileActionsMenu.Ui", "src\modules\previewpane\FileActionsMenu\FileActionsMenu.Ui\FileActionsMenu.Ui.csproj", "{2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileActionsMenu", "src\modules\previewpane\FileActionsMenu\FileActionsMenu\FileActionsMenu.vcxproj", "{3A2398AB-D8F5-49C5-9EA7-483BED398BEC}" @@ -2334,6 +2340,18 @@ Global {3157FA75-86CF-4EE2-8F62-C43F776493C6}.Release|x64.Build.0 = Release|x64 {3157FA75-86CF-4EE2-8F62-C43F776493C6}.Release|x86.ActiveCfg = Release|x64 {3157FA75-86CF-4EE2-8F62-C43F776493C6}.Release|x86.Build.0 = Release|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|ARM64.Build.0 = Debug|ARM64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x64.ActiveCfg = Debug|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x64.Build.0 = Debug|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x86.ActiveCfg = Debug|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Debug|x86.Build.0 = Debug|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|ARM64.ActiveCfg = Release|ARM64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|ARM64.Build.0 = Release|ARM64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x64.ActiveCfg = Release|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x64.Build.0 = Release|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x86.ActiveCfg = Release|x64 + {A37865FE-2881-449F-8ADB-B8CD373D6D79}.Release|x86.Build.0 = Release|x64 {FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}.Debug|ARM64.Build.0 = Debug|ARM64 {FC8EB78F-F061-4BD9-A3F6-507BEA965E2B}.Debug|x64.ActiveCfg = Debug|x64 @@ -2442,6 +2460,18 @@ Global {F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}.Release|x64.Build.0 = Release|x64 {F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}.Release|x86.ActiveCfg = Release|x64 {F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38}.Release|x86.Build.0 = Release|x64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|ARM64.Build.0 = Debug|ARM64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x64.ActiveCfg = Debug|x64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x64.Build.0 = Debug|x64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x86.ActiveCfg = Debug|Win32 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Debug|x86.Build.0 = Debug|Win32 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|ARM64.ActiveCfg = Release|ARM64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|ARM64.Build.0 = Release|ARM64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x64.ActiveCfg = Release|x64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x64.Build.0 = Release|x64 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x86.ActiveCfg = Release|Win32 + {0014D652-901F-4456-8D65-06FC5F997FB0}.Release|x86.Build.0 = Release|Win32 {2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}.Debug|ARM64.Build.0 = Debug|ARM64 {2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0}.Debug|x64.ActiveCfg = Debug|x64 @@ -2660,6 +2690,8 @@ Global {3B227528-4BA6-4CAF-B44A-A10C78A64849} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {F5E1146E-B7B3-4E11-85FD-270A500BD78C} = {3B227528-4BA6-4CAF-B44A-A10C78A64849} {3157FA75-86CF-4EE2-8F62-C43F776493C6} = {3B227528-4BA6-4CAF-B44A-A10C78A64849} + {A37865FE-2881-449F-8ADB-B8CD373D6D79} = {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B} + {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {FC8EB78F-F061-4BD9-A3F6-507BEA965E2B} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} {538ED0BB-B863-4B20-98CC-BCDF7FA0B68A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {51465DA1-C18B-4B99-93E1-ECF8E0FA0CBA} = {538ED0BB-B863-4B20-98CC-BCDF7FA0B68A} @@ -2670,6 +2702,7 @@ Global {6B04803D-B418-4833-A67E-B0FC966636A5} = {2F305555-C296-497E-AC20-5FA1B237996A} {3940AD4D-F748-4BE4-9083-85769CD553EF} = {2F305555-C296-497E-AC20-5FA1B237996A} {F8FFFC12-A31A-4AFA-B3DF-14DCF42B5E38} = {2F305555-C296-497E-AC20-5FA1B237996A} + {0014D652-901F-4456-8D65-06FC5F997FB0} = {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B} {2ED5F735-787A-4A86-9B0D-4A2DD06FAAF0} = {89A38AD7-DA2F-46B9-AA9B-C213AA172350} {3A2398AB-D8F5-49C5-9EA7-483BED398BEC} = {89A38AD7-DA2F-46B9-AA9B-C213AA172350} {89A38AD7-DA2F-46B9-AA9B-C213AA172350} = {2F305555-C296-497E-AC20-5FA1B237996A} diff --git a/README.md b/README.md index 575abe4c542a..4d8e8276d7b2 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline | | Current utilities: | | |--------------|--------------------|--------------| -| [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | -| [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | -| [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | -| [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | -| [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | -| [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | -| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | -| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | +| [Always on Top](https://aka.ms/PowerToysOverview_AoT) | [PowerToys Awake](https://aka.ms/PowerToysOverview_Awake) | [Command Not Found](https://aka.ms/PowerToysOverview_CmdNotFound) | +| [Color Picker](https://aka.ms/PowerToysOverview_ColorPicker) | [Crop And Lock](https://aka.ms/PowerToysOverview_CropAndLock) | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | +| [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | +| [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | +| [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | [Peek](https://aka.ms/PowerToysOverview_Peek) | +| [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | +| [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | +| [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | ## Installing and running Microsoft PowerToys @@ -40,19 +40,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. -[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F50 -[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F49 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysUserSetup-0.76.2-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysUserSetup-0.76.2-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysSetup-0.76.2-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.76.2/PowerToysSetup-0.76.2-arm64.exe +[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F51 +[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F50 +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysUserSetup-0.77.0-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysUserSetup-0.77.0-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysSetup-0.77.0-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.77.0/PowerToysSetup-0.77.0-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.76.2-x64.exe][ptUserX64] | 73D734FC34B3F9D7484081EC0F0B6ACD4789A55203A185904CC5C62ABD02AF16 | -| Per user - ARM64 | [PowerToysUserSetup-0.76.2-arm64.exe][ptUserArm64] | 5284CC5DA399DC37858A2FD260C30F20C484BA1B5616D0EB67CD90A8A286CB8B | -| Machine wide - x64 | [PowerToysSetup-0.76.2-x64.exe][ptMachineX64] | 72B87381C9E5C7447FB59D7CE478B3102C9CEE3C6EB3A6BC7EC7EC7D9EFAB2A0 | -| Machine wide - ARM64 | [PowerToysSetup-0.76.2-arm64.exe][ptMachineArm64] | F28C7DA377C25309775AB052B2B19A299C26C41582D05B95C3492A4A8C952BFE | +| Per user - x64 | [PowerToysUserSetup-0.77.0-x64.exe][ptUserX64] | 3485D3F45A3DE6ED7FA151A4CE9D6F941491C30E83AB51FD59B4ADCD20611F1A | +| Per user - ARM64 | [PowerToysUserSetup-0.77.0-arm64.exe][ptUserArm64] | 762DF383A01006A20C0BAB2D321667E855236EBA7108CDD475E4E2A8AB752E0E | +| Machine wide - x64 | [PowerToysSetup-0.77.0-x64.exe][ptMachineX64] | 1B6D4247313C289B07A3BF3531E215B3F9BEDBE9254919637F2AC502B4773C31 | +| Machine wide - ARM64 | [PowerToysSetup-0.77.0-arm64.exe][ptMachineArm64] | CF740B3AC0EB5C23E18B07ACC2D0C6EC5F4CE4B3A2EDC67C2C9FDF6EF78F0352 | This is our preferred method. @@ -98,154 +98,112 @@ 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.76 - November 2023 Update +### 0.77 - December 2023 Update In this release, we focused on new features, stability and improvements. **Highlights** - - Upgrade to .NET 8. Thanks [@snickler](https://github.com/snickler)! - - Keyboard Manager can now remap keys and shortcuts to send sequences of unicode text. - - Modernized the Keyboard Manager Editor UI. Thanks [@dillydylann](https://github.com/dillydylann)! - - Modernized the PowerToys Run, Quick Accent and Text Extractor UIs. Thanks [@niels9001](https://github.com/niels9001)! - - New File Explorer Add-ons: QOI image Preview Handler and Thumbnail Provider. Thanks [@pedrolamas](https://github.com/pedrolamas)! + - New utility: Command Not Found PowerShell 7.4 module - adds the ability to detect failed commands in PowerShell 7.4 and suggest a package to install using winget. Thanks [@carlos-zamora](https://github.com/carlos-zamora)! + - Keyboard manager does not register low level hook if there are no remappings anymore. + - Added support for QOI file type in Peek. Thanks [@pedrolamas](https://github.com/pedrolamas)! + - Added support for loading 3rd-party plugins with additional dependencies in PowerToys Run. Thanks [@coreyH](https://github.com/CoreyHayward)! ### General - - Updated the WebView 2 dependency to 1.0.2088.41. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed unreadable color brushes used across WinUI3 applications for improved accessibility. Thanks [@niels9001](https://github.com/niels9001)! - - Flyouts used across WinUI3 applications are no longer constrained to the application's bounds. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! - - Upgraded the WPF-UI dependency to preview.9 and then preview.11. Thanks [@niels9001](https://github.com/niels9001) and [@pomianowski](https://github.com/pomianowski)! - - Upgraded to .NET 8. Thanks [@snickler](https://github.com/snickler)! - - Updated the WinAppSDK dependency to 1.4.3. -### Awake + - Bump WPF-UI package version to fix crashes related to theme changes. (This was a hotfix for 0.76) + - Fixed typo in version change notification. Thanks [@PesBandi](https://github.com/PesBandi)! + - Code improvements and fixed silenced warnings introduced by upgrade to .NET 8. + - Update copyright year for 2024. + - Added setting to disable warning notifications about detecting an application running as Administrator. - - Added localization to the tray icon context menu. +### AlwaysOnTop -### Crop And Lock + - Show notification when elevated app is in the foreground but AlwaysOnTop is running non-elevated. - - Fixed restoring windows that were reparented while maximized. +### Command Not Found + + - Added a new utility: A Command Not Found PowerShell 7.4 module. It adds the ability to detect failed commands in PowerShell 7.4 and suggest a package to install using winget. Thanks [@carlos-zamora](https://github.com/carlos-zamora)! ### Environment Variables - - Fixed crash caused by WinAppSDK version bump by replacing ListView elements with ItemsControl. + - Fixed issue causing Environment Variables window not to appear as a foreground window. ### FancyZones - - Reverted a change that caused some applications, like the Windows Calculator, to not snap correctly. (This was a hotfix for 0.75) - - FancyZones Editor will no longer apply a layout to the current monitor after editing it. - - Fixed and refactored the code that detected if a window can be snapped. Added tests to it with known application window styles to avoid regressions in the future. + - Fixed snapping specific apps (e.g. Facebook messenger). (This was a hotfix for 0.76) + - Fixed behavior of Move newly created windows to current active monitor setting to keep maximize state on moving. Thanks [@quyenvsp](https://github.com/quyenvsp)! + - Fixed issue causing FancyZones Editor layout window to be zoned. ### File Explorer add-ons - - Solved an issue incorrectly detecting encoding when previewing code files preview. - - Fixed the background color for Gcode preview handler on dark theme. Thanks [@pedrolamas](https://github.com/pedrolamas)! - - New utilities: Preview Handler and Thumbnail Provider for QOI image files. Thanks [@pedrolamas](https://github.com/pedrolamas)! - - GCode Thumbnails are now in the 32 bit ARGB format. Thanks [@pedrolamas](https://github.com/pedrolamas)! - - Added the perceived type to SVG and QOI file thumbnails. Thanks [@pedrolamas](https://github.com/pedrolamas)! - -### GPO - - - Added the missing Environment Variables utility policy to the .admx and .adml files. (This was a hotfix for 0.75) - - Fixed some typos and text improvements in the .adml file. Thanks [@htcfreek](https://github.com/htcfreek)! + - Fixed WebView2 based previewers issue caused by the latest WebView update. (This was a hotfix for 0.76) ### Hosts File Editor - - Added a proper warning when the hosts file is read-only and a button to make it writable. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed issue causing settings not to be preserved on update. ### Image Resizer - - - Fixed a WPF-UI issue regarding the application's background brushes. Thanks [@niels9001](https://github.com/niels9001)! - -### Installer - - Included the Text Extractor and Awake localization files in the install process. + - Fixed crash caused by WpfUI ThemeWatcher. (This was a hotfix for 0.76) ### Keyboard Manager - - Modernized the UI with the Fluent design. Thanks [@dillydylann](https://github.com/dillydylann)! - - Added the feature to remap keys and shortcuts to arbitrary unicode text sequences. - -### Mouse Without Borders - - - Removed Thread.Suspend calls when exiting the utility. That call is deprecated, unneeded and was causing a silent crash. + - Do not register low level hook if there are no remappings. ### Peek - - Added the possibility to pause/resume videos with the space bar. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed high CPU usage when idle before initializing the main window. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Implemented Ctrl+W as a shortcut to close Peek. Thanks [@Physalis2](https://github.com/Physalis2)! - - Solved an issue incorrectly detecting encoding when previewing code files. - - Fixed background issues when peeking into HTML files after the WebView 2 upgrade. + - Improved icon and title showing for previewed files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Added QOI file type support. Thanks [@pedrolamas](https://github.com/pedrolamas)! ### PowerToys Run - - Moved to WPF-UI and redesigned according to Fluent UX principles. Thanks [@niels9001](https://github.com/niels9001)! - - Fixed an issue causing 3rd party plugins to not have their custom settings correctly initialized with default values. (This was a hotfix for 0.75) Thanks [@waaverecords](https://github.com/waaverecords)! - - Fixed a crash in the VSCode plugin when the VSCode path had trailing backspaces. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed a crash when trying to load invalid image icons. - - Fixed a crash in the Programs plugin when getting images for some .lnk files. - - Fixed a rare startup initialization error and removed cold start operations that were no longer needed. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Improved calculations for Windows File Time and Unix Epoch Time in the DateTime plugin. Thanks [@htcfreek](https://github.com/htcfreek)! - - Fixed a crash when trying to get the icon for a link that pointed to no file. - - Cleaned up code in the WindowWalker plugin improving the logic. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed results list UI element height for different maximum number of results value. (This was a hotfix for 0.76) + - Fixed icon extraction for .lnk files. (This was a hotfix for 0.76) + - Fixed search box UI glitch when FlowDirection is RightToLeft. (This was a hotfix for 0.76) + - Fixed theme setting. (This was a hotfix for 0.76) + - Fixed error reporting window UI issue. Thanks [@niels9001](https://github.com/niels9001)! + - UI improvements and ability to show/hide plugins overview panel. Thanks [@niels9001](https://github.com/niels9001)! + - Allow interaction with plugin hints. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Switch to WPF-UI theme manager. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed issue causing 3rd party plugin's dependencies dll not being loaded properly. Thanks [@coreyH](https://github.com/CoreyHayward)! + - Added configurable font sizes. Thanks [@niels9001](https://github.com/niels9001)! + - Changed the text color of plugin hints to improve the contrast when light theme is used. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fix scientific notation errors in Calculator plugin. Thanks [@viggyd](https://github.com/viggyd)! + - Add URI/URL features to Value generator plugin. Thanks [@htcfreek](https://github.com/htcfreek)! ### Quick Accent - - Moved from ModernWPF to WPF-UI. Thanks [@niels9001](https://github.com/niels9001)! - - Added support to the Finnish language character set. Thanks [@davidtlascelles](https://github.com/davidtlascelles)! - - Added currency symbols for Croatian, Gaeilge, Gàidhlig and Welsh. Thanks [@PesBandi](https://github.com/PesBandi)! - - Added a missing Latin letter ꝡ. Thanks [@cubedhuang](https://github.com/cubedhuang)! - - Added fraction characters. Thanks [@PesBandi](https://github.com/PesBandi)! - - Added support to the Danish language character set. Thanks [@PesBandi](https://github.com/PesBandi)! - - Added the Kazakhstani Tenge character to the Currencies characters set. Thanks [@PesBandi](https://github.com/PesBandi)! - - Renamed Slovakian to Slovak, which is the correct term. Thanks [@PesBandi](https://github.com/PesBandi)! - - Added the Greek language character set. Thanks [@mcbabo](https://github.com/mcbabo)! + - Moved Greek specific characters from All language set to Greek. Thanks [@PesBandi](https://github.com/PesBandi)! + - Add more mathematical symbols. Thanks [@kevinfu2](https://github.com/kevinfu2)! ### Settings - - When clicking a module's name on the Dashboard, it will navigate to that module's page. - - Fixed the clipping of information in the Backup and Restore section of the General Settings page. Thanks [@niels9001](https://github.com/niels9001)! - - Updated the File Explorer Add-ons fluent icon. Thanks [@niels9001](https://github.com/niels9001)! - - Added a warning when trying to set a shortcut that might conflict with "Alt Gr" key combinations. - - Added a direct link to the OOBE's "What's New page" from the main Settings window. Thanks [@iakrayna](https://github.com/iakrayna)! - - Changed mentions from Microsoft Docs to Microsoft Learn. - - Fixed the slow reaction to system theme changes. - -### Text Extractor - - - Move to WPF-UI, localization and light theme support. Thanks [@niels9001](https://github.com/niels9001)! - - Disabled by default on Windows 11, with a information box on Settings to prefer using the Windows Snipping Tool, which now supports OCR. + - Fixed exception occurring on theme change. + - Fix "What's new" icon. Thanks [@niels9001](https://github.com/niels9001)! + - Remove obsolete UI Font icon properties. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! + - OOBE UI improvements. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! + - XAML Binding improvements. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! + - Fixed crash caused by ThemeListener constructor exceptions. ### Documentation - - Fixed some typos in the README. Thanks [@Asymtode712](https://github.com/Asymtode712)! - - Reworked the gpo docs on learn.microsoft.com, adding .admx, registry and Intune information. Thanks [@htcfreek](https://github.com/htcfreek)! + - Improved docs for adding new languages to monaco. Thanks [@PesBandi](https://github.com/PesBandi)! + - Update README.md to directly state x64 & ARM processor in requirements. + - Added Scoop plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@Quriz](https://github.com/Quriz)! ### Development - - Updated the check-spelling ci action to 0.22. Thanks [@jsoref](https://github.com/jsoref)! - - Refactored the modules data model used between the Settings Dashboard and Flyout. - - Fixed a flaky interop test that was causing automated CI to hang occasionally. - - Increased the WebView 2 loading timeout to reduce flakiness in those tests. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Added support for building with the Dev Drive CopyOnWrite feature, increasing build speed. Thanks [@pedrolamas](https://github.com/pedrolamas)! - - Addressed the C# static analyzers suggestions. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Addressed the C++ static analyzers suggestions. - - PRs that only contain Markdown or text files changes no longer trigger the full CI. Thanks [@snickler](https://github.com/snickler)! - - Updated the Microsoft.Windows.CsWinRT to 2.0.4 to fix building with the official Visual Studio 17.8 release. - - Fixed new code quality issues caught by the official Visual Studio 17.8 release. - - Added a bot trigger to point contributors to the main new contribution issue on GitHub. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! - - Removed unneeded entries from expect.txt. - - Turned off a new feature from Visual Studio that was adding the commit hash to the binary files Product Version. - - Refactored and reviewed the spellcheck entries into different files. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)! - - Added Spectre mitigation and SHA256 hash creation for some DLLs. - - Reverted the release pipeline template to a previous release that's stable for shipping PowerToys. - -#### What is being planned for version 0.77 - -For [v0.77][github-next-release-work], we'll work on the items below: - - - New utility: Command Not Found + - Adopted XamlStyler for PowerToys Run source code. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Consolidate Microsoft.Windows.SDK.BuildTools across solution. + - Upgraded Boost's lib to v1.84. + - Upgraded HelixToolkit packages to the latest versions. + - Updated sdl baselines. + +#### What is being planned for version 0.78 + +For [v0.78][github-next-release-work], we'll work on the items below: + - Language selection - Automated UI testing through WinAppDriver - Develop support for Desired State Configuration diff --git a/doc/devdocs/modules/launcher/new-plugin-checklist.md b/doc/devdocs/modules/launcher/new-plugin-checklist.md index 3554e3af47c1..1d76e85b7596 100644 --- a/doc/devdocs/modules/launcher/new-plugin-checklist.md +++ b/doc/devdocs/modules/launcher/new-plugin-checklist.md @@ -1,11 +1,14 @@ # New plugin checklist + - [ ] The plugin is a project under `modules\launcher\Plugins` - [ ] Microsoft plugin project name pattern: `Microsoft.PowerToys.Run.Plugin.{PluginName}` - [ ] Community plugin project name pattern: `Community.PowerToys.Run.Plugin.{PluginName}` +- [ ] The plugin target framework should be `net8.0-windows` - [ ] The project file should import `Version.props` and specify `$(Version).0` -- [ ] Make sure `*.csproj` specify only x64 platform target -- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder -``` +- [ ] If the plugin uses any 3rd party dependencies the project file should import `DynamicPlugin.props` +- [ ] The plugin has to contain a `plugin.json` file of the following format in its root folder: + +```json { "ID": string, // GUID string "ActionKeyword": string, // Direct activation phrase @@ -17,23 +20,26 @@ "Website": "https://aka.ms/powertoys", "ExecuteFileName": string, // Should be {Type}.PowerToys.Run.Plugin.{PluginName}.dll "IcoPathDark": string, // Path to dark theme icon. The path is relative to the root plugin folder - "IcoPathLight": string // Path to light theme icon. The path is relative to the root plugin folder + "IcoPathLight": string // Path to light theme icon. The path is relative to the root plugin folder + "DynamicLoading": bool // Sets whether the plugin should dynamically load any dependencies isolated from the core application. } ``` + - [ ] Make sure your `Main` class contains a public, static string property for the `PluginID`. The plugin id has to be the same as the one in the `plugin.json`file. + ```csharp public static string PluginID => "xxxxxxx"; // The part xxxxxxx stands for the plugin ID. ``` + - [ ] Do not use plugin name or PowerToys as prefixes for entities inside of the plugin project - [ ] The plugin has to have Unit tests. Use MSTest framework - [ ] Plugin's output code and assets have to be included in the installer [`Product.wxs`](/installer/PowerToysSetup/Product.wxs) - [ ] Test the plugin with a local build. Build the installer, install, check that the plugin works as expected - [ ] All plugin's binaries have to be included in the signed build [`pipeline.user.windows.yml`](/.pipelines/pipeline.user.windows.yml) -- [ ] The plugin target framework has to be .NET Core 3.1. All dependencies have to have .NET 5 version +- [ ] The plugin target framework has to be net8.0-windows. All dependencies should be compatible with .NET 8. Some localization steps can only be done after the first pass by the localization team to provide the localized resources. In the PR that adds a new plugin, reference a new issue to track the work for fully enabling localization for the new plugin. - - [ ] Add the resource folder to https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L825 - - [ ] Add the resource files under the section https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L882 - +- [ ] Add the resource folder to https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L825 +- [ ] Add the resource files under the section https://github.com/microsoft/PowerToys/blob/21247c0bb09a1bee3d14d6efa53d0c247f7236af/installer/PowerToysSetup/Product.wxs#L882 diff --git a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md index 379e7d489491..4a580f090d0e 100644 --- a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md +++ b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md @@ -1,6 +1,6 @@ # Value Generator Plugin -The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings and to generate GUIDs versions 1, 3, 4 and 5. +The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs versions 1, 3, 4 and 5. ![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png) @@ -10,7 +10,7 @@ The Value Generator plugin is used to generate hashes for strings, to calculate - The result of `string ResultToString()` will be used for the Result's title - The `Description` field will be used for the Result's subtitle -### [`HashRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs) +### [`HashRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs) - Implements IComputeRequest - Supports the hashing algorithms from System.Security.Cryptography: - MD5 @@ -20,19 +20,19 @@ The Value Generator plugin is used to generate hashes for strings, to calculate - SHA512 - If other algorithms are added to System.Security.Cryptography, they can be added to the `_algorithms` dictionary. [`InputParser.ParseInput()`](#inputparser) will need to return a `HashRequest` for the algorithm in the query -### [`Base64Request`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs) +### [`Base64Request`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs) - Implements IComputeRequest - `Compute()` will populate `Result` with the base64 encoding of the byte array passed in the constructor -### [`Base64DecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs) +### [`Base64DecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs) - Implements IComputeRequest - `Compute()` will populate `Result` with the decoded byte array of the base64 string passed in the constructor -### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs) +### [`GUIDRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs) - Implements IComputeRequest - Uses the [`GUIDGenerator`](#guidgenerator) class to generate or compute the requested GUID -### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs) +### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs) - Utility class for generating or calculating GUIDs - Generating GUID versions 1 and 4 is done using builtin APIs. [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 and `System.Guid.NewGuid()` for version 4 - Versions 3 and 5 take two parameters, a namespace and a name @@ -40,6 +40,32 @@ The Value Generator plugin is used to generate hashes for strings, to calculate - The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces - The name can be any string +### [`UrlEncodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs) +- Implements IComputeRequest +- `Compute()` will populate `Result` with the encoded url converted using `HttpUtility.UrlEncode()`. + +### [`UrlDecodeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs) +- Implements IComputeRequest +- `Compute()` will populate `Result` with the decoded url converted using `HttpUtility.UrlDecode()`. + +### [`DataEscapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs) +- Implements IComputeRequest +- `Compute()` will populate `Result` with the escaped data string converted using `System.Uri.EscapeDataString()`. + +### [`DataUnescapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs) +- Implements IComputeRequest +- `Compute()` will populate `Result` with the unescaped data string converted using `System.Uri.UnescapeDataString()`. + +### [`HexEscapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs) +- Implements IComputeRequest +- `Compute()` will populate `Result` with the escaped data string converted using `System.Uri.HexEscape()`. +- Only single characters are supported as input. + +### [`HexUnescapeRequest`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs) +- Implements IComputeRequest +- `Compute()` will populate `Result` with the unescaped data string converted using `System.Uri.HexUnescape()`. +- Only the first hexadecimal character in the string gets unescaped. The rest of the user input is ignored. + ### [`InputParser`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs) - It is responsible only for parsing the query from the user - Based on the user query, the `ParseInput()` method must return an object that implements the `IComputeRequest` interface or it must throw one of `FormatException` or `ArgumentException` @@ -51,6 +77,6 @@ The Value Generator plugin is used to generate hashes for strings, to calculate > The error message will not be shown to the user but a log message will be created ### Adding a new value generator -1. To add a new value generator, create a folder under `/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/` and inside it add a class that implements `IComputeRequest`. +1. To add a new value generator, create a folder under `/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/` and inside it add a class that implements `IComputeRequest`. 2. Add any utility classes that are specific to the new generator inside the same folder to keep them separated from the other generators. 3. Modify the `InputParser.ParseInput()` to handle a request for the new generator and return an instance of the class you created in step 1 \ No newline at end of file diff --git a/doc/images/icons/Command Not Found.png b/doc/images/icons/Command Not Found.png new file mode 100644 index 000000000000..8243b9f8c8eb Binary files /dev/null and b/doc/images/icons/Command Not Found.png differ diff --git a/doc/images/overview/Original/PowerLauncher.png b/doc/images/overview/Original/PowerLauncher.png index 701969e2c77a..597e64067fdf 100644 Binary files a/doc/images/overview/Original/PowerLauncher.png and b/doc/images/overview/Original/PowerLauncher.png differ diff --git a/doc/images/overview/PowerLauncher_large.png b/doc/images/overview/PowerLauncher_large.png index aecb74c9fb65..063e51be4c31 100644 Binary files a/doc/images/overview/PowerLauncher_large.png and b/doc/images/overview/PowerLauncher_large.png differ diff --git a/doc/images/overview/PowerLauncher_small.png b/doc/images/overview/PowerLauncher_small.png index cdf5776b37db..0ef995180f54 100644 Binary files a/doc/images/overview/PowerLauncher_small.png and b/doc/images/overview/PowerLauncher_small.png differ diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index bf85dd421c93..0e9f4a62ab37 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -34,3 +34,6 @@ Contact the developers of a plugin directly for assistance with a specific plugi | [Visual Studio](https://github.com/davidegiacometti/PowerToys-Run-VisualStudio) | [davidegiacometti](https://github.com/davidegiacometti) | Open Visual Studio recents | | [WinGet](https://github.com/bostrot/PowerToysRunPluginWinget) | [bostrot](https://github.com/bostrot) | Search and install packages from WinGet | | [Scoop](https://github.com/Quriz/PowerToysRunScoop) | [Quriz](https://github.com/Quriz) | Search and install packages from Scoop | +| [Spotify](https://github.com/waaverecords/PowerToys-Run-Spotify) | [waaverecords](https://github.com/waaverecords) | Search Spotify and control its player | +| [Input Typer](https://github.com/CoreyHayward/PowerToys-Run-InputTyper) | [CoreyHayward](https://github.com/CoreyHayward) | Type the input as if sent from a keyboard | +| [Clipboard Manager](https://github.com/CoreyHayward/PowerToys-Run-ClipboardManager) | [CoreyHayward](https://github.com/CoreyHayward) | Search and paste text from your clipboard history | \ No newline at end of file diff --git a/installer/PowerToysSetup/CmdNotFound.wxs b/installer/PowerToysSetup/CmdNotFound.wxs new file mode 100644 index 000000000000..3ac8445d887d --- /dev/null +++ b/installer/PowerToysSetup/CmdNotFound.wxs @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/Common.wxi b/installer/PowerToysSetup/Common.wxi index 4a1c93fc8ea3..6e68ce19861d 100644 --- a/installer/PowerToysSetup/Common.wxi +++ b/installer/PowerToysSetup/Common.wxi @@ -18,6 +18,7 @@ + diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index 6a4f3b80cb79..8fb186ebb644 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -1,5 +1,6 @@ - + @@ -14,7 +15,7 @@ SET PTRoot=$(SolutionDir)\.. call "..\..\..\publish.cmd" x64 ) call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuildThisFileDirectory)\generateMonacoWxs.ps1 -monacoWxsFile "$(MSBuildThisFileDirectory)\MonacoSRC.wxs" - + @@ -25,7 +26,7 @@ SET PTRoot=$(SolutionDir)\.. call "..\..\..\publish.cmd" arm64 ) call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuildThisFileDirectory)\generateMonacoWxs.ps1 -monacoWxsFile "$(MSBuildThisFileDirectory)\MonacoSRC.wxs" - + Always @@ -67,7 +68,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil - Release + Release $(Platform) 3.10 022a9d30-7c4f-416d-a9df-5ff2661cc0ad @@ -103,6 +104,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil + @@ -180,17 +182,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil --> - + \ No newline at end of file diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 2dea1fab5570..ee15ec090a59 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -72,6 +72,7 @@ + @@ -133,6 +134,7 @@ + @@ -159,6 +161,9 @@ Installed AND (REMOVE="ALL") + + Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") + Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") @@ -200,6 +205,10 @@ Property="UnApplyModulesRegistryChangeSets" Value="[INSTALLFOLDER]" /> + + @@ -252,7 +261,15 @@ BinaryKey="PTCustomActions" DllEntry="UninstallServicesCA" /> - + + + + + @@ -45,6 +47,19 @@ + + + + + + + + + + + + + @@ -55,7 +70,9 @@ + + diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index c76e2203f885..290df09898f4 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -433,7 +433,29 @@ UINT __stdcall RemoveWindowsServiceByName(std::wstring serviceName) return ERROR_SUCCESS; } +UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + std::wstring installationFolder; + std::string command; + hr = WcaInitialize(hInstall, "UninstallCommandNotFoundModule"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = getInstallFolder(hInstall, installationFolder); + ExitOnFailure(hr, "Failed to get installFolder."); + + command = "pwsh.exe"; + command += " "; + command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\""; + + system(command.c_str()); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} UINT __stdcall UninstallServicesCA(MSIHANDLE hInstall) { diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def index 7c3c058d9cd1..6a503da797c0 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.def +++ b/installer/PowerToysSetupCustomActions/CustomAction.def @@ -22,4 +22,5 @@ EXPORTS UninstallVirtualCameraDriverCA UnRegisterContextMenuPackagesCA UninstallEmbeddedMSIXCA - UninstallServicesCA \ No newline at end of file + UninstallServicesCA + UninstallCommandNotFoundModuleCA \ No newline at end of file diff --git a/nuget.config b/nuget.config index 81243afb3c86..0643a02af3c0 100644 --- a/nuget.config +++ b/nuget.config @@ -6,8 +6,5 @@ - - - - \ No newline at end of file + diff --git a/packages.config b/packages.config new file mode 100644 index 000000000000..c99cd91fd719 --- /dev/null +++ b/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/.editorconfig b/src/.editorconfig index 68812c52b97c..28245c30fc47 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -96,49 +96,4 @@ end_of_line = crlf dotnet_diagnostic.IDE0065.severity = none # IDE0009: Add this or Me qualification -dotnet_diagnostic.IDE0009.severity = none - -# CA1859: Change type for improved performance -dotnet_diagnostic.CA1859.severity = none - -# CA1716: Identifiers should not match keywords -dotnet_diagnostic.CA1716.severity = none - -# SYSLIB1096: Convert to 'GeneratedComInterface' -dotnet_diagnostic.SYSLIB1096.severity = silent - -# CA1309: Use ordinal StringComparison -dotnet_diagnostic.CA1309.severity = suggestion - -# CS1615: Argument may not be passed with the ref keyword -dotnet_diagnostic.CS1615.severity = none - -# CA1854: Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup -dotnet_diagnostic.CA1854.severity = suggestion - -# CA1860: Avoid using 'Enumerable.Any()' extension method -dotnet_diagnostic.CA1860.severity = suggestion - -# CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array -dotnet_diagnostic.CA1861.severity = suggestion - -# CA1862: Prefer using 'StringComparer'/'StringComparison' to perform case-insensitive string comparisons -dotnet_diagnostic.CA1862.severity = suggestion - -# CA1863: Cache a CompositeFormat for repeated use in this formatting operation -dotnet_diagnostic.CA1863.severity = none - -# CA1864: Prefer the 'IDictionary.TryAdd(TKey, TValue)' method -dotnet_diagnostic.CA1864.severity = suggestion - -# CA1865: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char -dotnet_diagnostic.CA1865.severity = suggestion - -# CA1869: Cache and reuse 'JsonSerializerOptions' instances -dotnet_diagnostic.CA1869.severity = none - -# CA2208: Instantiate argument exceptions correctly -dotnet_diagnostic.CA2208.severity = none - -# CS9191: The 'ref' modifier for argument corresponding to 'in' is equivalent to 'in'. Consider using 'in' instead. -dotnet_diagnostic.CS9191.severity = none +dotnet_diagnostic.IDE0009.severity = none \ No newline at end of file diff --git a/src/ActionRunner/actionRunner.vcxproj b/src/ActionRunner/actionRunner.vcxproj index ebe3fc8fc8af..8809611842f1 100644 --- a/src/ActionRunner/actionRunner.vcxproj +++ b/src/ActionRunner/actionRunner.vcxproj @@ -62,7 +62,7 @@ - + @@ -70,6 +70,6 @@ - + \ No newline at end of file diff --git a/src/ActionRunner/packages.config b/src/ActionRunner/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/ActionRunner/packages.config +++ b/src/ActionRunner/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/Update/PowerToys.Update.vcxproj b/src/Update/PowerToys.Update.vcxproj index e04037475406..914732926ceb 100644 --- a/src/Update/PowerToys.Update.vcxproj +++ b/src/Update/PowerToys.Update.vcxproj @@ -68,7 +68,7 @@ - + @@ -76,6 +76,6 @@ - + \ No newline at end of file diff --git a/src/Update/packages.config b/src/Update/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/Update/packages.config +++ b/src/Update/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/common/AllExperiments/Experiments.cs b/src/common/AllExperiments/Experiments.cs index 426e7af1d1d9..2fdcaa0482a9 100644 --- a/src/common/AllExperiments/Experiments.cs +++ b/src/common/AllExperiments/Experiments.cs @@ -145,7 +145,7 @@ private async Task VariantAssignmentProvider_Initialize() private string? AssignmentUnit { get; set; } - private IVariantAssignmentRequest GetVariantAssignmentRequest() + private VariantAssignmentRequest GetVariantAssignmentRequest() { var jsonFilePath = CreateFilePath(); try diff --git a/src/common/COMUtils/COMUtils.vcxproj b/src/common/COMUtils/COMUtils.vcxproj index 6a49eb786067..f582df593bf9 100644 --- a/src/common/COMUtils/COMUtils.vcxproj +++ b/src/common/COMUtils/COMUtils.vcxproj @@ -36,12 +36,12 @@ - + 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}. - + \ No newline at end of file diff --git a/src/common/COMUtils/packages.config b/src/common/COMUtils/packages.config index 3d9798ef5e72..6199e2345ccd 100644 --- a/src/common/COMUtils/packages.config +++ b/src/common/COMUtils/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs b/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs index 4f366fbff831..91b7013c6fdd 100644 --- a/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs +++ b/src/common/FilePreviewCommon/Formatters/JsonFormatter.cs @@ -12,6 +12,12 @@ public class JsonFormatter : IFormatter /// public string LangSet => "json"; + private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + }; + /// public string Format(string value) { @@ -22,11 +28,7 @@ public string Format(string value) using (var jDocument = JsonDocument.Parse(value, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip })) { - return JsonSerializer.Serialize(jDocument, new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - }); + return JsonSerializer.Serialize(jDocument, _serializerOptions); } } } diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 77d81e85160c..c3177d60dbee 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -12,6 +12,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredAwakeEnabledValue()); } + GpoRuleConfigured GPOWrapper::GetConfiguredCmdNotFoundEnabledValue() + { + return static_cast(powertoys_gpo::getConfiguredCmdNotFoundEnabledValue()); + } GpoRuleConfigured GPOWrapper::GetConfiguredColorPickerEnabledValue() { return static_cast(powertoys_gpo::getConfiguredColorPickerEnabledValue()); diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index c29fac95b6a9..bd153ed25833 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -9,6 +9,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation GPOWrapper() = default; static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue(); static GpoRuleConfigured GetConfiguredAwakeEnabledValue(); + static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue(); static GpoRuleConfigured GetConfiguredColorPickerEnabledValue(); static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue(); static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue(); diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index d0e3ebbd4fb6..3b4c0eca2800 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -13,6 +13,7 @@ namespace PowerToys [default_interface] static runtimeclass GPOWrapper { static GpoRuleConfigured GetConfiguredAlwaysOnTopEnabledValue(); static GpoRuleConfigured GetConfiguredAwakeEnabledValue(); + static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue(); static GpoRuleConfigured GetConfiguredColorPickerEnabledValue(); static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue(); static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue(); diff --git a/src/common/GPOWrapperProjection/GPOWrapper.cs b/src/common/GPOWrapperProjection/GPOWrapper.cs index 92196323e5e4..5e1ef147484b 100644 --- a/src/common/GPOWrapperProjection/GPOWrapper.cs +++ b/src/common/GPOWrapperProjection/GPOWrapper.cs @@ -27,6 +27,11 @@ public static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue() return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredFancyZonesEnabledValue(); } + public static GpoRuleConfigured GetConfiguredCmdNotFoundEnabledValue() + { + return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredCmdNotFoundEnabledValue(); + } + public static GpoRuleConfigured GetConfiguredColorPickerEnabledValue() { return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredColorPickerEnabledValue(); diff --git a/src/common/ManagedCommon/ManagedCommon.csproj b/src/common/ManagedCommon/ManagedCommon.csproj index 53ca704109c8..dbe6eca3f7ba 100644 --- a/src/common/ManagedCommon/ManagedCommon.csproj +++ b/src/common/ManagedCommon/ManagedCommon.csproj @@ -4,7 +4,7 @@ net8.0-windows - win-x64;win-arm64 + win-x64;win-arm64 $(Version).0 Microsoft Corporation PowerToys diff --git a/src/common/ManagedCommon/ModuleType.cs b/src/common/ManagedCommon/ModuleType.cs index 3a489711b08a..0bbfaeeda357 100644 --- a/src/common/ManagedCommon/ModuleType.cs +++ b/src/common/ManagedCommon/ModuleType.cs @@ -9,6 +9,7 @@ public enum ModuleType AlwaysOnTop, Awake, ColorPicker, + CmdNotFound, CropAndLock, EnvironmentVariables, FancyZones, diff --git a/src/common/SettingsAPI/FileWatcher.cpp b/src/common/SettingsAPI/FileWatcher.cpp index 7a0c778cf627..dc25e15c7a92 100644 --- a/src/common/SettingsAPI/FileWatcher.cpp +++ b/src/common/SettingsAPI/FileWatcher.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "FileWatcher.h" +#include std::optional FileWatcher::MyFileTime() { @@ -19,50 +20,47 @@ std::optional FileWatcher::MyFileTime() return result; } -void FileWatcher::Run() +FileWatcher::FileWatcher(const std::wstring& path, std::function callback) : + m_path(path), + m_callback(callback) { - while (1) - { - auto lastWrite = MyFileTime(); - if (!m_lastWrite.has_value()) - { - m_lastWrite = lastWrite; - } - else if (lastWrite.has_value()) - { - if (m_lastWrite->dwHighDateTime != lastWrite->dwHighDateTime || - m_lastWrite->dwLowDateTime != lastWrite->dwLowDateTime) + std::filesystem::path fsPath(path); + m_file_name = fsPath.filename(); + std::transform(m_file_name.begin(), m_file_name.end(), m_file_name.begin(), ::towlower); + m_folder_change_reader = wil::make_folder_change_reader_nothrow( + fsPath.parent_path().c_str(), + false, + wil::FolderChangeEvents::LastWriteTime, + [this](wil::FolderChangeEvent, PCWSTR fileName) { + std::wstring lowerFileName(fileName); + std::transform(lowerFileName.begin(), lowerFileName.end(), lowerFileName.begin(), ::towlower); + + if (m_file_name.compare(fileName) == 0) { - m_lastWrite = lastWrite; - m_callback(); + auto lastWrite = MyFileTime(); + if (!m_lastWrite.has_value()) + { + m_lastWrite = lastWrite; + } + else if (lastWrite.has_value()) + { + if (m_lastWrite->dwHighDateTime != lastWrite->dwHighDateTime || + m_lastWrite->dwLowDateTime != lastWrite->dwLowDateTime) + { + m_lastWrite = lastWrite; + m_callback(); + } + } } - } - - if (WaitForSingleObject(m_abortEvent, m_refreshPeriod) == WAIT_OBJECT_0) - { - return; - } - } -} + }); -FileWatcher::FileWatcher(const std::wstring& path, std::function callback, DWORD refreshPeriod) : - m_refreshPeriod(refreshPeriod), - m_path(path), - m_callback(callback) -{ - m_abortEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); - if (m_abortEvent) + if (!m_folder_change_reader) { - m_thread = std::thread([this]() { Run(); }); + Logger::error(L"Failed to start folder change reader for path {}. {}", path, get_last_error_or_default(GetLastError())); } } FileWatcher::~FileWatcher() { - if (m_abortEvent) - { - SetEvent(m_abortEvent); - m_thread.join(); - CloseHandle(m_abortEvent); - } + m_folder_change_reader.reset(); } diff --git a/src/common/SettingsAPI/FileWatcher.h b/src/common/SettingsAPI/FileWatcher.h index 206c1cd74778..073a33a0c81c 100644 --- a/src/common/SettingsAPI/FileWatcher.h +++ b/src/common/SettingsAPI/FileWatcher.h @@ -11,16 +11,14 @@ class FileWatcher { - DWORD m_refreshPeriod; std::wstring m_path; + std::wstring m_file_name; std::optional m_lastWrite; std::function m_callback; - HANDLE m_abortEvent; - std::thread m_thread; - + wil::unique_folder_change_reader_nothrow m_folder_change_reader; + std::optional MyFileTime(); - void Run(); public: - FileWatcher(const std::wstring& path, std::function callback, DWORD refreshPeriod = 1000); + FileWatcher(const std::wstring& path, std::function callback); ~FileWatcher(); }; diff --git a/src/common/SettingsAPI/SettingsAPI.vcxproj b/src/common/SettingsAPI/SettingsAPI.vcxproj index 306b4a8c3be5..c8a06a1a9e56 100644 --- a/src/common/SettingsAPI/SettingsAPI.vcxproj +++ b/src/common/SettingsAPI/SettingsAPI.vcxproj @@ -47,10 +47,15 @@ {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + @@ -58,5 +63,6 @@ + \ No newline at end of file diff --git a/src/common/SettingsAPI/pch.h b/src/common/SettingsAPI/pch.h index 9526a2f07203..86f6eff28321 100644 --- a/src/common/SettingsAPI/pch.h +++ b/src/common/SettingsAPI/pch.h @@ -9,3 +9,5 @@ #include #include +#include +#include diff --git a/src/common/interop/PowerToys.Interop.vcxproj b/src/common/interop/PowerToys.Interop.vcxproj index 3f792f85ce21..75382dabfb6c 100644 --- a/src/common/interop/PowerToys.Interop.vcxproj +++ b/src/common/interop/PowerToys.Interop.vcxproj @@ -136,12 +136,12 @@ - + 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}. - + \ No newline at end of file diff --git a/src/common/interop/PowerToys.Interop.vcxproj.filters b/src/common/interop/PowerToys.Interop.vcxproj.filters index b54ad522d3d9..cf0425666e52 100644 --- a/src/common/interop/PowerToys.Interop.vcxproj.filters +++ b/src/common/interop/PowerToys.Interop.vcxproj.filters @@ -68,6 +68,9 @@ Resource Files + + + diff --git a/src/common/interop/packages.config b/src/common/interop/packages.config index 3d9798ef5e72..6199e2345ccd 100644 --- a/src/common/interop/packages.config +++ b/src/common/interop/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h index 91dd5faae8ab..cc1b3825cecc 100644 --- a/src/common/logger/logger_settings.h +++ b/src/common/logger/logger_settings.h @@ -67,6 +67,8 @@ struct LogSettings inline const static std::string cropAndLockLoggerName = "crop-and-lock"; inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.txt"; inline const static std::string environmentVariablesLoggerName = "environment-variables"; + inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.txt"; + inline const static std::string cmdNotFoundLoggerName = "cmd-not-found"; inline const static int retention = 30; std::wstring logLevel; LogSettings(); diff --git a/src/common/notifications/NotificationUtil.h b/src/common/notifications/NotificationUtil.h new file mode 100644 index 000000000000..8fcf3d91d8c3 --- /dev/null +++ b/src/common/notifications/NotificationUtil.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +#include "Generated Files/resource.h" + +namespace notifications +{ + // Non-Localizable strings + namespace NonLocalizable + { + const wchar_t RunAsAdminInfoPage[] = L"https://aka.ms/powertoysDetectedElevatedHelp"; + const wchar_t ToastNotificationButtonUrl[] = L"powertoys://cant_drag_elevated_disable/"; + } + + inline void WarnIfElevationIsRequired(std::wstring title, std::wstring message, std::wstring button1, std::wstring button2) + { + using namespace NonLocalizable; + + auto settings = PTSettingsHelper::load_general_settings(); + auto enableWarningsElevatedApps = settings.GetNamedBoolean(L"enable_warnings_elevated_apps", true); + + static bool warning_shown = false; + if (enableWarningsElevatedApps && !warning_shown && !is_toast_disabled(ElevatedDontShowAgainRegistryPath, ElevatedDisableIntervalInDays)) + { + std::vector actions = { + link_button{ button1, RunAsAdminInfoPage }, + link_button{ button2, ToastNotificationButtonUrl } + }; + show_toast_with_activations(message, + title, + {}, + std::move(actions)); + warning_shown = true; + } + } +} \ No newline at end of file diff --git a/src/common/notifications/dont_show_again.h b/src/common/notifications/dont_show_again.h index 98657b1e7e0c..f1cdd0f32242 100644 --- a/src/common/notifications/dont_show_again.h +++ b/src/common/notifications/dont_show_again.h @@ -4,8 +4,8 @@ namespace notifications { - const inline wchar_t CantDragElevatedDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})"; - const inline int64_t CantDragElevatedDisableIntervalInDays = 30; + const inline wchar_t ElevatedDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{e16ea82f-6d94-4f30-bb02-d6d911588afd})"; + const inline int64_t ElevatedDisableIntervalInDays = 30; const inline wchar_t PreviewModulesDontShowAgainRegistryPath[] = LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain\{7e29e2b2-b31c-4dcd-b7b0-79c078b02430})"; const inline int64_t PreviewModulesDisableIntervalInDays = 30; diff --git a/src/common/notifications/notifications.vcxproj b/src/common/notifications/notifications.vcxproj index c66752318802..c0438616ad57 100644 --- a/src/common/notifications/notifications.vcxproj +++ b/src/common/notifications/notifications.vcxproj @@ -27,6 +27,7 @@ + @@ -43,7 +44,7 @@ - + @@ -51,6 +52,6 @@ - + \ No newline at end of file diff --git a/src/common/notifications/packages.config b/src/common/notifications/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/common/notifications/packages.config +++ b/src/common/notifications/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/common/updating/packages.config b/src/common/updating/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/common/updating/packages.config +++ b/src/common/updating/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/common/updating/updating.vcxproj b/src/common/updating/updating.vcxproj index 97089b03a1f1..dfd2817d8376 100644 --- a/src/common/updating/updating.vcxproj +++ b/src/common/updating/updating.vcxproj @@ -58,7 +58,7 @@ - + @@ -66,6 +66,6 @@ - + \ No newline at end of file diff --git a/src/common/updating/updating.vcxproj.filters b/src/common/updating/updating.vcxproj.filters index 5952e2f3bd1c..4eb124fc0c65 100644 --- a/src/common/updating/updating.vcxproj.filters +++ b/src/common/updating/updating.vcxproj.filters @@ -45,4 +45,7 @@ + + + \ No newline at end of file diff --git a/src/common/utils/elevation.h b/src/common/utils/elevation.h index 177a4b24e29a..62ab1c75d5f2 100644 --- a/src/common/utils/elevation.h +++ b/src/common/utils/elevation.h @@ -494,3 +494,30 @@ inline bool check_user_is_admin() freeMemory(pSID, pGroupInfo); return false; } + +inline bool IsProcessOfWindowElevated(HWND window) +{ + DWORD pid = 0; + GetWindowThreadProcessId(window, &pid); + if (!pid) + { + return false; + } + + wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, + FALSE, + pid) }; + + wil::unique_handle token; + + if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token)) + { + TOKEN_ELEVATION elevation; + DWORD size; + if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size)) + { + return elevation.TokenIsElevated != 0; + } + } + return false; +} diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index be88e0d22d77..016bd744cc28 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -24,6 +24,7 @@ namespace powertoys_gpo { const std::wstring POLICY_CONFIGURE_ENABLED_GLOBAL_ALL_UTILITIES = L"ConfigureGlobalUtilityEnabledState"; const std::wstring POLICY_CONFIGURE_ENABLED_ALWAYS_ON_TOP = L"ConfigureEnabledUtilityAlwaysOnTop"; const std::wstring POLICY_CONFIGURE_ENABLED_AWAKE = L"ConfigureEnabledUtilityAwake"; + const std::wstring POLICY_CONFIGURE_ENABLED_CMD_NOT_FOUND = L"ConfigureEnabledUtilityCmdNotFound"; const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker"; const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock"; const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones"; @@ -224,6 +225,11 @@ namespace powertoys_gpo { return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_AWAKE); } + inline gpo_rule_configured_t getConfiguredCmdNotFoundEnabledValue() + { + return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_CMD_NOT_FOUND); + } + inline gpo_rule_configured_t getConfiguredColorPickerEnabledValue() { return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_COLOR_PICKER); diff --git a/src/common/utils/window.h b/src/common/utils/window.h index cfcf20534ac0..dbce3a95ee03 100644 --- a/src/common/utils/window.h +++ b/src/common/utils/window.h @@ -6,9 +6,13 @@ #include #include +#include +#include // Initializes and runs windows message loop -inline int run_message_loop(const bool until_idle = false, const std::optional timeout_ms = {}) +inline int run_message_loop(const bool until_idle = false, + const std::optional timeout_ms = {}, + std::unordered_map> wm_app_msg_callbacks = {}) { MSG msg{}; bool stop = false; @@ -24,11 +28,16 @@ inline int run_message_loop(const bool until_idle = false, const std::optionalsecond(); } + if (timeout_ms.has_value()) { KillTimer(nullptr, timerId); } + return static_cast(msg.wParam); } @@ -60,7 +69,7 @@ template inline T GetWindowCreateParam(LPARAM lparam) { static_assert(sizeof(T) <= sizeof(void*)); - T data{ static_cast (reinterpret_cast(lparam)->lpCreateParams) }; + T data{ static_cast(reinterpret_cast(lparam)->lpCreateParams) }; return data; } @@ -74,5 +83,5 @@ inline void StoreWindowParam(HWND window, T data) template inline T GetWindowParam(HWND window) { - return reinterpret_cast (GetWindowLongPtrW(window, GWLP_USERDATA)); + return reinterpret_cast(GetWindowLongPtrW(window, GWLP_USERDATA)); } diff --git a/src/common/version/version.h b/src/common/version/version.h index 8c3ffd302d16..b30061956599 100644 --- a/src/common/version/version.h +++ b/src/common/version/version.h @@ -13,7 +13,7 @@ #define PRODUCT_VERSION_STRING FILE_VERSION_STRING #define COMPANY_NAME "Microsoft Corporation" -#define COPYRIGHT_NOTE "Copyright (C) 2023 Microsoft Corporation" +#define COPYRIGHT_NOTE "Copyright (C) 2024 Microsoft Corporation" #define PRODUCT_NAME "PowerToys" #include diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index 0b135f665e5d..42ebfe334996 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -1,11 +1,11 @@ - + - + @@ -15,6 +15,7 @@ + @@ -59,6 +60,16 @@ + + + + + + + + + + diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index de04d77c8bd4..2eb3c700b3e6 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -1,7 +1,7 @@ - + PowerToys PowerToys @@ -17,6 +17,7 @@ PowerToys version 0.73.0 or later PowerToys version 0.75.0 or later PowerToys version 0.76.0 or later + PowerToys version 0.77.0 or later This policy configures the enabled state for all PowerToys utilities. @@ -112,6 +113,7 @@ Note: Changes require a restart of PowerToys Run. Always On Top: Configure enabled state Awake: Configure enabled state Color Picker: Configure enabled state + Command Not Found: Configure enabled state Crop And Lock: Configure enabled state Environment Variables: Configure enabled state FancyZones: Configure enabled state diff --git a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj index 04833d956711..fabdbc9c2531 100644 --- a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj +++ b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj @@ -155,8 +155,8 @@ - + @@ -164,7 +164,7 @@ - + \ No newline at end of file diff --git a/src/modules/CropAndLock/CropAndLock/packages.config b/src/modules/CropAndLock/CropAndLock/packages.config index 1a0b177ac118..fe9625b6da89 100644 --- a/src/modules/CropAndLock/CropAndLock/packages.config +++ b/src/modules/CropAndLock/CropAndLock/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj b/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj index 7c26becd5eab..6c44320941b7 100644 --- a/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj +++ b/src/modules/CropAndLock/CropAndLockModuleInterface/CropAndLockModuleInterface.vcxproj @@ -102,8 +102,8 @@ - + @@ -112,7 +112,7 @@ - + \ No newline at end of file diff --git a/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config b/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config +++ b/src/modules/CropAndLock/CropAndLockModuleInterface/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj index 4d544d3b658b..2c7035035d17 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariables.csproj @@ -58,11 +58,6 @@ - - - https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json - - diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml index 37ee7095fe12..5be8dc688db6 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/Views/MainPage.xaml @@ -2,14 +2,14 @@ x:Class="EnvironmentVariables.Views.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" xmlns:converters="using:EnvironmentVariables.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:ic="using:Microsoft.Xaml.Interactions.Core" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:EnvironmentVariables.Models" - xmlns:toolkitConverters="using:CommunityToolkit.WinUI.Converters" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters" xmlns:ui="using:CommunityToolkit.WinUI" x:Name="RootPage" mc:Ignorable="d"> @@ -22,12 +22,12 @@ - - + - - + + - + - + @@ -98,12 +98,12 @@ - - - + + @@ -194,22 +194,22 @@ - - + - + - + @@ -253,17 +253,17 @@ - - + - + - + - - + + - - + + - + - + - - - - - + - - + @@ -676,8 +676,8 @@ x:Uid="AddNewVariableValue" Margin="0,16,0,0" /> - - + + - - + + diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/Microsoft.WindowsAppSdk.manifest b/src/modules/EnvironmentVariables/EnvironmentVariables/Microsoft.WindowsAppSdk.manifest new file mode 100644 index 000000000000..045e30ad152b --- /dev/null +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/Microsoft.WindowsAppSdk.manifest @@ -0,0 +1,2 @@ + +PerMonitorV2 \ No newline at end of file diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj index 6d5ada2cef19..77c2b3bdd73f 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj +++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj @@ -64,11 +64,6 @@ false - - - https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json - - diff --git a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml index 848974dce0ef..72519afe7221 100644 --- a/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml +++ b/src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithXAML/Views/MainPage.xaml @@ -8,17 +8,17 @@ xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interop="using:FileLocksmith.Interop" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:tkControls="using:CommunityToolkit.WinUI.Controls" - xmlns:tkConverters="using:CommunityToolkit.WinUI.Converters" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters" xmlns:ui="using:CommunityToolkit.WinUI" mc:Ignorable="d"> - - - - + + 0 - - + + - - + + - - - + + + - - + + - - - + + + - + @@ -162,9 +162,9 @@ - - - + + + diff --git a/src/modules/FileLocksmith/FileLocksmithUI/Microsoft.WindowsAppSdk.manifest b/src/modules/FileLocksmith/FileLocksmithUI/Microsoft.WindowsAppSdk.manifest new file mode 100644 index 000000000000..4f4d25cd7e44 --- /dev/null +++ b/src/modules/FileLocksmith/FileLocksmithUI/Microsoft.WindowsAppSdk.manifest @@ -0,0 +1,2 @@ + +true/PMPerMonitorV2, PerMonitor \ No newline at end of file diff --git a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj index c4ee03077a1a..3e789295e5f4 100644 --- a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj +++ b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj @@ -22,6 +22,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml index 16a6ec5c61ca..700c9da14463 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml +++ b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml @@ -2,7 +2,6 @@ x:Class="Hosts.Views.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helpers="using:Hosts.Helpers" xmlns:i="using:Microsoft.Xaml.Interactivity" @@ -10,6 +9,7 @@ xmlns:local="using:Hosts.Views" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:Hosts.Models" + xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters" xmlns:ui="using:CommunityToolkit.WinUI" x:Name="Page" Loaded="Page_Loaded" @@ -21,17 +21,17 @@ - - - - + + - +true/PMPerMonitorV2, PerMonitor \ No newline at end of file diff --git a/src/modules/Hosts/Hosts/Settings/UserSettings.cs b/src/modules/Hosts/Hosts/Settings/UserSettings.cs index 237dcc1bbba7..5c73dd3a172b 100644 --- a/src/modules/Hosts/Hosts/Settings/UserSettings.cs +++ b/src/modules/Hosts/Hosts/Settings/UserSettings.cs @@ -17,7 +17,7 @@ public class UserSettings : IUserSettings private const string HostsModuleName = "Hosts"; private const int MaxNumberOfRetry = 5; - private readonly ISettingsUtils _settingsUtils; + private readonly SettingsUtils _settingsUtils; private readonly IFileSystemWatcher _watcher; private readonly object _loadingSettingsLock = new object(); diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index b9a95730b592..840bb868a5de 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,7 +1,7 @@  - - + + true @@ -144,21 +144,21 @@ - - - + + + 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}. - - - - - + + + + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index abb0cf1bdb60..627512b77879 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -1,7 +1,7 @@  - - - + + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolUI/Microsoft.WindowsAppSdk.manifest b/src/modules/MeasureTool/MeasureToolUI/Microsoft.WindowsAppSdk.manifest new file mode 100644 index 000000000000..5cb144b58d49 --- /dev/null +++ b/src/modules/MeasureTool/MeasureToolUI/Microsoft.WindowsAppSdk.manifest @@ -0,0 +1,2 @@ + +true/PMPerMonitorV2, PerMonitor \ No newline at end of file diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp index 29ab5419ec50..51b93c37a66b 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.cpp @@ -75,6 +75,11 @@ struct SuperSonar static constexpr int FinalAlphaDenominator = 100; winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr }; + // Don't consider movements started past these milliseconds to detect shaking. + int m_shakeIntervalMs = FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS; + // By which factor must travelled distance be than the diagonal of the rectangle containing the movements. (value in percent) + int m_shakeFactor = FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR; + private: // Save the mouse movement that occurred in any direction. @@ -87,10 +92,6 @@ struct SuperSonar // Raw Input may give relative or absolute values. Need to take each case into account. bool m_seenAnAbsoluteMousePosition = false; POINT m_lastAbsolutePosition = { 0, 0 }; - // Don't consider movements started past these milliseconds to detect shaking. - static constexpr LONG ShakeIntervalMs = 1000; - // By which factor must travelled distance be than the diagonal of the rectangle containing the movements. - static constexpr float ShakeFactor = 4.0f; static inline byte GetSign(LONG const& num) { @@ -398,7 +399,7 @@ void SuperSonar::OnSonarKeyboardInput(RAWINPUT const& input) template void SuperSonar::DetectShake() { - ULONGLONG shakeStartTick = GetTickCount64() - ShakeIntervalMs; + ULONGLONG shakeStartTick = GetTickCount64() - m_shakeIntervalMs; // Prune the story of movements for those movements that started too long ago. std::erase_if(m_movementHistory, [shakeStartTick](const PointerRecentMovement& movement) { return movement.tick < shakeStartTick; }); @@ -429,7 +430,7 @@ void SuperSonar::DetectShake() double rectangleHeight = static_cast(maxY) - minY; double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight); - if (diagonal > 0 && distanceTravelled / diagonal > ShakeFactor) + if (diagonal > 0 && distanceTravelled / diagonal > (m_shakeFactor/100.f)) { m_movementHistory.clear(); StartSonar(); @@ -767,6 +768,8 @@ struct CompositionSpotlight : SuperSonar m_sonarZoomFactor = settings.spotlightInitialZoom; m_excludedApps = settings.excludedApps; m_shakeMinimumDistance = settings.shakeMinimumDistance; + m_shakeIntervalMs = settings.shakeIntervalMs; + m_shakeFactor = settings.shakeFactor; } else { @@ -794,6 +797,8 @@ struct CompositionSpotlight : SuperSonar m_sonarZoomFactor = localSettings.spotlightInitialZoom; m_excludedApps = localSettings.excludedApps; m_shakeMinimumDistance = localSettings.shakeMinimumDistance; + m_shakeIntervalMs = localSettings.shakeIntervalMs; + m_shakeFactor = localSettings.shakeFactor; UpdateMouseSnooping(); // For the shake mouse activation method // Apply new settings to runtime composition objects. diff --git a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h index 149e52842bdd..d55b72a34d02 100644 --- a/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h +++ b/src/modules/MouseUtils/FindMyMouse/FindMyMouse.h @@ -19,6 +19,8 @@ constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500; constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9; constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleLeftControlKey; constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE = 1000; +constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS = 1000; +constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR = 400; // 400 percent struct FindMyMouseSettings { @@ -31,6 +33,8 @@ struct FindMyMouseSettings int animationDurationMs = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS; int spotlightInitialZoom = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM; int shakeMinimumDistance = FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE; + int shakeIntervalMs = FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS; + int shakeFactor = FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR; std::vector excludedApps; }; diff --git a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp index 2d1aa55accaf..d0102ba5076a 100644 --- a/src/modules/MouseUtils/FindMyMouse/dllmain.cpp +++ b/src/modules/MouseUtils/FindMyMouse/dllmain.cpp @@ -23,6 +23,8 @@ namespace const wchar_t JSON_KEY_SPOTLIGHT_INITIAL_ZOOM[] = L"spotlight_initial_zoom"; const wchar_t JSON_KEY_EXCLUDED_APPS[] = L"excluded_apps"; const wchar_t JSON_KEY_SHAKING_MINIMUM_DISTANCE[] = L"shaking_minimum_distance"; + const wchar_t JSON_KEY_SHAKING_INTERVAL_MS[] = L"shaking_interval_ms"; + const wchar_t JSON_KEY_SHAKING_FACTOR[] = L"shaking_factor"; const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"activation_shortcut"; } @@ -396,6 +398,42 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings) { Logger::warn("Failed to initialize Shaking Minimum Distance from settings. Will use default value"); } + try + { + // Parse Shaking Interval Milliseconds + auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHAKING_INTERVAL_MS); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.shakeIntervalMs = value; + } + else + { + throw std::runtime_error("Invalid Shaking Interval Milliseconds value"); + } + } + catch (...) + { + Logger::warn("Failed to initialize Shaking Interval Milliseconds from settings. Will use default value"); + } + try + { + // Parse Shaking Factor + auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHAKING_FACTOR); + int value = static_cast(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE)); + if (value >= 0) + { + findMyMouseSettings.shakeFactor = value; + } + else + { + throw std::runtime_error("Invalid Shaking Factor value"); + } + } + catch (...) + { + Logger::warn("Failed to initialize Shaking Factor from settings. Will use default value"); + } try { diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj index 86100f4a8dfa..66b91f144344 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj +++ b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj @@ -22,6 +22,10 @@ + + + runtime + diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs index 348c1f149eaa..0020edb552b0 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs @@ -45,6 +45,10 @@ internal partial class Common private static string lastDragDropFile; private static long clipboardCopiedTime; + internal static readonly char[] Comma = new char[] { ',' }; + internal static readonly char[] Star = new char[] { '*' }; + internal static readonly char[] NullSeparator = new char[] { '\0' }; + internal static ID LastIDWithClipboardData { get; set; } internal static string LastDragDropFile @@ -406,7 +410,7 @@ private static void ConnectAndGetData(object postAction) try { - remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(new char[] { ',' })[1] : Common.LastMachineWithClipboardData; + remoteMachine = postAct.Contains("mspaint,") ? postAct.Split(Comma)[1] : Common.LastMachineWithClipboardData; remoteMachine = remoteMachine.Trim(); @@ -518,7 +522,7 @@ internal static void ReceiveAndProcessClipboardData(string remoteMachine, Socket fileName = Common.GetStringU(header).Replace("\0", string.Empty); Common.LogDebug("Header: " + fileName); - string[] headers = fileName.Split(new char[] { '*' }); + string[] headers = fileName.Split(Star); if (headers.Length < 2 || !long.TryParse(headers[0], out long dataSize)) { @@ -973,7 +977,7 @@ internal static void SetClipboardData(byte[] data) foreach (string txt in texts) { - if (string.IsNullOrEmpty(txt.Trim(new char[] { '\0' }))) + if (string.IsNullOrEmpty(txt.Trim(NullSeparator))) { continue; } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs index 33f528941947..d557a8223c78 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs @@ -22,7 +22,9 @@ namespace MouseWithoutBorders { internal partial class Common { - private static SymmetricAlgorithm symAl; +#pragma warning disable SYSLIB0021 + private static AesCryptoServiceProvider symAl; +#pragma warning restore SYSLIB0021 private static string myKey; private static uint magicNumber; private static Random ran = new(); // Used for non encryption related functionality. diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs index 54affce04baf..5202eb6cc9a8 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs @@ -313,7 +313,8 @@ internal static void RunDDHelper(bool cleanUp = false) HasSwitchedMachineSinceLastCopy = true; // Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0); - if (Process.GetProcessesByName(HelperProcessName)?.Any() != true) + var processes = Process.GetProcessesByName(HelperProcessName); + if (processes?.Length == 0) { Log("Unable to start helper process."); Common.ShowToolTip("Error starting Mouse Without Borders Helper, clipboard sharing will not work!", 5000, ToolTipIcon.Error); @@ -325,7 +326,8 @@ internal static void RunDDHelper(bool cleanUp = false) } else { - if (Process.GetProcessesByName(HelperProcessName)?.Any() == true) + var processes = Process.GetProcessesByName(HelperProcessName); + if (processes?.Length > 0) { Log("Helper process found running."); } @@ -432,7 +434,7 @@ internal static bool GetUserName() { if (string.IsNullOrEmpty(Setting.Values.Username) && !Common.RunOnLogonDesktop) { - if (Program.User.ToLower(CultureInfo.CurrentCulture).Contains("system")) + if (Program.User.Contains("system", StringComparison.CurrentCultureIgnoreCase)) { _ = Common.ImpersonateLoggedOnUserAndDoSomething(() => { diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs index 02dbfd707b02..80ffe9fd8415 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs @@ -84,12 +84,11 @@ internal static bool ImpersonateLoggedOnUserAndDoSomething(Action targetFunc) } } - [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")] internal static int CreateProcessInInputDesktopSession(string commandLine, string arg, string desktop, short wShowWindow, bool lowIntegrity = false) // As user who runs explorer.exe { - if (!Program.User.ToLower(CultureInfo.InvariantCulture).Contains("system")) + if (!Program.User.Contains("system", StringComparison.InvariantCultureIgnoreCase)) { ProcessStartInfo s = new(commandLine, arg); s.WindowStyle = wShowWindow != 0 ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden; diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs index 2673d95c48ab..1b65270fd0b4 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs @@ -45,7 +45,7 @@ internal static void StartMouseWithoutBordersService(string desktopToRunMouseWit { Process[] ps = Process.GetProcessesByName("MouseWithoutBordersSvc"); - if (ps.Any()) + if (ps.Length != 0) { if (DateTime.UtcNow - lastStartServiceTime < TimeSpan.FromSeconds(5)) { diff --git a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs index adb0c90bd0ec..8f6224e3f18a 100644 --- a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs +++ b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs @@ -353,6 +353,7 @@ internal static void MouseClickDotForm(int x, int y) private static bool ctrlDown; private static bool altDown; private static bool shiftDown; + internal static readonly string[] Args = new string[] { "CAD" }; private static void ResetModifiersState(HotkeySettings matchingHotkey) { @@ -456,7 +457,7 @@ private static void InputProcessKeyEx(int vkCode, int flags, out bool eatKey) if (ctrlDown && altDown) { ctrlDown = altDown = false; - new ServiceController("MouseWithoutBordersSvc").Start(new string[] { "CAD" }); + new ServiceController("MouseWithoutBordersSvc").Start(Args); } break; diff --git a/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs b/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs index 51bbd88921e6..2420e43b3771 100644 --- a/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs +++ b/src/modules/MouseWithoutBorders/App/Class/MachinePool.cs @@ -156,7 +156,7 @@ public void Initialize(IEnumerable machineNames) } else if (list.Count >= 4) { - throw new ArgumentException("machineNames.Length > Common.MAX_MACHINE"); + throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {Common.MAX_MACHINE}. Actual count: {list.Count}."); } _ = LearnMachine(name); @@ -178,7 +178,7 @@ public void Initialize(IEnumerable infos) } else if (list.Count >= 4) { - throw new ArgumentException("infos.Length > Common.MAX_MACHINE"); + throw new ArgumentException($"The number of machines exceeded the maximum allowed limit of {Common.MAX_MACHINE}. Actual count: {list.Count}."); } _ = LearnMachine(inf.Name); diff --git a/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs b/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs index 66175d8f552c..d2162273fb7f 100644 --- a/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs +++ b/src/modules/MouseWithoutBorders/App/Class/MachinePoolHelpers.cs @@ -8,6 +8,9 @@ namespace MouseWithoutBorders.Class { internal static class MachinePoolHelpers { + internal static readonly char[] Comma = new char[] { ',' }; + internal static readonly char[] Colon = new char[] { ':' }; + internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string s) { if (s == null) @@ -15,7 +18,7 @@ internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string throw new ArgumentNullException(s); } - string[] st = s.Split(new char[] { ',' }); + string[] st = s.Split(Comma); if (st.Length < Common.MAX_MACHINE) { @@ -25,7 +28,7 @@ internal static MachineInf[] LoadMachineInfoFromMachinePoolStringSetting(string MachineInf[] rv = new MachineInf[Common.MAX_MACHINE]; for (int i = 0; i < Common.MAX_MACHINE; i++) { - string[] mc = st[i].Split(new char[] { ':' }); + string[] mc = st[i].Split(Colon); if (mc.Length == 2) { rv[i].Name = mc[0]; diff --git a/src/modules/MouseWithoutBorders/App/Class/Setting.cs b/src/modules/MouseWithoutBorders/App/Class/Setting.cs index 46f647c15180..edb74665cb27 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Setting.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Setting.cs @@ -38,7 +38,7 @@ internal class Settings { internal bool Changed; - private readonly ISettingsUtils _settingsUtils; + private readonly SettingsUtils _settingsUtils; private readonly object _loadingSettingsLock = new object(); private readonly IFileSystemWatcher _watcher; diff --git a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs index 106ba6941ae2..a3ea7e3c0862 100644 --- a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs +++ b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs @@ -887,14 +887,14 @@ void ClientThread(object obj) if (!string.IsNullOrEmpty(Setting.Values.Name2IP)) { - string[] name2ip = Setting.Values.Name2IP.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + string[] name2ip = Setting.Values.Name2IP.Split(Separator, StringSplitOptions.RemoveEmptyEntries); string[] nameNip; if (name2ip != null) { foreach (string st in name2ip) { - nameNip = st.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + nameNip = st.Split(BlankSeparator, StringSplitOptions.RemoveEmptyEntries); if (nameNip != null && nameNip.Length >= 2 && nameNip[0].Trim().Equals(machineName, StringComparison.OrdinalIgnoreCase) && IPAddress.TryParse(nameNip[1].Trim(), out IPAddress ip) && !validAddressesSt.Contains("[" + ip.ToString() + "]") @@ -1063,7 +1063,7 @@ private bool CheckForSameSubNet(List validatedAddresses, string machi List localIPv4Addresses = GetMyIPv4Addresses().ToList(); - if (!localIPv4Addresses.Any()) + if (localIPv4Addresses.Count == 0) { Common.Log($"No IPv4 resolved from the local machine: {Common.MachineName}"); return true; @@ -1234,6 +1234,8 @@ private void FlagReopenSocketIfNeeded(Exception e) } private long lastRemoteMachineID; + internal static readonly string[] Separator = new string[] { "\r\n" }; + internal static readonly char[] BlankSeparator = new char[] { ' ' }; private void MainTCPRoutine(TcpSk tcp, string machineName, bool isClient) { diff --git a/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs b/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs index 6e5478d71b96..f7ea7ba09437 100644 --- a/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs +++ b/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs @@ -104,6 +104,7 @@ internal void Close() } private static bool logged; + internal static readonly string[] Separator = new[] { " " }; private void LogError(string log) { @@ -146,7 +147,7 @@ private void LogError(string log) try { // Assuming the format of netstat's output is fixed. - pid = int.Parse(portLogLine.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).Last(), CultureInfo.CurrentCulture); + pid = int.Parse(portLogLine.Split(Separator, StringSplitOptions.RemoveEmptyEntries).Last(), CultureInfo.CurrentCulture); process = Process.GetProcessById(pid); } catch (Exception) diff --git a/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs b/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs index f6cf932811f8..a8195a79ad28 100644 --- a/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs +++ b/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs @@ -123,13 +123,14 @@ private void CloseWindowButtonClick(object sender, EventArgs e) } private string lastMessage = string.Empty; + private static readonly string[] Separator = new string[] { "\r\n" }; internal void ShowTip(ToolTipIcon icon, string msg, int durationInMilliseconds) { int x = 0; string text = msg + $"\r\n {(lastMessage.Equals(msg, StringComparison.OrdinalIgnoreCase) ? string.Empty : $"\r\nPrevious message/error: {lastMessage}")} "; lastMessage = msg; - int y = (-text.Split(new string[] { "\r\n" }, StringSplitOptions.None).Length * 15) - 30; + int y = (-text.Split(Separator, StringSplitOptions.None).Length * 15) - 30; toolTipManual.Hide(this); diff --git a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs index c786498f971b..cea9daafc95a 100644 --- a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs +++ b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs @@ -742,11 +742,13 @@ private void LoadSettingsToUI() LoadMachines(); } + internal static readonly string[] Separator = new string[] { "\r\n" }; + internal void ShowTip(ToolTipIcon icon, string text, int duration) { int x = 0; text += "\r\n "; - int y = (-text.Split(new string[] { "\r\n" }, StringSplitOptions.None).Length * 15) - 30; + int y = (-text.Split(Separator, StringSplitOptions.None).Length * 15) - 30; toolTipManual.Hide(this); diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj index c9e3417ab3cd..276c65385530 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj +++ b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj @@ -58,20 +58,20 @@ - + - + - + 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}. - + \ No newline at end of file diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters index 27d483b17418..e3dbd34dc7ff 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters +++ b/src/modules/MouseWithoutBorders/ModuleInterface/MouseWithoutBordersModuleInterface.vcxproj.filters @@ -34,13 +34,13 @@ {8d479404-964b-4eb1-8fe8-554be3e68c9b} - - - + + + \ No newline at end of file diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/packages.config b/src/modules/MouseWithoutBorders/ModuleInterface/packages.config index 3d9798ef5e72..6199e2345ccd 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/packages.config +++ b/src/modules/MouseWithoutBorders/ModuleInterface/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs b/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs index 1e11a83801f7..241e1f7433db 100644 --- a/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs +++ b/src/modules/PowerOCR/PowerOCR/Helpers/ImageMethods.cs @@ -148,6 +148,8 @@ internal static async Task GetClickedWord(Window passedWindow, System.Wi return resultText.Trim(); } + internal static readonly char[] Separator = new char[] { '\n', '\r' }; + public static async Task ExtractText(Bitmap bmp, Language? preferredLanguage, System.Windows.Point? singlePoint = null) { Language? selectedLanguage = preferredLanguage ?? GetOCRLanguage(); @@ -211,7 +213,7 @@ public static async Task ExtractText(Bitmap bmp, Language? preferredLang if (culture.TextInfo.IsRightToLeft) { - string[] textListLines = text.ToString().Split(new char[] { '\n', '\r' }); + string[] textListLines = text.ToString().Split(Separator); _ = text.Clear(); foreach (string textLine in textListLines) diff --git a/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs b/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs index 06d3afa009ed..812fd985f575 100644 --- a/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs +++ b/src/modules/PowerOCR/PowerOCR/Models/ResultTable.cs @@ -239,7 +239,7 @@ private static List CalculateRowAreas(Rectangle rectCanvasSize, int hitGrid return rowAreas; } - private static void CheckIntersectionsWithWordBorders(int hitGridSpacing, ICollection wordBorders, ICollection rowAreas, int i, Rect horizontalLineRect) + private static void CheckIntersectionsWithWordBorders(int hitGridSpacing, ICollection wordBorders, List rowAreas, int i, Rect horizontalLineRect) { foreach (WordBorder wb in wordBorders) { diff --git a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs index d08bc5b02038..a1a5ba6d726a 100644 --- a/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs +++ b/src/modules/PowerOCR/PowerOCR/Settings/UserSettings.cs @@ -16,7 +16,7 @@ namespace PowerOCR.Settings [Export(typeof(IUserSettings))] public class UserSettings : IUserSettings { - private readonly ISettingsUtils _settingsUtils; + private readonly SettingsUtils _settingsUtils; private const string PowerOcrModuleName = "TextExtractor"; private const string DefaultActivationShortcut = "Win + Shift + O"; private const int MaxNumberOfRetry = 5; diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.rc b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.base.rc similarity index 55% rename from src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.rc rename to src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.base.rc index 5fa3c8b90d58..c4e1b7cfc5e9 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.rc +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.base.rc @@ -1,27 +1,27 @@ #include #include "resource.h" -#include "../../../common/version/version.h" +#include "../../../../common/version/version.h" #define APSTUDIO_READONLY_SYMBOLS #include "winres.h" #undef APSTUDIO_READONLY_SYMBOLS 1 VERSIONINFO -FILEVERSION FILE_VERSION -PRODUCTVERSION PRODUCT_VERSION -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEVERSION FILE_VERSION + PRODUCTVERSION PRODUCT_VERSION + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG -FILEFLAGS VS_FF_DEBUG + FILEFLAGS VS_FF_DEBUG #else -FILEFLAGS 0x0L + FILEFLAGS 0x0L #endif -FILEOS VOS_NT_WINDOWS32 -FILETYPE VFT_DLL -FILESUBTYPE VFT2_UNKNOWN + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BLOCK "040904b0" BEGIN VALUE "CompanyName", COMPANY_NAME VALUE "FileDescription", FILE_DESCRIPTION @@ -35,6 +35,15 @@ BEGIN END BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + VALUE "Translation", 0x409, 1200 END END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "../../Assets/AlwaysOnTop.ico" diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp index 31c774ea91c8..b9ec910c62f7 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp @@ -8,6 +8,10 @@ #include #include +#include +#include +#include + #include #include @@ -489,6 +493,10 @@ void AlwaysOnTop::HandleWinHookEvent(WinHookEvent* data) noexcept break; case EVENT_SYSTEM_FOREGROUND: { + if (!is_process_elevated() && IsProcessOfWindowElevated(data->hwnd)) + { + notifications::WarnIfElevationIsRequired(GET_RESOURCE_STRING(IDS_ALWAYSONTOP), GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED), GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_LEARN_MORE), GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_DIALOG_DONT_SHOW_AGAIN)); + } RefreshBorders(); } break; diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj index 169a53e24307..39233d7ed593 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj @@ -3,6 +3,9 @@ + + + true @@ -141,9 +144,12 @@ + + + @@ -168,15 +174,25 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {1d5be09d-78c0-4fd7-af00-ae7c1af7c525} + + + + + + + + - + - + @@ -184,6 +200,6 @@ - + \ No newline at end of file diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters index 4a12888f4af1..156373674f95 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {A74B5AAC-E913-410D-8941-D73346CF47AE} + @@ -49,8 +52,22 @@ Source Files + + + Generated Files + + + + Resource Files + + + Header Files + + + Resource Files + @@ -98,10 +115,19 @@ Header Files + + Generated Files + + + Header Files + - + + + + Resource Files - + \ No newline at end of file diff --git a/src/modules/alwaysontop/AlwaysOnTop/Resources.resx b/src/modules/alwaysontop/AlwaysOnTop/Resources.resx new file mode 100644 index 000000000000..17ce38ca6922 --- /dev/null +++ b/src/modules/alwaysontop/AlwaysOnTop/Resources.resx @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + AlwaysOnTop + AlwaysOnTop is a product name, keep as is. + + + We've detected an application running with administrator privileges. This will prevent certain interactions with these applications. + administrator is context of user account. + + + Learn more + + + Don't show again + + \ No newline at end of file diff --git a/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp b/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp index da335bb101ac..763a9770f757 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/WindowBorder.cpp @@ -126,6 +126,11 @@ bool WindowBorder::Init(HINSTANCE hinstance) return false; } + if (!SetLayeredWindowAttributes(m_window, 0, 255, LWA_ALPHA)) + { + return false; + } + // set position of the border-window behind the tracking window // helps to prevent border overlapping (happens after turning borders off and on) SetWindowPos(m_trackingWindow diff --git a/src/modules/alwaysontop/AlwaysOnTop/packages.config b/src/modules/alwaysontop/AlwaysOnTop/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/packages.config +++ b/src/modules/alwaysontop/AlwaysOnTop/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/alwaysontop/AlwaysOnTop/pch.h b/src/modules/alwaysontop/AlwaysOnTop/pch.h index 1a3b9fd1fd2a..848c90fea23d 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/pch.h +++ b/src/modules/alwaysontop/AlwaysOnTop/pch.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/src/modules/alwaysontop/AlwaysOnTop/resource.base.h b/src/modules/alwaysontop/AlwaysOnTop/resource.base.h new file mode 100644 index 000000000000..9dd2bfb332b9 --- /dev/null +++ b/src/modules/alwaysontop/AlwaysOnTop/resource.base.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by AlwaysOnTop.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys.AlwaysOnTop" +#define INTERNAL_NAME "PowerToys.AlwaysOnTop" +#define ORIGINAL_FILENAME "PowerToys.AlwaysOnTop.exe" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj index 298bb2c8c69a..ad8e3e138bb2 100644 --- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj +++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj @@ -68,7 +68,7 @@ - + @@ -77,6 +77,6 @@ - + \ No newline at end of file diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters index 5b442a35d885..66fe083d3a16 100644 --- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters +++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/AlwaysOnTopModuleInterface.vcxproj.filters @@ -47,4 +47,7 @@ Resource Files + + + \ No newline at end of file diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config +++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/alwaysontop/Assets/AlwaysOnTop.ico b/src/modules/alwaysontop/Assets/AlwaysOnTop.ico new file mode 100644 index 000000000000..fdfc40a14bc7 Binary files /dev/null and b/src/modules/alwaysontop/Assets/AlwaysOnTop.ico differ diff --git a/src/modules/awake/Awake/Core/Manager.cs b/src/modules/awake/Awake/Core/Manager.cs index 6016a4551e51..09b3bbc3e831 100644 --- a/src/modules/awake/Awake/Core/Manager.cs +++ b/src/modules/awake/Awake/Core/Manager.cs @@ -28,6 +28,9 @@ namespace Awake.Core /// public class Manager { + private static readonly CompositeFormat AwakeMinutes = System.Text.CompositeFormat.Parse(Properties.Resources.AWAKE_MINUTES); + private static readonly CompositeFormat AwakeHours = System.Text.CompositeFormat.Parse(Properties.Resources.AWAKE_HOURS); + private static BlockingCollection _stateQueue; private static CancellationTokenSource _tokenSource; @@ -276,9 +279,9 @@ public static Dictionary GetDefaultTrayOptions() { Dictionary optionsList = new Dictionary { - { string.Format(CultureInfo.InvariantCulture, Resources.AWAKE_MINUTES, 30), 1800 }, + { string.Format(CultureInfo.InvariantCulture, AwakeMinutes, 30), 1800 }, { Resources.AWAKE_1_HOUR, 3600 }, - { string.Format(CultureInfo.InvariantCulture, Resources.AWAKE_HOURS, 2), 7200 }, + { string.Format(CultureInfo.InvariantCulture, AwakeHours, 2), 7200 }, }; return optionsList; } diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index 7475e9872530..2baab45c2998 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -47,6 +47,11 @@ internal sealed class Program #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private static ManualResetEvent _exitSignal = new ManualResetEvent(false); + internal static readonly string[] AliasesConfigOption = new[] { "--use-pt-config", "-c" }; + internal static readonly string[] AliasesDisplayOption = new[] { "--display-on", "-d" }; + internal static readonly string[] AliasesTimeOption = new[] { "--time-limit", "-t" }; + internal static readonly string[] AliasesPidOption = new[] { "--pid", "-p" }; + internal static readonly string[] AliasesExpireAtOption = new[] { "--expire-at", "-e" }; private static int Main(string[] args) { @@ -86,7 +91,7 @@ private static int Main(string[] args) Logger.LogInfo("Parsing parameters..."); Option configOption = new( - aliases: new[] { "--use-pt-config", "-c" }, + aliases: AliasesConfigOption, getDefaultValue: () => false, description: $"Specifies whether {Core.Constants.AppName} will be using the PowerToys configuration file for managing the state.") { @@ -95,7 +100,7 @@ private static int Main(string[] args) }; Option displayOption = new( - aliases: new[] { "--display-on", "-d" }, + aliases: AliasesDisplayOption, getDefaultValue: () => true, description: "Determines whether the display should be kept awake.") { @@ -104,7 +109,7 @@ private static int Main(string[] args) }; Option timeOption = new( - aliases: new[] { "--time-limit", "-t" }, + aliases: AliasesTimeOption, getDefaultValue: () => 0, description: "Determines the interval, in seconds, during which the computer is kept awake.") { @@ -113,7 +118,7 @@ private static int Main(string[] args) }; Option pidOption = new( - aliases: new[] { "--pid", "-p" }, + aliases: AliasesPidOption, getDefaultValue: () => 0, description: $"Bind the execution of {Core.Constants.AppName} to another process. When the process ends, the system will resume managing the current sleep and display state.") { @@ -122,7 +127,7 @@ private static int Main(string[] args) }; Option expireAtOption = new( - aliases: new[] { "--expire-at", "-e" }, + aliases: AliasesExpireAtOption, getDefaultValue: () => string.Empty, description: $"Determines the end date/time when {Core.Constants.AppName} will back off and let the system manage the current sleep and display state.") { diff --git a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj index 491ab5916014..6e4556e7f627 100644 --- a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj +++ b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj @@ -67,7 +67,7 @@ - + @@ -75,6 +75,6 @@ - + \ No newline at end of file diff --git a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters index eeb4aece126f..1109a2f73cf3 100644 --- a/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters +++ b/src/modules/awake/AwakeModuleInterface/AwakeModuleInterface.vcxproj.filters @@ -47,4 +47,7 @@ Resource Files + + + \ No newline at end of file diff --git a/src/modules/awake/AwakeModuleInterface/packages.config b/src/modules/awake/AwakeModuleInterface/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/awake/AwakeModuleInterface/packages.config +++ b/src/modules/awake/AwakeModuleInterface/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/cmdNotFound/CmdNotFound/CmdNotFound.csproj b/src/modules/cmdNotFound/CmdNotFound/CmdNotFound.csproj new file mode 100644 index 000000000000..65e6a0436e4a --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/CmdNotFound.csproj @@ -0,0 +1,69 @@ + + + + + net8.0-windows10.0.22621.0 + 10.0.19041.0 + 10.0.19041.0 + win-x64;win-arm64 + enable + Microsoft Corporation + PowerToys + enable + PowerToys CommandNotFound + PowerToys.CmdNotFound + false + true + ..\..\..\..\$(Platform)\$(Configuration) + false + false + true + true + + + + + win-x64 + + + win-arm64 + + + + DEBUG;TRACE + full + prompt + 4 + false + + + + TRACE;RELEASE + true + pdbonly + prompt + 4 + + + + + contentFiles + all + runtime; compile; build; native; analyzers; buildtransitive + + + contentFiles + all + + + PreserveNewest + PreserveNewest + + + + + + + + + diff --git a/src/modules/cmdNotFound/CmdNotFound/Init.cs b/src/modules/cmdNotFound/CmdNotFound/Init.cs new file mode 100644 index 000000000000..4d4ecb6d60c8 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/Init.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Management.Automation; +using System.Management.Automation.Subsystem; +using System.Management.Automation.Subsystem.Feedback; +using System.Management.Automation.Subsystem.Prediction; + +namespace WinGetCommandNotFound +{ + public sealed class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup + { + internal const string Id = "e5351aa4-dfde-4d4d-bf0f-1a2f5a37d8d6"; + + public void OnImport() + { + if (!Platform.IsWindows || !IsWinGetInstalled()) + { + return; + } + + SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, WinGetCommandNotFoundFeedbackPredictor.Singleton); + SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, WinGetCommandNotFoundFeedbackPredictor.Singleton); + } + + public void OnRemove(PSModuleInfo psModuleInfo) + { + if (!IsWinGetInstalled()) + { + return; + } + + SubsystemManager.UnregisterSubsystem(new Guid(Id)); + SubsystemManager.UnregisterSubsystem(new Guid(Id)); + } + + private bool IsWinGetInstalled() + { + // Ensure WinGet is installed + using (var pwsh = PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + var results = pwsh.AddCommand("Get-Command") + .AddParameter("Name", "winget") + .AddParameter("CommandType", "Application") + .Invoke(); + + if (results.Count is 0) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/modules/cmdNotFound/CmdNotFound/PooledPowerShellObjectPolicy.cs b/src/modules/cmdNotFound/CmdNotFound/PooledPowerShellObjectPolicy.cs new file mode 100644 index 000000000000..75d2ba8922c3 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/PooledPowerShellObjectPolicy.cs @@ -0,0 +1,34 @@ +// 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.Management.Automation; +using System.Management.Automation.Runspaces; +using Microsoft.Extensions.ObjectPool; + +namespace WinGetCommandNotFound +{ + public sealed class PooledPowerShellObjectPolicy : IPooledObjectPolicy + { + private static readonly string[] WingetClientModuleName = new[] { "Microsoft.WinGet.Client" }; + + public PowerShell Create() + { + var iss = InitialSessionState.CreateDefault2(); + iss.ImportPSModule(WingetClientModuleName); + return PowerShell.Create(iss); + } + + public bool Return(PowerShell obj) + { + if (obj != null) + { + obj.Commands.Clear(); + obj.Streams.ClearStreams(); + return true; + } + + return false; + } + } +} diff --git a/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundFeedbackProvidedEvent.cs b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundFeedbackProvidedEvent.cs new file mode 100644 index 000000000000..83563bbe3515 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundFeedbackProvidedEvent.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace WinGetCommandNotFound.Telemetry +{ + [EventData] + public class CmdNotFoundFeedbackProvidedEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundSuggestionProvidedEvent.cs b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundSuggestionProvidedEvent.cs new file mode 100644 index 000000000000..2f55dbbb3905 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/Telemetry/CmdNotFoundSuggestionProvidedEvent.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.Tracing; +using Microsoft.PowerToys.Telemetry; +using Microsoft.PowerToys.Telemetry.Events; + +namespace WinGetCommandNotFound.Telemetry +{ + [EventData] + public class CmdNotFoundSuggestionProvidedEvent : EventBase, IEvent + { + public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; + } +} diff --git a/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFound.psd1 b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFound.psd1 new file mode 100644 index 000000000000..c7a1118eac19 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFound.psd1 @@ -0,0 +1,11 @@ +@{ + ModuleVersion = '0.1.0' + GUID = '28c9afa2-92e5-413e-8e53-44b2d7a83ac6' + Author = 'Carlos Zamora' + CompanyName = "Microsoft Corporation" + Copyright = "Copyright (c) Microsoft Corporation." + Description = 'Enable suggestions on how to install missing commands via winget' + PowerShellVersion = '7.4' + NestedModules = @('PowerToys.CmdNotFound.dll') + RequiredModules = @(@{ModuleName = 'Microsoft.WinGet.Client'; ModuleVersion = "0.2.1"; }) +} diff --git a/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFoundFeedbackPredictor.cs b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFoundFeedbackPredictor.cs new file mode 100644 index 000000000000..405898ef35c5 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFound/WinGetCommandNotFoundFeedbackPredictor.cs @@ -0,0 +1,217 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Management.Automation; +using System.Management.Automation.Subsystem.Feedback; +using System.Management.Automation.Subsystem.Prediction; +using ManagedCommon; +using Microsoft.Extensions.ObjectPool; +using Microsoft.PowerToys.Telemetry; + +namespace WinGetCommandNotFound +{ + public sealed class WinGetCommandNotFoundFeedbackPredictor : IFeedbackProvider, ICommandPredictor + { + private readonly Guid _guid; + + private readonly ObjectPool _pool; + + private const int _maxSuggestions = 20; + + private List? _candidates; + + private bool _warmedUp; + + public static WinGetCommandNotFoundFeedbackPredictor Singleton { get; } = new WinGetCommandNotFoundFeedbackPredictor(Init.Id); + + private WinGetCommandNotFoundFeedbackPredictor(string guid) + { + Logger.InitializeLogger("\\CmdNotFound\\Logs"); + + _guid = new Guid(guid); + + var provider = new DefaultObjectPoolProvider(); + _pool = provider.Create(new PooledPowerShellObjectPolicy()); + _pool.Return(_pool.Get()); + Task.Run(() => WarmUp()); + } + + public Guid Id => _guid; + + public string Name => "Windows Package Manager - WinGet"; + + public string Description => "Finds missing commands that can be installed via WinGet."; + + public Dictionary? FunctionsToDefine => null; + + private void WarmUp() + { + var ps = _pool.Get(); + try + { + ps.AddCommand("Find-WinGetPackage") + .AddParameter("Count", 1) + .Invoke(); + } + finally + { + _pool.Return(ps); + _warmedUp = true; + } + } + + /// + /// Gets feedback based on the given commandline and error record. + /// + public FeedbackItem? GetFeedback(FeedbackContext context, CancellationToken token) + { + var target = (string)context.LastError!.TargetObject; + if (target is not null) + { + try + { + bool tooManySuggestions = false; + string packageMatchFilterField = "command"; + var pkgList = FindPackages(target, ref tooManySuggestions, ref packageMatchFilterField); + if (pkgList.Count == 0) + { + return null; + } + + // Build list of suggestions + _candidates = new List(); + foreach (var pkg in pkgList) + { + _candidates.Add(string.Format(CultureInfo.InvariantCulture, "winget install --id {0}", pkg.Members["Id"].Value.ToString())); + } + + // Build footer message + var footerMessage = tooManySuggestions ? + string.Format(CultureInfo.InvariantCulture, "Additional results can be found using \"winget search --{0} {1}\"", packageMatchFilterField, target) : + null; + + PowerToysTelemetry.Log.WriteEvent(new Telemetry.CmdNotFoundFeedbackProvidedEvent()); + + return new FeedbackItem( + "Try installing this package using winget:", + _candidates, + footerMessage, + FeedbackDisplayLayout.Portrait); + } + catch (Exception ex) + { + Logger.LogError("GetFeedback failed to execute", ex); + return new FeedbackItem($"Failed to execute PowerToys Command Not Found.{Environment.NewLine}This is a known issue if PowerShell 7 is installed from the Store or MSIX. If that isn't your case, please report an issue.", new List(), FeedbackDisplayLayout.Portrait); + } + } + + return null; + } + + private Collection FindPackages(string query, ref bool tooManySuggestions, ref string packageMatchFilterField) + { + if (!_warmedUp) + { + return new Collection(); + } + + var ps = _pool.Get(); + try + { + var common = new Hashtable() + { + ["Source"] = "winget", + }; + + // 1) Search by command + var pkgList = ps.AddCommand("Find-WinGetPackage") + .AddParameter("Command", query) + .AddParameter("MatchOption", "StartsWithCaseInsensitive") + .AddParameters(common) + .Invoke(); + if (pkgList.Count > 0) + { + tooManySuggestions = pkgList.Count > _maxSuggestions; + packageMatchFilterField = "command"; + return pkgList; + } + + // 2) No matches found, + // search by name + ps.Commands.Clear(); + pkgList = ps.AddCommand("Find-WinGetPackage") + .AddParameter("Name", query) + .AddParameter("MatchOption", "ContainsCaseInsensitive") + .AddParameters(common) + .Invoke(); + if (pkgList.Count > 0) + { + tooManySuggestions = pkgList.Count > _maxSuggestions; + packageMatchFilterField = "name"; + return pkgList; + } + + // 3) No matches found, + // search by moniker + ps.Commands.Clear(); + pkgList = ps.AddCommand("Find-WinGetPackage") + .AddParameter("Moniker", query) + .AddParameter("MatchOption", "ContainsCaseInsensitive") + .AddParameters(common) + .Invoke(); + tooManySuggestions = pkgList.Count > _maxSuggestions; + packageMatchFilterField = "moniker"; + return pkgList; + } + finally + { + _pool.Return(ps); + } + } + + public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback) + { + return feedback switch + { + PredictorFeedbackKind.CommandLineAccepted => true, + _ => false, + }; + } + + public SuggestionPackage GetSuggestion(PredictionClient client, PredictionContext context, CancellationToken cancellationToken) + { + if (_candidates is not null) + { + string input = context.InputAst.Extent.Text; + List? result = null; + + foreach (string c in _candidates) + { + if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase)) + { + result ??= new List(_candidates.Count); + result.Add(new PredictiveSuggestion(c)); + } + } + + if (result is not null) + { + PowerToysTelemetry.Log.WriteEvent(new Telemetry.CmdNotFoundSuggestionProvidedEvent()); + return new SuggestionPackage(result); + } + } + + return default; + } + + public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList history) + { + // Reset the candidate state. + _candidates = null; + } + } +} diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.rc b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.rc new file mode 100644 index 000000000000..e9ac022fc59a --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.rc @@ -0,0 +1,108 @@ +// Microsoft Visual C++ generated resource script. +// +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + 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 // US English (0x0409), Unicode (1200) charset + END +END + + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_CMD_NOT_FOUND_NAME "Command Not Found" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj new file mode 100644 index 000000000000..a7fd427c2af5 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj @@ -0,0 +1,107 @@ + + + + 17.0 + Win32Proj + {0014d652-901f-4456-8d65-06fc5f997fb0} + CmdNotFoundModuleInterface + PowerToys.CmdNotFoundModuleInterface + v143 + CmdNotFoundModuleInterface + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + ..\..\..\..\$(Platform)\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;CMDNOTFOUNDMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + + + Windows + true + false + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;CMDNOTFOUNDMODULEINTERFACE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + + + Windows + true + true + true + false + Shlwapi.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + ..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) + + + + + + Header Files + + + + + + Source Files + + + + Create + + + + + {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + + + + + + + + \ No newline at end of file diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj.filters b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj.filters new file mode 100644 index 000000000000..1834d3ae8849 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/CmdNotFoundModuleInterface.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/dllmain.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/dllmain.cpp new file mode 100644 index 000000000000..edb8e6455b91 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/dllmain.cpp @@ -0,0 +1,158 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "resource.h" +#include "trace.h" + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + Trace::RegisterProvider(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +const static wchar_t* MODULE_NAME = L"Command Not Found"; +const static wchar_t* MODULE_DESC = L"A module that detects an error thrown by a command in PowerShell and suggests a relevant WinGet package to install, if available."; + +inline const std::wstring ModuleKey = L"CmdNotFound"; + +class CmdNotFound : public PowertoyModuleIface +{ + std::wstring app_name; + std::wstring app_key; + +private: + bool m_enabled = false; + + void install_module() + { + auto module_path = get_module_folderpath(); + + std::string command = "pwsh.exe"; + command += " "; + command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(module_path) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\EnableModule.ps1" + "\"" + " -scriptPath \"" + winrt::to_string(module_path) + "\""; + + int ret = system(command.c_str()); + + if (ret != 0) + { + Logger::error("Running EnableModule.ps1 script failed."); + } + else + { + Logger::info("Module installed successfully."); + Trace::EnableCmdNotFoundGpo(true); + } + } + + void uninstall_module() + { + auto module_path = get_module_folderpath(); + + std::string command = "pwsh.exe"; + command += " "; + command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(module_path) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\""; + + int ret = system(command.c_str()); + + if (ret != 0) + { + Logger::error("Running EnableModule.ps1 script failed."); + } + else + { + Logger::info("Module uninstalled successfully."); + Trace::EnableCmdNotFoundGpo(false); + } + } + +public: + CmdNotFound() + { + app_name = GET_RESOURCE_STRING(IDS_CMD_NOT_FOUND_NAME); + app_key = ModuleKey; + LoggerHelpers::init_logger(app_key, L"ModuleInterface", LogSettings::cmdNotFoundLoggerName); + Logger::info("CmdNotFound object is constructing"); + + powertoys_gpo::gpo_rule_configured_t gpo_rule_configured_value = gpo_policy_enabled_configuration(); + if (gpo_rule_configured_value == powertoys_gpo::gpo_rule_configured_t::gpo_rule_configured_enabled) + { + install_module(); + m_enabled = true; + } + else if (gpo_rule_configured_value == powertoys_gpo::gpo_rule_configured_t::gpo_rule_configured_disabled) + { + uninstall_module(); + m_enabled = false; + } + } + + virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override + { + return powertoys_gpo::getConfiguredCmdNotFoundEnabledValue(); + } + + virtual void destroy() override + { + delete this; + } + + virtual const wchar_t* get_name() override + { + return MODULE_NAME; + } + + virtual const wchar_t* get_key() override + { + return app_key.c_str(); + } + + virtual bool get_config(wchar_t* /*buffer*/, int* /*buffer_size*/) override + { + return false; + } + + virtual void set_config(const wchar_t* config) override + { + } + + virtual void enable() + { + } + + virtual void disable() + { + } + + virtual bool is_enabled() override + { + return m_enabled; + } +}; + +extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() +{ + return new CmdNotFound(); +} diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.cpp new file mode 100644 index 000000000000..64b7eef6d6b9 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h new file mode 100644 index 000000000000..96a774ab2ad3 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h @@ -0,0 +1,16 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#include + +#endif //PCH_H diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/resource.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/resource.h new file mode 100644 index 000000000000..9b16533a8a48 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/resource.h @@ -0,0 +1,21 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Awake.rc +// +#define IDS_CMD_NOT_FOUND_NAME 101 + + +#define FILE_DESCRIPTION "PowerToys Command Not Found" +#define INTERNAL_NAME "PowerToys.CmdNotFoundModuleInterface" +#define ORIGINAL_FILENAME "PowerToys.CmdNotFoundModuleInterface.dll" + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp new file mode 100644 index 000000000000..255c46ea9944 --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp @@ -0,0 +1,30 @@ +#include "pch.h" +#include "trace.h" + +TRACELOGGING_DEFINE_PROVIDER( + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); + +void Trace::RegisterProvider() +{ + TraceLoggingRegister(g_hProvider); +} + +void Trace::UnregisterProvider() +{ + TraceLoggingUnregister(g_hProvider); +} + +// Log if the user has CmdNotFound enabled or disabled +void Trace::EnableCmdNotFoundGpo(const bool enabled) noexcept +{ + TraceLoggingWrite( + g_hProvider, + "CmdNotFound_EnableCmdNotFound", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), + TraceLoggingBoolean(enabled, "Enabled")); +} diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h new file mode 100644 index 000000000000..4294c510a64d --- /dev/null +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h @@ -0,0 +1,11 @@ +#pragma once + +class Trace +{ +public: + static void RegisterProvider(); + static void UnregisterProvider(); + + // Log if the user has CmdNotFound enabled or disabled + static void EnableCmdNotFoundGpo(const bool enabled) noexcept; +}; diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs index 5424f44acd08..6d020a752b6a 100644 --- a/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/ResizeBehavior.cs @@ -20,7 +20,7 @@ public class ResizeBehavior : Behavior private static readonly TimeSpan _animationTimeSmaller = _animationTime; private static readonly IEasingFunction _easeFunctionSmaller = new QuadraticEase() { EasingMode = EasingMode.EaseIn }; - private static void CustomAnimation(DependencyProperty prop, IAnimatable sender, double fromValue, double toValue) + private static void CustomAnimation(DependencyProperty prop, FrameworkElement sender, double fromValue, double toValue) { // if the animation is to/from a value of 0, it will cancel the current animation DoubleAnimation move = null; diff --git a/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs b/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs index 7c5fe0e26e45..b7fbdbc67a8b 100644 --- a/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/Controls/ColorPickerControl.xaml.cs @@ -329,7 +329,7 @@ private static string ColorToHex(Color color, string oldValue = "") newHexString = newHexString.ToLowerInvariant(); // Return only with hashtag if user typed it before - bool addHashtag = oldValue.StartsWith("#", StringComparison.InvariantCulture); + bool addHashtag = oldValue.StartsWith('#'); return addHashtag ? "#" + newHexString : newHexString; } @@ -348,7 +348,7 @@ private static string FormatHexColorString(string hexCodeText) else { // Hex with or without hashtag and six characters - return hexCodeText.StartsWith("#", StringComparison.InvariantCulture) ? hexCodeText : "#" + hexCodeText; + return hexCodeText.StartsWith('#') ? hexCodeText : "#" + hexCodeText; } } diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs index 9df21d04119b..4b31c423849e 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/SerializationHelper.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.Linq; using System.Text.Json; +using System.Text.Json.Serialization; using System.Windows.Media; using ColorPicker.Models; @@ -21,6 +22,9 @@ internal enum GroupExportedColorsBy internal static class SerializationHelper { + public static readonly JsonSerializerOptions DefaultOptions = new JsonSerializerOptions { WriteIndented = false }; + public static readonly JsonSerializerOptions IndentedOptions = new JsonSerializerOptions { WriteIndented = true }; + public static Dictionary> ConvertToDesiredColorFormats( IList colorsToExport, IEnumerable colorRepresentations, @@ -116,10 +120,7 @@ public static string ToTxt(this Dictionary> s public static string ToJson(this Dictionary> source, bool indented = true) { - var options = new JsonSerializerOptions - { - WriteIndented = indented, - }; + var options = indented ? IndentedOptions : DefaultOptions; return JsonSerializer.Serialize(source, options); } diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs index a67c8b73595b..0b38ebbc42f4 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs @@ -91,7 +91,7 @@ private void SetZoomImage(System.Windows.Point point) ShowZoomWindow(point); } - private static BitmapSource BitmapToImageSource(Bitmap bitmap) + private static BitmapImage BitmapToImageSource(Bitmap bitmap) { using (MemoryStream memory = new MemoryStream()) { diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index 4ab33f09a148..477465e84f65 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -22,7 +22,7 @@ namespace ColorPicker.Settings [Export(typeof(IUserSettings))] public class UserSettings : IUserSettings { - private readonly ISettingsUtils _settingsUtils; + private readonly SettingsUtils _settingsUtils; private const string ColorPickerModuleName = "ColorPicker"; private const string ColorPickerHistoryFilename = "colorHistory.json"; private const string DefaultActivationShortcut = "Ctrl + Break"; @@ -36,6 +36,11 @@ public class UserSettings : IUserSettings private bool _loadingColorsHistory; + private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + }; + [ImportingConstructor] public UserSettings(Helpers.IThrottledActionInvoker throttledActionInvoker) { @@ -58,7 +63,7 @@ private void ColorHistory_CollectionChanged(object sender, System.Collections.Sp { if (!_loadingColorsHistory) { - _settingsUtils.SaveSettings(JsonSerializer.Serialize(ColorHistory, new JsonSerializerOptions { WriteIndented = true }), ColorPickerModuleName, ColorPickerHistoryFilename); + _settingsUtils.SaveSettings(JsonSerializer.Serialize(ColorHistory, _serializerOptions), ColorPickerModuleName, ColorPickerHistoryFilename); } } diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj index 009be3601428..8219d398c6e5 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/UnitTest-ColorPickerUI.csproj @@ -22,6 +22,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj index 21cd0a9cb4bc..643aad98cf6b 100644 --- a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj +++ b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj @@ -161,7 +161,7 @@ - + @@ -169,6 +169,6 @@ - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters index 3f34fdfd49f5..a099341ce226 100644 --- a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters +++ b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj.filters @@ -39,4 +39,7 @@ + + + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZones/packages.config b/src/modules/fancyzones/FancyZones/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/fancyzones/FancyZones/packages.config +++ b/src/modules/fancyzones/FancyZones/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index faf124b3efdd..d2e5b1ca43db 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -61,7 +61,6 @@ - @@ -156,7 +155,7 @@ - + @@ -164,6 +163,6 @@ - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index 220b0cc22f62..c72ebd2892cd 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -153,9 +153,6 @@ Header Files - - Header Files - Header Files @@ -299,4 +296,7 @@ Generated Files + + + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/NotificationUtil.h b/src/modules/fancyzones/FancyZonesLib/NotificationUtil.h deleted file mode 100644 index 7686f409be7c..000000000000 --- a/src/modules/fancyzones/FancyZonesLib/NotificationUtil.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace FancyZonesNotifications -{ - // Non-Localizable strings - namespace NonLocalizable - { - const wchar_t FancyZonesRunAsAdminInfoPage[] = L"https://aka.ms/powertoysDetectedElevatedHelp"; - const wchar_t ToastNotificationButtonUrl[] = L"powertoys://cant_drag_elevated_disable/"; - } - - inline void WarnIfElevationIsRequired() - { - using namespace notifications; - using namespace NonLocalizable; - - static bool warning_shown = false; - if (!warning_shown && !is_toast_disabled(CantDragElevatedDontShowAgainRegistryPath, CantDragElevatedDisableIntervalInDays)) - { - std::vector actions = { - link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), FancyZonesRunAsAdminInfoPage }, - link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN), ToastNotificationButtonUrl } - }; - show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), - GET_RESOURCE_STRING(IDS_FANCYZONES), - {}, - std::move(actions)); - warning_shown = true; - } - } -} diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp index edb32685f7dc..0a7251c78cf6 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include #include +#include WindowMouseSnap::WindowMouseSnap(HWND window, const std::unordered_map>& activeWorkAreas) : m_window(window), @@ -34,10 +34,10 @@ std::unique_ptr WindowMouseSnap::Create(HWND window, const std: return nullptr; } - if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window)) + if (!is_process_elevated() && IsProcessOfWindowElevated(window)) { // Notifies user if unable to drag elevated window - FancyZonesNotifications::WarnIfElevationIsRequired(); + notifications::WarnIfElevationIsRequired(GET_RESOURCE_STRING(IDS_FANCYZONES), GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN)); return nullptr; } diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp index 77bad34683a7..3de75ce7646a 100644 --- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp @@ -158,33 +158,6 @@ bool FancyZonesWindowUtils::IsRoot(HWND window) noexcept return GetAncestor(window, GA_ROOT) == window; } -bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window) -{ - DWORD pid = 0; - GetWindowThreadProcessId(window, &pid); - if (!pid) - { - return false; - } - - wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, - FALSE, - pid) }; - - wil::unique_handle token; - - if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token)) - { - TOKEN_ELEVATION elevation; - DWORD size; - if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size)) - { - return elevation.TokenIsElevated != 0; - } - } - return false; -} - bool FancyZonesWindowUtils::IsExcluded(HWND window) { std::wstring processPath = get_process_path_waiting_uwp(window); diff --git a/src/modules/fancyzones/FancyZonesLib/packages.config b/src/modules/fancyzones/FancyZonesLib/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/fancyzones/FancyZonesLib/packages.config +++ b/src/modules/fancyzones/FancyZonesLib/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/pch.h b/src/modules/fancyzones/FancyZonesLib/pch.h index db90d123e2ad..70a402b0fa03 100644 --- a/src/modules/fancyzones/FancyZonesLib/pch.h +++ b/src/modules/fancyzones/FancyZonesLib/pch.h @@ -13,8 +13,9 @@ #include #include #include -#include -#include +#include +#include +#include #include #include #include diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj index a46103c5b7aa..12a82f47e270 100644 --- a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj +++ b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj @@ -70,7 +70,7 @@ - + @@ -79,6 +79,6 @@ - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters index e27c0a55384f..b49ea1cd1abc 100644 --- a/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesModuleInterface/FancyZonesModuleInterface.vcxproj.filters @@ -41,4 +41,7 @@ Resource Files + + + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/packages.config b/src/modules/fancyzones/FancyZonesModuleInterface/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/fancyzones/FancyZonesModuleInterface/packages.config +++ b/src/modules/fancyzones/FancyZonesModuleInterface/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj index c0da418a8f58..160abae1fa1a 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj @@ -86,7 +86,7 @@ - + @@ -94,6 +94,6 @@ - + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters index 0732c34ad4af..fc286dd63eeb 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters @@ -91,4 +91,7 @@ Resource Files + + + \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config b/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj b/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj index 8d1972207fcc..750c3a8c6e6b 100644 --- a/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj +++ b/src/modules/fancyzones/UnitTests-FancyZonesEditor/UnitTests-FancyZonesEditor.csproj @@ -20,6 +20,10 @@ + + + runtime + diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs index ac9c8ac7c38b..1429bda4f822 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/CanvasZone.xaml.cs @@ -237,7 +237,7 @@ public override void Move(int delta) private SnappyHelperBase snappyX; private SnappyHelperBase snappyY; - private SnappyHelperBase NewMagneticSnapper(bool isX, ResizeMode mode) + private SnappyHelperMagnetic NewMagneticSnapper(bool isX, ResizeMode mode) { Rect workingArea = App.Overlay.WorkArea; int screenAxisOrigin = (int)(isX ? workingArea.Left : workingArea.Top); @@ -245,7 +245,7 @@ private SnappyHelperBase NewMagneticSnapper(bool isX, ResizeMode mode) return new SnappyHelperMagnetic(Model.Zones, ZoneIndex, isX, mode, screenAxisOrigin, screenAxisSize); } - private SnappyHelperBase NewNonMagneticSnapper(bool isX, ResizeMode mode) + private SnappyHelperNonMagnetic NewNonMagneticSnapper(bool isX, ResizeMode mode) { Rect workingArea = App.Overlay.WorkArea; int screenAxisOrigin = (int)(isX ? workingArea.Left : workingArea.Top); diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs index 01e17f70b8ce..5971f2c72277 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Controls/CustomSliderAutomationPeer.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Text; using System.Windows.Automation.Peers; using System.Windows.Controls; @@ -10,6 +11,8 @@ namespace FancyZonesEditor.Controls { internal sealed class CustomSliderAutomationPeer : SliderAutomationPeer { + private static readonly CompositeFormat CustomSliderAnnounce = System.Text.CompositeFormat.Parse(Properties.Resources.Custom_slider_announce); + private string name = string.Empty; public CustomSliderAutomationPeer(Slider owner) @@ -29,7 +32,7 @@ protected override string GetNameCore() string announce = string.Format( CultureInfo.CurrentCulture, - Properties.Resources.Custom_slider_announce, + CustomSliderAnnounce, name, element.Minimum, element.Maximum, diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs index 944222b73da7..383b10181a56 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; +using System.Text; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; @@ -36,6 +37,10 @@ public partial class MainWindow : Window private bool haveTriedToGetFocusAlready; + private static readonly CompositeFormat EditTemplate = System.Text.CompositeFormat.Parse(Properties.Resources.Edit_Template); + private static readonly CompositeFormat PixelValue = System.Text.CompositeFormat.Parse(Properties.Resources.Pixel_Value); + private static readonly CompositeFormat TemplateZoneCountValue = System.Text.CompositeFormat.Parse(Properties.Resources.Template_Zone_Count_Value); + public int WrapPanelItemSize { get; set; } = DefaultWrapPanelItemSize; public MainWindow(bool spanZonesAcrossMonitors, Rect workArea) @@ -335,7 +340,7 @@ private async void EditLayout_Click(object sender, RoutedEventArgs e) App.Overlay.StartEditing(_settings.SelectedModel); Keyboard.ClearFocus(); - EditLayoutDialogTitle.Text = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Edit_Template, ((LayoutModel)dataContext).Name); + EditLayoutDialogTitle.Text = string.Format(CultureInfo.CurrentCulture, EditTemplate, ((LayoutModel)dataContext).Name); await EditLayoutDialog.ShowAsync(); } @@ -567,7 +572,7 @@ private void SensitivityInput_ValueChanged(object sender, RoutedPropertyChangedE FrameworkElementAutomationPeer.FromElement(SensitivityInput) as SliderAutomationPeer; string activityId = "sliderValueChanged"; - string value = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Pixel_Value, SensitivityInput.Value); + string value = string.Format(CultureInfo.CurrentCulture, PixelValue, SensitivityInput.Value); if (peer != null && value != null) { @@ -588,7 +593,7 @@ private void TemplateZoneCount_ValueChanged(object sender, RoutedPropertyChanged FrameworkElementAutomationPeer.FromElement(TemplateZoneCount) as SliderAutomationPeer; string activityId = "templateZoneCountValueChanged"; - string value = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Template_Zone_Count_Value, TemplateZoneCount.Value); + string value = string.Format(CultureInfo.CurrentCulture, TemplateZoneCountValue, TemplateZoneCount.Value); if (peer != null && value != null) { @@ -609,7 +614,7 @@ private void Spacing_ValueChanged(object sender, RoutedPropertyChangedEventArgs< FrameworkElementAutomationPeer.FromElement(Spacing) as SliderAutomationPeer; string activityId = "spacingValueChanged"; - string value = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Pixel_Value, Spacing.Value); + string value = string.Format(CultureInfo.CurrentCulture, PixelValue, Spacing.Value); if (peer != null && value != null) { diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs index 5e64d2fdb1ae..58ee764d59dc 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Models/MainWindowSettingsModel.cs @@ -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.Generic; using System.Collections.ObjectModel; using System.ComponentModel; @@ -231,7 +232,7 @@ public LayoutModel UpdateSelectedLayoutModel() { foreach (LayoutModel model in CustomModels) { - if (model.Uuid == currentApplied.ZonesetUuid.ToUpperInvariant()) + if (string.Equals(model.Uuid, currentApplied.ZonesetUuid, StringComparison.OrdinalIgnoreCase)) { // found match foundModel = model; diff --git a/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj b/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj index d32b74bb5077..c8d82684896a 100644 --- a/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj +++ b/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj @@ -121,7 +121,7 @@ MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv - + @@ -129,6 +129,6 @@ MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv - + \ No newline at end of file diff --git a/src/modules/imageresizer/ImageResizerContextMenu/packages.config b/src/modules/imageresizer/ImageResizerContextMenu/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/imageresizer/ImageResizerContextMenu/packages.config +++ b/src/modules/imageresizer/ImageResizerContextMenu/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/imageresizer/tests/ImageResizerUITest.csproj b/src/modules/imageresizer/tests/ImageResizerUITest.csproj index cbcfa6aed745..b0839981e664 100644 --- a/src/modules/imageresizer/tests/ImageResizerUITest.csproj +++ b/src/modules/imageresizer/tests/ImageResizerUITest.csproj @@ -56,5 +56,9 @@ + + + runtime + diff --git a/src/modules/imageresizer/tests/Properties/SettingsTests.cs b/src/modules/imageresizer/tests/Properties/SettingsTests.cs index 9c337a42b38f..dcc88aeab64b 100644 --- a/src/modules/imageresizer/tests/Properties/SettingsTests.cs +++ b/src/modules/imageresizer/tests/Properties/SettingsTests.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Globalization; using System.Linq; +using System.Text; using System.Text.Json; using ImageResizer.Models; using ImageResizer.Test; @@ -16,6 +17,13 @@ namespace ImageResizer.Properties [TestClass] public class SettingsTests { + private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + }; + + private static readonly CompositeFormat ValueMustBeBetween = System.Text.CompositeFormat.Parse(Properties.Resources.ValueMustBeBetween); + private static App _imageResizerApp; public SettingsTests() @@ -187,7 +195,7 @@ public void IDataErrorInfoItemJpegQualityLevelReturnsErrorWhenOutOfRange(int val // Using InvariantCulture since this is used internally Assert.AreEqual( - string.Format(CultureInfo.InvariantCulture, Resources.ValueMustBeBetween, 1, 100), + string.Format(CultureInfo.InvariantCulture, ValueMustBeBetween, 1, 100), result); } @@ -355,9 +363,9 @@ public void SystemTextJsonDeserializesCorrectly() // Execute readFile/writefile twice and see if serialized string is still correct var resultWrapper = JsonSerializer.Deserialize(defaultInput); - var serializedInput = JsonSerializer.Serialize(resultWrapper, new JsonSerializerOptions() { WriteIndented = true }); + var serializedInput = JsonSerializer.Serialize(resultWrapper, _serializerOptions); var resultWrapper2 = JsonSerializer.Deserialize(serializedInput); - var serializedInput2 = JsonSerializer.Serialize(resultWrapper2, new JsonSerializerOptions() { WriteIndented = true }); + var serializedInput2 = JsonSerializer.Serialize(resultWrapper2, _serializerOptions); Assert.AreEqual(serializedInput, serializedInput2); Assert.AreEqual("Image Resizer", resultWrapper2.Name); diff --git a/src/modules/imageresizer/ui/Models/ResizeSize.cs b/src/modules/imageresizer/ui/Models/ResizeSize.cs index 92d64b6f4ff5..761f59adf2f8 100644 --- a/src/modules/imageresizer/ui/Models/ResizeSize.cs +++ b/src/modules/imageresizer/ui/Models/ResizeSize.cs @@ -12,7 +12,7 @@ namespace ImageResizer.Models { public class ResizeSize : Observable { - private static readonly IDictionary _tokens = new Dictionary + private static readonly Dictionary _tokens = new Dictionary { ["$small$"] = Resources.Small, ["$medium$"] = Resources.Medium, diff --git a/src/modules/imageresizer/ui/Properties/Settings.cs b/src/modules/imageresizer/ui/Properties/Settings.cs index 0a0186c527f7..f2f555ccf015 100644 --- a/src/modules/imageresizer/ui/Properties/Settings.cs +++ b/src/modules/imageresizer/ui/Properties/Settings.cs @@ -10,6 +10,7 @@ using System.ComponentModel; using System.Globalization; using System.IO.Abstractions; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; @@ -27,6 +28,8 @@ public sealed partial class Settings : IDataErrorInfo, INotifyPropertyChanged WriteIndented = true, }; + private static readonly CompositeFormat ValueMustBeBetween = System.Text.CompositeFormat.Parse(Properties.Resources.ValueMustBeBetween); + // Used to synchronize access to the settings.json file private static Mutex _jsonMutex = new Mutex(); private static string _settingsPath = _fileSystem.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "Image Resizer", "settings.json"); @@ -122,7 +125,7 @@ string IDataErrorInfo.this[string columnName] if (JpegQualityLevel < 1 || JpegQualityLevel > 100) { // Using CurrentCulture since this is user facing - return string.Format(CultureInfo.CurrentCulture, Resources.ValueMustBeBetween, 1, 100); + return string.Format(CultureInfo.CurrentCulture, ValueMustBeBetween, 1, 100); } return string.Empty; diff --git a/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp b/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp index 25965ffd9b71..31417e9c235f 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp @@ -61,9 +61,14 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, } auto kbm = KeyboardManager(); - kbm.StartLowlevelKeyboardHook(); + if (kbm.HasRegisteredRemappings()) + kbm.StartLowlevelKeyboardHook(); - run_message_loop(); + auto StartHookFunc = [&kbm]() { + kbm.StartLowlevelKeyboardHook(); + }; + + run_message_loop({}, {}, { { KeyboardManager::StartHookMessageID, StartHookFunc } }); kbm.StopLowlevelKeyboardHook(); Trace::UnregisterProvider(); diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp index acfcacfc3b92..bb5b8a13e735 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.cpp @@ -21,8 +21,15 @@ HHOOK KeyboardManager::hookHandleCopy; HHOOK KeyboardManager::hookHandle; KeyboardManager* KeyboardManager::keyboardManagerObjectPtr; +namespace +{ + DWORD mainThreadId = {}; +} + KeyboardManager::KeyboardManager() { + mainThreadId = GetCurrentThreadId(); + // Load the initial settings. LoadSettings(); @@ -38,9 +45,11 @@ KeyboardManager::KeyboardManager() } loadingSettings = true; + bool loadedSuccessfully = false; try { LoadSettings(); + loadedSuccessfully = true; } catch (...) { @@ -48,6 +57,18 @@ KeyboardManager::KeyboardManager() } loadingSettings = false; + + if (!loadedSuccessfully) + return; + + const bool newHasRemappings = HasRegisteredRemappingsUnchecked(); + // We didn't have any bindings before and we have now + if (newHasRemappings && !hookHandle) + PostThreadMessageW(mainThreadId, StartHookMessageID, 0, 0); + + // All bindings were removed + if (!newHasRemappings && hookHandle) + StopLowlevelKeyboardHook(); }; editorIsRunningEvent = CreateEvent(nullptr, true, false, KeyboardManagerConstants::EditorWindowEventName.c_str()); @@ -121,6 +142,32 @@ void KeyboardManager::StopLowlevelKeyboardHook() } } +bool KeyboardManager::HasRegisteredRemappings() const +{ + constexpr int MaxAttempts = 5; + + if (loadingSettings) + { + for (int currentAttempt = 0; currentAttempt < MaxAttempts; ++currentAttempt) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + if (!loadingSettings) + break; + } + } + + // Assume that we have registered remappings to be on the safe side if we couldn't check + if (loadingSettings) + return true; + + return HasRegisteredRemappingsUnchecked(); +} + +bool KeyboardManager::HasRegisteredRemappingsUnchecked() const +{ + return !(state.appSpecificShortcutReMap.empty() && state.appSpecificShortcutReMapSortedKeys.empty() && state.osLevelShortcutReMap.empty() && state.osLevelShortcutReMapSortedKeys.empty() && state.singleKeyReMap.empty() && state.singleKeyToTextReMap.empty()); +} + intptr_t KeyboardManager::HandleKeyboardHookEvent(LowlevelKeyboardEvent* data) noexcept { if (loadingSettings) diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h index 457a9135b778..846d19a3dbb8 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/KeyboardManager.h @@ -7,6 +7,8 @@ class KeyboardManager { public: + static const inline DWORD StartHookMessageID = WM_APP + 1; + // Constructor KeyboardManager(); @@ -21,7 +23,12 @@ class KeyboardManager void StartLowlevelKeyboardHook(); void StopLowlevelKeyboardHook(); + bool HasRegisteredRemappings() const; + private: + // Returns whether there are any remappings available without waiting for settings to load + bool HasRegisteredRemappingsUnchecked() const; + // Contains the non localized module name std::wstring moduleName = KeyboardManagerConstants::ModuleName; diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj index 42a63f5d83d3..9984a9431ec9 100644 --- a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj +++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj @@ -77,7 +77,7 @@ - + @@ -85,6 +85,6 @@ - + \ No newline at end of file diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters index c6ceeb2f27e9..81c3649d1e57 100644 --- a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters +++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters @@ -41,4 +41,7 @@ Generated Files + + + \ No newline at end of file diff --git a/src/modules/launcher/Microsoft.Launcher/packages.config b/src/modules/launcher/Microsoft.Launcher/packages.config index e11b462529ec..2c654a3e7df1 100644 --- a/src/modules/launcher/Microsoft.Launcher/packages.config +++ b/src/modules/launcher/Microsoft.Launcher/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs index 4f4cfd0e9411..99a1a265b992 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/InputInterpreterTests.cs @@ -11,6 +11,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter.UnitTest [TestClass] public class InputInterpreterTests { +#pragma warning disable CA1861 // Avoid constant arrays as arguments [DataTestMethod] [DataRow(new string[] { "1,5'" }, new string[] { "1,5", "'" })] [DataRow(new string[] { "1.5'" }, new string[] { "1.5", "'" })] @@ -18,6 +19,12 @@ public class InputInterpreterTests [DataRow(new string[] { "1'5\"" }, new string[] { "1", "'", "5", "\"" })] [DataRow(new string[] { "5\"" }, new string[] { "5", "\"" })] [DataRow(new string[] { "1'5" }, new string[] { "1", "'", "5" })] + [DataRow(new string[] { "-1,5'" }, new string[] { "-1,5", "'" })] + [DataRow(new string[] { "-1.5'" }, new string[] { "-1.5", "'" })] + [DataRow(new string[] { "-1'" }, new string[] { "-1", "'" })] + [DataRow(new string[] { "-1'5\"" }, new string[] { "-1", "'", "5", "\"" })] + [DataRow(new string[] { "-5\"" }, new string[] { "-5", "\"" })] + [DataRow(new string[] { "-1'5" }, new string[] { "-1", "'", "5" })] public void RegexSplitsInput(string[] input, string[] expectedResult) { string[] shortsplit = InputInterpreter.RegexSplitter(input); @@ -26,6 +33,7 @@ public void RegexSplitsInput(string[] input, string[] expectedResult) [DataTestMethod] [DataRow(new string[] { "1cm", "to", "mm" }, new string[] { "1", "cm", "to", "mm" })] + [DataRow(new string[] { "-1cm", "to", "mm" }, new string[] { "-1", "cm", "to", "mm" })] public void InsertsSpaces(string[] input, string[] expectedResult) { InputInterpreter.InputSpaceInserter(ref input); @@ -37,6 +45,10 @@ public void InsertsSpaces(string[] input, string[] expectedResult) [DataRow(new string[] { "1\"", "in", "cm" }, new string[] { "1", "inch", "in", "cm" })] [DataRow(new string[] { "1'6", "in", "cm" }, new string[] { "1.5", "foot", "in", "cm" })] [DataRow(new string[] { "1'6\"", "in", "cm" }, new string[] { "1.5", "foot", "in", "cm" })] + [DataRow(new string[] { "-1'", "in", "cm" }, new string[] { "-1", "foot", "in", "cm" })] + [DataRow(new string[] { "-1\"", "in", "cm" }, new string[] { "-1", "inch", "in", "cm" })] + [DataRow(new string[] { "-1'6", "in", "cm" }, new string[] { "-1.5", "foot", "in", "cm" })] + [DataRow(new string[] { "-1'6\"", "in", "cm" }, new string[] { "-1.5", "foot", "in", "cm" })] public void HandlesShorthandFeetInchNotation(string[] input, string[] expectedResult) { InputInterpreter.ShorthandFeetInchHandler(ref input, CultureInfo.InvariantCulture); @@ -58,6 +70,7 @@ public void HandlesMetreVsMeterNotation(string[] input, string[] expectedResult) [DataRow(new string[] { "5", "f", "in", "celsius" }, new string[] { "5", "°f", "in", "DegreeCelsius" })] [DataRow(new string[] { "5", "c", "in", "f" }, new string[] { "5", "°c", "in", "°f" })] [DataRow(new string[] { "5", "f", "in", "c" }, new string[] { "5", "°f", "in", "°c" })] +#pragma warning restore CA1861 // Avoid constant arrays as arguments public void PrefixesDegrees(string[] input, string[] expectedResult) { InputInterpreter.DegreePrefixer(ref input); @@ -67,6 +80,8 @@ public void PrefixesDegrees(string[] input, string[] expectedResult) [DataTestMethod] [DataRow("a f in c")] [DataRow("12 f in")] + [DataRow("1-2 f in c")] + [DataRow("12- f in c")] public void ParseInvalidQueries(string queryString) { Query query = new Query(queryString); @@ -77,6 +92,8 @@ public void ParseInvalidQueries(string queryString) [DataTestMethod] [DataRow("12 f in c", 12)] [DataRow("10m to cm", 10)] + [DataRow("-12 f in c", -12)] + [DataRow("-10m to cm", -10)] public void ParseValidQueries(string queryString, double result) { Query query = new Query(queryString); diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj index 9d81ee857e69..99c7afcafc98 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj @@ -45,6 +45,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs index 6e9d62190671..5cdaf9683e30 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/InputInterpreter.cs @@ -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.Generic; using System.Globalization; using System.Linq; @@ -13,11 +14,11 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter { public static class InputInterpreter { - private static string pattern = @"(?<=\d)(?![,.])(?=\D)|(?<=\D)(? @@ -30,7 +31,7 @@ public static void InputSpaceInserter(ref string[] split) return; } - string[] parseInputWithoutSpace = Regex.Split(split[0], pattern); + string[] parseInputWithoutSpace = Regex.Split(split[0], Pattern); if (parseInputWithoutSpace.Length > 1) { @@ -79,6 +80,12 @@ public static void ShorthandFeetInchHandler(ref string[] split, CultureInfo cult // ex: 1'2 and 1'2" if (shortsplit[1] == "\'") { + bool isNegative = shortsplit[0].StartsWith('-'); + if (isNegative) + { + shortsplit[0] = shortsplit[0].Remove(0, 1); + } + bool isFeet = double.TryParse(shortsplit[0], NumberStyles.AllowDecimalPoint, culture, out double feet); bool isInches = double.TryParse(shortsplit[2], NumberStyles.AllowDecimalPoint, culture, out double inches); @@ -88,9 +95,13 @@ public static void ShorthandFeetInchHandler(ref string[] split, CultureInfo cult break; } - string convertedTotalInFeet = Length.FromFeetInches(feet, inches).Feet.ToString(culture); + double convertedTotalInFeet = Length.FromFeetInches(feet, inches).Feet; + if (isNegative) + { + convertedTotalInFeet *= -1; + } - string[] newInput = new string[] { convertedTotalInFeet, "foot", split[1], split[2] }; + string[] newInput = new string[] { convertedTotalInFeet.ToString(culture), "foot", split[1], split[2] }; split = newInput; } @@ -157,12 +168,12 @@ public static void DegreePrefixer(ref string[] split) /// public static void FeetToFt(ref string[] split) { - if (split[1].ToLowerInvariant() == "feet") + if (string.Equals(split[1], "feet", StringComparison.OrdinalIgnoreCase)) { split[1] = "ft"; } - if (split[3].ToLowerInvariant() == "feet") + if (string.Equals(split[3], "feet", StringComparison.OrdinalIgnoreCase)) { split[3] = "ft"; } @@ -183,7 +194,8 @@ public static void MetreToMeter(ref string[] split) public static void GallonHandler(ref string[] split, CultureInfo culture) { HashSet britishCultureNames = new HashSet() { "en-AI", "en-VG", "en-GB", "en-KY", "en-MS", "en-AG", "en-DM", "en-GD", "en-KN", "en-LC", "en-VC", "en-IE", "en-GY", "en-AE" }; - if (split[1].ToLowerInvariant() == "gal" || split[1].ToLowerInvariant() == "gallon") + if (string.Equals(split[1], "gal", StringComparison.OrdinalIgnoreCase) || + string.Equals(split[1], "gallon", StringComparison.OrdinalIgnoreCase)) { if (britishCultureNames.Contains(culture.Name)) { @@ -195,7 +207,8 @@ public static void GallonHandler(ref string[] split, CultureInfo culture) } } - if (split[3].ToLowerInvariant() == "gal" || split[3].ToLowerInvariant() == "gallon") + if (string.Equals(split[3], "gal", StringComparison.OrdinalIgnoreCase) || + string.Equals(split[3], "gallon", StringComparison.OrdinalIgnoreCase)) { if (britishCultureNames.Contains(culture.Name)) { diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs index ce2f4cfe5ed2..44013e782d4a 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using System.Windows; using System.Windows.Input; @@ -27,6 +28,8 @@ public class Main : IPlugin, IPluginI18n, IContextMenu, IDisposable private static string _icon_path; private bool _disposed; + private static readonly CompositeFormat CopyToClipboard = System.Text.CompositeFormat.Parse(Properties.Resources.copy_to_clipboard); + public void Init(PluginInitContext context) { ArgumentNullException.ThrowIfNull(context); @@ -61,7 +64,7 @@ private Result GetResult(UnitConversionResult result) Title = result.ToString(null), IcoPath = _icon_path, Score = 300, - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.copy_to_clipboard, result.QuantityInfo.Name), + SubTitle = string.Format(CultureInfo.CurrentCulture, CopyToClipboard, result.QuantityInfo.Name), Action = c => { var ret = false; diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs index 89b6938695c2..a24e6fd90baf 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs @@ -36,7 +36,8 @@ public static class UnitHandler private static Enum GetUnitEnum(string unit, QuantityInfo unitInfo) { UnitInfo first = Array.Find(unitInfo.UnitInfos, info => - unit.ToLowerInvariant() == info.Name.ToLowerInvariant() || unit.ToLowerInvariant() == info.PluralName.ToLowerInvariant()); + string.Equals(unit, info.Name, StringComparison.OrdinalIgnoreCase) || + string.Equals(unit, info.PluralName, StringComparison.OrdinalIgnoreCase)); if (first != null) { diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj index 52a1b4b1e0f4..1f686fab7eea 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj @@ -41,6 +41,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs index 7a41b008d4e3..94c4cd8484e8 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Main.cs @@ -138,7 +138,7 @@ public List Query(Query query) }); } - results = results.Where(a => a.Title.ToLowerInvariant().Contains(query.Search.ToLowerInvariant())).ToList(); + results = results.Where(a => a.Title.Contains(query.Search, StringComparison.InvariantCultureIgnoreCase)).ToList(); results.ForEach(x => { diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs index 56dfd4ae06ce..0fb5e20a9960 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/RemoteMachinesHelper/VSCodeRemoteMachinesApi.cs @@ -14,6 +14,12 @@ namespace Community.PowerToys.Run.Plugin.VSCodeWorkspaces.RemoteMachinesHelper { public class VSCodeRemoteMachinesApi { + private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + }; + public VSCodeRemoteMachinesApi() { } @@ -35,7 +41,7 @@ public List Machines try { - JsonElement vscodeSettingsFile = JsonSerializer.Deserialize(fileContent, new JsonSerializerOptions() { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip }); + JsonElement vscodeSettingsFile = JsonSerializer.Deserialize(fileContent, _serializerOptions); if (vscodeSettingsFile.TryGetProperty("remote.SSH.configFile", out var pathElement)) { var path = pathElement.GetString(); diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs index bd318f0896c6..0be408dc4947 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/WorkspacesHelper/VSCodeWorkspacesApi.cs @@ -46,6 +46,11 @@ private VSCodeWorkspace ParseVSCodeUriAndAuthority(string uri, string authority, path = path[1..]; } + if (!DoesPathExist(path, workspaceEnv.Value, machineName)) + { + return null; + } + var folderName = Path.GetFileName(path); // Check we haven't returned '' if we have a path like C:\ @@ -67,6 +72,24 @@ private VSCodeWorkspace ParseVSCodeUriAndAuthority(string uri, string authority, }; } + private bool DoesPathExist(string path, WorkspaceEnvironment workspaceEnv, string machineName) + { + if (workspaceEnv == WorkspaceEnvironment.Local || workspaceEnv == WorkspaceEnvironment.RemoteWSL) + { + var resolvedPath = path; + + if (workspaceEnv == WorkspaceEnvironment.RemoteWSL) + { + resolvedPath = $"\\\\wsl$\\{machineName}{resolvedPath.Replace('/', '\\')}"; + } + + return Directory.Exists(resolvedPath) || File.Exists(resolvedPath); + } + + // If the workspace environment is not Local or WSL, assume the path exists + return true; + } + public List Workspaces { get diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs index afc2d0232dd9..b6794611ee59 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs @@ -30,12 +30,23 @@ public class InputParserTests [DataRow("base99 abc", null)] [DataRow("base64s abc", null)] [DataRow("base64d abc=", typeof(Base64.Base64DecodeRequest))] - public void ParserTest(string input, Type? expectedRequestType) + [DataRow("url http://googl.de/u oii d", typeof(Uri.UrlEncodeRequest))] + [DataRow("urld http://googl.de/u oii d=oo", typeof(Uri.UrlDecodeRequest))] + [DataRow("esc:data jjdje332j 3 3l2jl32", typeof(Uri.DataEscapeRequest))] + [DataRow("esc:hex d", typeof(Uri.HexEscapeRequest))] + [DataRow("esc:hex 4", typeof(Uri.HexEscapeRequest))] + [DataRow("esc:hex ", typeof(Uri.HexEscapeRequest), true)] + [DataRow("esc:hex z44", typeof(Uri.HexEscapeRequest), true)] + [DataRow("uesc:data jjdje332j 3 3l2jl32", typeof(Uri.DataUnescapeRequest))] + [DataRow("uesc:hex %21", typeof(Uri.HexUnescapeRequest))] + [DataRow("uesc:hex 4", typeof(Uri.HexUnescapeRequest))] + [DataRow("uesc:hex ", typeof(Uri.HexUnescapeRequest))] + [DataRow("uesc:hex z44", typeof(Uri.HexUnescapeRequest))] + public void ParserTest(string input, Type? expectedRequestType, bool expectException = false) { var parser = new InputParser(); var query = new Query(input); - var expectException = false; string? command = null; if (query.Terms.Count == 0) { @@ -79,15 +90,24 @@ public void ParserTest(string input, Type? expectedRequestType) private static bool CommandIsKnown(string command) { string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64", "base64d" }; - if (hashes.Contains(command.ToLowerInvariant())) { return true; } - Regex regex = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + Regex regexGuiUUID = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + if (regexGuiUUID.IsMatch(command)) + { + return true; + } + + string[] uriCommands = new string[] { "url", "urld", "esc:hex", "uesc:hex", "esc:data", "uesc:data" }; + if (uriCommands.Contains(command.ToLowerInvariant())) + { + return true; + } - return regex.IsMatch(command); + return false; } } } diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj index fde3e7f4abca..80fb46deea9a 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj @@ -36,6 +36,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs similarity index 100% rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64DecodeRequest.cs rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64DecodeRequest.cs diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs similarity index 100% rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Base64/Base64Request.cs rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Base64/Base64Request.cs diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs similarity index 100% rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDGenerator.cs rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs similarity index 100% rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/GUID/GUIDRequest.cs rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs similarity index 100% rename from src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Hashing/HashRequest.cs rename to src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Hashing/HashRequest.cs diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs new file mode 100644 index 000000000000..cfe581a294fa --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataEscapeRequest.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Text; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri +{ + public class DataEscapeRequest : IComputeRequest + { + public byte[] Result { get; set; } + + public string Description => "Data string escaped"; + + public bool IsSuccessful { get; set; } + + public string ErrorMessage { get; set; } + + private string DataToEscape { get; set; } + + public DataEscapeRequest(string dataToEscape) + { + DataToEscape = dataToEscape ?? throw new ArgumentNullException(nameof(dataToEscape)); + } + + public bool Compute() + { + IsSuccessful = true; + try + { + Result = Encoding.UTF8.GetBytes(System.Uri.EscapeDataString(DataToEscape)); + } + catch (Exception e) + { + Log.Exception(e.Message, e, GetType()); + ErrorMessage = e.Message; + IsSuccessful = false; + } + + return IsSuccessful; + } + + public string ResultToString() + { + return Encoding.UTF8.GetString(Result); + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs new file mode 100644 index 000000000000..2de144d81e27 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/DataUnescapeRequest.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Text; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri +{ + public class DataUnescapeRequest : IComputeRequest + { + public byte[] Result { get; set; } + + public string Description => "Data string unescaped"; + + public bool IsSuccessful { get; set; } + + public string ErrorMessage { get; set; } + + private string DataToUnescape { get; set; } + + public DataUnescapeRequest(string dataToUnescape) + { + DataToUnescape = dataToUnescape ?? throw new ArgumentNullException(nameof(dataToUnescape)); + } + + public bool Compute() + { + IsSuccessful = true; + try + { + Result = Encoding.UTF8.GetBytes(System.Uri.UnescapeDataString(DataToUnescape)); + } + catch (Exception e) + { + Log.Exception(e.Message, e, GetType()); + ErrorMessage = e.Message; + IsSuccessful = false; + } + + return IsSuccessful; + } + + public string ResultToString() + { + if (Result != null) + { + return Encoding.UTF8.GetString(Result); + } + else + { + return string.Empty; + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs new file mode 100644 index 000000000000..7c47af69bc1f --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexEscapeRequest.cs @@ -0,0 +1,58 @@ +// 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.Linq; +using System.Text; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri +{ + public class HexEscapeRequest : IComputeRequest + { + public byte[] Result { get; set; } + + public string Description => "Hex escaped char"; + + public bool IsSuccessful { get; set; } + + public string ErrorMessage { get; set; } + + private string DataToEscape { get; set; } + + public HexEscapeRequest(string dataToEscape) + { + DataToEscape = dataToEscape ?? throw new ArgumentNullException(nameof(dataToEscape)); + + // Validate that we have only one character + if (dataToEscape.Length != 1) + { + throw new ArgumentOutOfRangeException(nameof(dataToEscape)); + } + } + + public bool Compute() + { + IsSuccessful = true; + try + { + char charToEscape = DataToEscape[0]; + Result = Encoding.UTF8.GetBytes(System.Uri.HexEscape(charToEscape)); + } + catch (Exception e) + { + Log.Exception(e.Message, e, GetType()); + ErrorMessage = e.Message; + IsSuccessful = false; + } + + return IsSuccessful; + } + + public string ResultToString() + { + return Encoding.UTF8.GetString(Result); + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs new file mode 100644 index 000000000000..8bd5e80ed05d --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/HexUnescapeRequest.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Text; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri +{ + public class HexUnescapeRequest : IComputeRequest + { + public byte[] Result { get; set; } + + public string Description => "Hex char unescaped"; + + public bool IsSuccessful { get; set; } + + public string ErrorMessage { get; set; } + + private string DataToUnescape { get; set; } + + public HexUnescapeRequest(string dataToUnescape) + { + DataToUnescape = dataToUnescape ?? throw new ArgumentNullException(nameof(dataToUnescape)); + } + + public bool Compute() + { + IsSuccessful = true; + try + { + int index = 0; + if (System.Uri.IsHexEncoding(DataToUnescape, index)) + { + Result = Encoding.UTF8.GetBytes(System.Uri.HexUnescape(DataToUnescape, ref index).ToString()); + } + } + catch (Exception e) + { + Log.Exception(e.Message, e, GetType()); + ErrorMessage = e.Message; + IsSuccessful = false; + } + + return IsSuccessful; + } + + public string ResultToString() + { + if (Result != null) + { + return Encoding.UTF8.GetString(Result); + } + else + { + return string.Empty; + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs new file mode 100644 index 000000000000..db26f7009a70 --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlDecodeRequest.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Security.Policy; +using System.Text; +using System.Web; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri +{ + public class UrlDecodeRequest : IComputeRequest + { + public byte[] Result { get; set; } + + public string Description => "Decoded URL"; + + public bool IsSuccessful { get; set; } + + public string ErrorMessage { get; set; } + + private string DataToDecode { get; set; } + + public UrlDecodeRequest(string dataToDecode) + { + DataToDecode = dataToDecode ?? throw new ArgumentNullException(nameof(dataToDecode)); + } + + public bool Compute() + { + IsSuccessful = true; + try + { + Result = Encoding.UTF8.GetBytes(HttpUtility.UrlDecode(DataToDecode)); + } + catch (Exception e) + { + Log.Exception(e.Message, e, GetType()); + ErrorMessage = e.Message; + IsSuccessful = false; + } + + return IsSuccessful; + } + + public string ResultToString() + { + if (Result != null) + { + return Encoding.UTF8.GetString(Result); + } + else + { + return string.Empty; + } + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs new file mode 100644 index 000000000000..1022cd702ace --- /dev/null +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/Uri/UrlEncodeRequest.cs @@ -0,0 +1,52 @@ +// 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.Security.Policy; +using System.Text; +using System.Web; +using Wox.Plugin.Logger; + +namespace Community.PowerToys.Run.Plugin.ValueGenerator.Uri +{ + public class UrlEncodeRequest : IComputeRequest + { + public byte[] Result { get; set; } + + public string Description => "Encoded URL"; + + public bool IsSuccessful { get; set; } + + public string ErrorMessage { get; set; } + + private string DataToEncode { get; set; } + + public UrlEncodeRequest(string dataToEncode) + { + DataToEncode = dataToEncode ?? throw new ArgumentNullException(nameof(dataToEncode)); + } + + public bool Compute() + { + IsSuccessful = true; + try + { + Result = Encoding.UTF8.GetBytes(HttpUtility.UrlEncode(DataToEncode)); + } + catch (Exception e) + { + Log.Exception(e.Message, e, GetType()); + ErrorMessage = e.Message; + IsSuccessful = false; + } + + return IsSuccessful; + } + + public string ResultToString() + { + return Encoding.UTF8.GetString(Result); + } + } +} diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs index 1581bab2972d..929366f597c8 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs @@ -8,6 +8,7 @@ using Community.PowerToys.Run.Plugin.ValueGenerator.Base64; using Community.PowerToys.Run.Plugin.ValueGenerator.GUID; using Community.PowerToys.Run.Plugin.ValueGenerator.Hashing; +using Community.PowerToys.Run.Plugin.ValueGenerator.Uri; using Wox.Plugin; using Wox.Plugin.Logger; @@ -26,7 +27,7 @@ public IComputeRequest ParseInput(Query query) string command = query.Terms[0]; - if (command.ToLower(null) == "md5") + if (command.Equals("md5", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); @@ -115,18 +116,81 @@ public IComputeRequest ParseInput(Query query) request = new GUIDRequest(version); } } - else if (command.ToLower(null) == "base64") + else if (command.Equals("base64", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new Base64Request(Encoding.UTF8.GetBytes(content)); } - else if (command.ToLower(null) == "base64d") + else if (command.Equals("base64d", StringComparison.OrdinalIgnoreCase)) { int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); request = new Base64DecodeRequest(content); } + else if (command.StartsWith("esc:", StringComparison.OrdinalIgnoreCase)) + { + // Escape things + if (command.Equals("esc:data", StringComparison.OrdinalIgnoreCase)) + { + int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); + string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); + request = new DataEscapeRequest(content); + } + else if (command.Equals("esc:hex", StringComparison.OrdinalIgnoreCase)) + { + int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); + string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); + + // This is only for single chars + if (content.Length > 1) + { + throw new ArgumentException($"Invalid Query: {query.RawUserQuery} (To many characters.)"); + } + else if (content.Length == 0) + { + throw new FormatException($"Invalid Query: {query.RawUserQuery}"); + } + + request = new HexEscapeRequest(content); + } + else + { + throw new FormatException($"Invalid Query: {query.RawUserQuery}"); + } + } + else if (command.StartsWith("uesc:", StringComparison.OrdinalIgnoreCase)) + { + // Unescape things + if (command.Equals("uesc:data", StringComparison.OrdinalIgnoreCase)) + { + int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); + string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); + request = new DataUnescapeRequest(content); + } + else if (command.Equals("uesc:hex", StringComparison.OrdinalIgnoreCase)) + { + int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); + string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); + request = new HexUnescapeRequest(content); + } + else + { + throw new FormatException($"Invalid Query: {query.RawUserQuery}"); + } + } + else if (command.Equals("url", StringComparison.OrdinalIgnoreCase)) + { + int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); + string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); + request = new UrlEncodeRequest(content); + } + else if (command.Equals("urld", StringComparison.OrdinalIgnoreCase)) + { + int commandIndex = query.RawUserQuery.IndexOf(command, StringComparison.InvariantCultureIgnoreCase); + string content = query.RawUserQuery.Substring(commandIndex + command.Length).Trim(); + request = new UrlDecodeRequest(content); + } else { throw new FormatException($"Invalid Query: {query.RawUserQuery}"); diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs index 36536da3039a..1ed218606356 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs @@ -93,7 +93,16 @@ public List Query(Query query) try { IComputeRequest computeRequest = _inputParser.ParseInput(query); - results.Add(GetResult(computeRequest)); + var result = GetResult(computeRequest); + + if (!string.IsNullOrEmpty(result.Title)) + { + results.Add(result); + } + else + { + return results; + } } catch (ArgumentException e) { diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj index f5740c38dd7b..fb6c164ad4f7 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj @@ -37,6 +37,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs index 669f20d63c19..525b4c6ab374 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text; using System.Windows.Controls; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; @@ -38,6 +39,10 @@ public class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider, IReloa public static string PluginID => "9F1B49201C3F4BF781CAAD5CD88EA4DC"; + private static readonly CompositeFormat PluginInBrowserName = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_in_browser_name); + private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open); + private static readonly CompositeFormat PluginSearchFailed = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_search_failed); + public IEnumerable AdditionalOptions => new List() { new PluginAdditionalOption() @@ -66,7 +71,7 @@ public List Query(Query query) results.Add(new Result { Title = Properties.Resources.plugin_description.Remove(Description.Length - 1, 1), - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.plugin_in_browser_name, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), + SubTitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), QueryTextDisplay = string.Empty, IcoPath = _iconPath, ProgramArguments = arguments, @@ -98,7 +103,7 @@ public List Query(Query query) var result = new Result { Title = searchTerm, - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.plugin_open, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), + SubTitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), QueryTextDisplay = searchTerm, IcoPath = _iconPath, }; @@ -170,7 +175,7 @@ public void Init(PluginInitContext context) onPluginError = () => { - string errorMsgString = string.Format(CultureInfo.CurrentCulture, Properties.Resources.plugin_search_failed, BrowserInfo.Name ?? BrowserInfo.MSEdgeName); + string errorMsgString = string.Format(CultureInfo.CurrentCulture, PluginSearchFailed, BrowserInfo.Name ?? BrowserInfo.MSEdgeName); Log.Error(errorMsgString, this.GetType()); _context.API.ShowMsg( diff --git a/src/modules/launcher/Plugins/DynamicPlugin.props b/src/modules/launcher/Plugins/DynamicPlugin.props new file mode 100644 index 000000000000..746e635e8d1d --- /dev/null +++ b/src/modules/launcher/Plugins/DynamicPlugin.props @@ -0,0 +1,12 @@ + + + + true + + + + + false + + + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs index e521246768e2..12cce1ea393d 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/DriveOrSharedFolderTests.cs @@ -15,6 +15,8 @@ namespace Microsoft.Plugin.Folder.UnitTests [TestClass] public class DriveOrSharedFolderTests { + private static readonly string[] DriverNames = new[] { "c:", "d:" }; + [DataTestMethod] [DataRow(@"\\test-server\testdir", true)] [DataRow(@"c:", true)] @@ -33,7 +35,7 @@ public void IsDriveOrSharedFolder_WhenCalled(string search, bool expectedSuccess var driveInformationMock = new Mock(); driveInformationMock.Setup(r => r.GetDriveNames()) - .Returns(() => new[] { "c:", "d:" }); + .Returns(() => DriverNames); var folderLinksMock = new Mock(); var folderHelper = new FolderHelper(driveInformationMock.Object, folderLinksMock.Object); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs index ff93b9ad9889..aa4e0a055367 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder.UnitTests/QueryEnvironmentVariableTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.Plugin.Folder.UnitTests [TestClass] public class QueryEnvironmentVariableTests { - private static IQueryEnvironmentVariable _queryEnvironmentVariable; + private static QueryEnvironmentVariable _queryEnvironmentVariable; private static MockFileSystem _fileSystem; [TestInitialize] diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs index 84d52ebc45f0..9a6968bc117b 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Main.cs @@ -37,7 +37,7 @@ public class Main : IPlugin, IPluginI18n, ISavable, IContextMenu, IDisposable }; private static PluginInitContext _context; - private IContextMenu _contextMenuLoader; + private ContextMenuLoader _contextMenuLoader; private bool _disposed; public string Name => Properties.Resources.wox_plugin_folder_plugin_name; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj index 384b278b2d9e..9f003281f342 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj @@ -54,6 +54,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs index c4cf6fbb1d09..a827cb7e15b9 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/QueryInternalDirectory.cs @@ -75,7 +75,7 @@ public static bool RecursiveSearch(string query) { // folder exist, add \ at the end of doesn't exist // Using Ordinal since this is internal and is used for a symbol - if (!search.EndsWith(@"\", StringComparison.Ordinal)) + if (!search.EndsWith('\\')) { search += @"\"; } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs index 5bf2a391f320..0a01febcc5c2 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/CreateOpenCurrentFolderResult.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; +using System.Text; using Wox.Plugin; namespace Microsoft.Plugin.Folder.Sources.Result @@ -12,6 +13,8 @@ public class CreateOpenCurrentFolderResult : IItemResult { private readonly IShellAction _shellAction; + private static readonly CompositeFormat WoxPluginFolderSelectFolderFirstResultTitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_first_result_title); + public string Search { get; set; } public CreateOpenCurrentFolderResult(string search) @@ -29,10 +32,10 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi) { return new Wox.Plugin.Result { - Title = string.Format(CultureInfo.InvariantCulture, Properties.Resources.wox_plugin_folder_select_folder_first_result_title, new DirectoryInfo(Search).Name), + Title = string.Format(CultureInfo.InvariantCulture, WoxPluginFolderSelectFolderFirstResultTitle, new DirectoryInfo(Search).Name), QueryTextDisplay = Search, SubTitle = Properties.Resources.wox_plugin_folder_select_folder_first_result_subtitle, - ToolTipData = new ToolTipData(string.Format(CultureInfo.InvariantCulture, Properties.Resources.wox_plugin_folder_select_folder_first_result_title, new DirectoryInfo(Search).Name), Properties.Resources.wox_plugin_folder_select_folder_first_result_subtitle), + ToolTipData = new ToolTipData(string.Format(CultureInfo.InvariantCulture, WoxPluginFolderSelectFolderFirstResultTitle, new DirectoryInfo(Search).Name), Properties.Resources.wox_plugin_folder_select_folder_first_result_subtitle), IcoPath = Search, Score = 500, Action = c => _shellAction.ExecuteSanitized(Search, contextApi), diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs index 3295c3e10002..f37e54d4381a 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/EnvironmentVariableResult.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Text; using Wox.Infrastructure; using Wox.Plugin; @@ -10,7 +11,9 @@ namespace Microsoft.Plugin.Folder.Sources.Result { public class EnvironmentVariableResult : IItemResult { - private readonly IShellAction _shellAction = new ShellAction(); + private readonly ShellAction _shellAction = new ShellAction(); + + private static readonly CompositeFormat WoxPluginFolderSelectFolderResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_result_subtitle); public string Search { get; set; } @@ -35,8 +38,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi) IcoPath = Path, // Using CurrentCulture since this is user facing - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Path), - ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Path)), + SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Path), + ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Path)), QueryTextDisplay = Path, ContextData = new SearchResult { Type = ResultType.Folder, Path = Path }, Action = c => _shellAction.Execute(Path, contextApi), diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs index bf55c89738f9..4ab22e5c514e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO.Abstractions; +using System.Text; using Wox.Infrastructure; using Wox.Plugin; @@ -11,7 +12,9 @@ namespace Microsoft.Plugin.Folder.Sources.Result { public class FileItemResult : IItemResult { - private static readonly IShellAction ShellAction = new ShellAction(); + private static readonly ShellAction ShellAction = new ShellAction(); + + private static readonly CompositeFormat WoxPluginFolderSelectFileResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_file_result_subtitle); private readonly IPath _path; @@ -38,8 +41,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi) Title = Title, // Using CurrentCulture since this is user facing - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_file_result_subtitle, FilePath), - ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_file_result_subtitle, FilePath)), + SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFileResultSubtitle, FilePath), + ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFileResultSubtitle, FilePath)), IcoPath = FilePath, Action = c => ShellAction.Execute(FilePath, contextApi), ContextData = new SearchResult { Type = ResultType.File, Path = FilePath }, diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs index 55ccdaf1732d..d8c37e31f1c6 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Text; using Wox.Infrastructure; using Wox.Plugin; @@ -10,7 +11,9 @@ namespace Microsoft.Plugin.Folder.Sources.Result { public class FolderItemResult : IItemResult { - private static readonly IShellAction ShellAction = new ShellAction(); + private static readonly ShellAction ShellAction = new ShellAction(); + + private static readonly CompositeFormat WoxPluginFolderSelectFolderResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_result_subtitle); public FolderItemResult() { @@ -39,8 +42,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi) IcoPath = Path, // Using CurrentCulture since this is user facing - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle), - ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle)), + SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Subtitle), + ToolTipData = new ToolTipData(Title, string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Subtitle)), QueryTextDisplay = Path, ContextData = new SearchResult { Type = ResultType.Folder, Path = Path }, Action = c => ShellAction.Execute(Path, contextApi), diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs index e41f363a0ffb..16f1c8bd921e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/TruncatedItemResult.cs @@ -3,12 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Text; using Wox.Plugin; namespace Microsoft.Plugin.Folder.Sources.Result { public class TruncatedItemResult : IItemResult { + private static readonly CompositeFormat MicrosoftPluginFolderTruncationWarningSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_folder_truncation_warning_subtitle); + public int PreTruncationCount { get; set; } public int PostTruncationCount { get; set; } @@ -25,8 +28,8 @@ public Wox.Plugin.Result Create(IPublicAPI contextApi) QueryTextDisplay = Search, // Using CurrentCulture since this is user facing - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Microsoft_plugin_folder_truncation_warning_subtitle, PostTruncationCount, PreTruncationCount), - ToolTipData = new ToolTipData(Properties.Resources.Microsoft_plugin_folder_truncation_warning_title, string.Format(CultureInfo.CurrentCulture, Properties.Resources.Microsoft_plugin_folder_truncation_warning_subtitle, PostTruncationCount, PreTruncationCount)), + SubTitle = string.Format(CultureInfo.CurrentCulture, MicrosoftPluginFolderTruncationWarningSubtitle, PostTruncationCount, PreTruncationCount), + ToolTipData = new ToolTipData(Properties.Resources.Microsoft_plugin_folder_truncation_warning_title, string.Format(CultureInfo.CurrentCulture, MicrosoftPluginFolderTruncationWarningSubtitle, PostTruncationCount, PreTruncationCount)), IcoPath = WarningIconPath, }; } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs index 364f7faa3483..3e47f3a15537 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/ShellAction.cs @@ -32,7 +32,7 @@ private static string SanitizedPath(string search) // A network path must start with \\ // Using Ordinal since this is internal and used with a symbol - if (!sanitizedPath.StartsWith("\\", StringComparison.Ordinal)) + if (!sanitizedPath.StartsWith('\\')) { return sanitizedPath; } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs index c8c21d30813e..a3d0d8a0d1e0 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderProcessor.cs @@ -24,7 +24,7 @@ public IEnumerable Results(string actionKeyword, string search) .Select(item => CreateFolderResult(item.Nickname, item.Path, item.Path, search)); } - private static IItemResult CreateFolderResult(string title, string subtitle, string path, string search) + private static UserFolderResult CreateFolderResult(string title, string subtitle, string path, string search) { return new UserFolderResult { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs index 2cebcad33ed5..58db87f3ceb6 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Globalization; +using System.Text; using Microsoft.Plugin.Folder.Sources; using Microsoft.Plugin.Folder.Sources.Result; using Wox.Infrastructure; @@ -12,7 +13,7 @@ namespace Microsoft.Plugin.Folder { public class UserFolderResult : IItemResult { - private readonly IShellAction _shellAction = new ShellAction(); + private readonly ShellAction _shellAction = new ShellAction(); public string Search { get; set; } @@ -22,6 +23,8 @@ public class UserFolderResult : IItemResult public string Subtitle { get; set; } + private static readonly CompositeFormat WoxPluginFolderSelectFolderResultSubtitle = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_folder_select_folder_result_subtitle); + public Result Create(IPublicAPI contextApi) { return new Result(StringMatcher.FuzzySearch(Search, Title).MatchData) @@ -30,7 +33,7 @@ public Result Create(IPublicAPI contextApi) IcoPath = Path, // Using CurrentCulture since this is user facing - SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle), + SubTitle = string.Format(CultureInfo.CurrentCulture, WoxPluginFolderSelectFolderResultSubtitle, Subtitle), QueryTextDisplay = Path, ContextData = new SearchResult { Type = ResultType.Folder, Path = Path }, Action = c => _shellAction.Execute(Path, contextApi), diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs index 38944a836cf4..53e081be3262 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Main.cs @@ -63,7 +63,7 @@ internal class Main : ISettingProvider, IPlugin, ISavable, IPluginI18n, IContext }, }; - private IContextMenu _contextMenuLoader; + private ContextMenuLoader _contextMenuLoader; private bool disposedValue; // To save the configurations of plugins diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj index bf3b3cb1eed2..43e1920f4682 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj @@ -30,6 +30,26 @@ + + + runtime + + + + runtime + + + + runtime + + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs index 12c36c79c867..3cb8cf2f4896 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/ListRepositoryTests.cs @@ -16,7 +16,7 @@ public void ContainsShouldReturnTrueWhenListIsInitializedWithItem() { // Arrange var itemName = "originalItem1"; - IRepository repository = new ListRepository() { itemName }; + ListRepository repository = new ListRepository() { itemName }; // Act var result = repository.Contains(itemName); @@ -29,7 +29,7 @@ public void ContainsShouldReturnTrueWhenListIsInitializedWithItem() public void ContainsShouldReturnTrueWhenListIsUpdatedWithAdd() { // Arrange - IRepository repository = new ListRepository(); + ListRepository repository = new ListRepository(); // Act var itemName = "newItem"; @@ -45,7 +45,7 @@ public void ContainsShouldReturnFalseWhenListIsUpdatedWithRemove() { // Arrange var itemName = "originalItem1"; - IRepository repository = new ListRepository() { itemName }; + ListRepository repository = new ListRepository() { itemName }; // Act repository.Remove(itemName); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs index 4cb1b0510fa2..dc4b8e4f7ea8 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs @@ -25,6 +25,7 @@ public class Win32ProgramRepositoryTest private List _fileSystemWatchers; private List> _fileSystemMocks; + private static readonly string[] Path = new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" }; [TestInitialize] public void SetFileSystemWatchers() @@ -219,7 +220,7 @@ public void Win32ProgramRepositoryMustNotCreateUrlAppWhenCreatedEventIsRaised(st // File.ReadAllLines must be mocked for url applications var mockFile = new Mock(); - mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" }); + mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(Path); Win32Program.FileWrapper = mockFile.Object; // Act @@ -268,7 +269,7 @@ public void Win32ProgramRepositoryMustCallOnAppDeletedForUrlAppsWhenDeletedEvent // File.ReadAllLines must be mocked for url applications var mockFile = new Mock(); - mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" }); + mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(Path); Win32Program.FileWrapper = mockFile.Object; string fullPath = directory + "\\" + path; @@ -292,7 +293,7 @@ public void Win32ProgramRepositoryMustCallOnAppRenamedForUrlAppsWhenRenamedEvent // File.ReadAllLines must be mocked for url applications var mockFile = new Mock(); - mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" }); + mockFile.Setup(m => m.ReadLines(It.IsAny())).Returns(Path); Win32Program.FileWrapper = mockFile.Object; string oldFullPath = directory + "\\" + oldpath; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs index ebd6d0d3dbbc..f17b4a239f3b 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Main.cs @@ -91,7 +91,7 @@ private IEnumerable Query(string program, string programArguments) .Where(r => r?.Score > 0) .ToArray(); - if (result.Any()) + if (result.Length != 0) { var maxScore = result.Max(x => x.Score); return result diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj index c334a5876b77..4507376b0610 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj @@ -45,7 +45,18 @@ false - + + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs index 51d166e66a2b..3085a53470b5 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs @@ -308,7 +308,7 @@ internal string ResourceFromPri(string packageFullName, string resourceReference { parsed = prefix + key; } - else if (key.StartsWith("/", StringComparison.Ordinal)) + else if (key.StartsWith('/')) { parsed = prefix + "//" + key; } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index 934f518bbfb2..a54b4497f335 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -761,7 +761,7 @@ private static IEnumerable CustomProgramPaths(IEnumerable .ToList() ?? Enumerable.Empty(); // Function to obtain the list of applications, the locations of which have been added to the env variable PATH - private static IEnumerable PathEnvironmentProgramPaths(IList suffixes) + private static List PathEnvironmentProgramPaths(IList suffixes) { // To get all the locations stored in the PATH env variable var pathEnvVariable = Environment.GetEnvironmentVariable("PATH"); @@ -788,7 +788,7 @@ private static List IndexPath(IList suffixes, List index .SelectMany(indexLocation => ProgramPaths(indexLocation, suffixes)) .ToList(); - private static IEnumerable StartMenuProgramPaths(IList suffixes) + private static List StartMenuProgramPaths(IList suffixes) { var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu); var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu); @@ -797,7 +797,7 @@ private static IEnumerable StartMenuProgramPaths(IList suffixes) return IndexPath(suffixes, indexLocation); } - private static IEnumerable DesktopProgramPaths(IList suffixes) + private static List DesktopProgramPaths(IList suffixes) { var directory1 = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); var directory2 = Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory); @@ -807,7 +807,7 @@ private static IEnumerable DesktopProgramPaths(IList suffixes) return IndexPath(suffixes, indexLocation); } - private static IEnumerable RegistryAppProgramPaths(IList suffixes) + private static List RegistryAppProgramPaths(IList suffixes) { // https://msdn.microsoft.com/library/windows/desktop/ee872121 const string appPaths = @"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs index 584a9a4f7a04..78269398eab7 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs @@ -11,6 +11,7 @@ using System.IO.Abstractions; using System.Linq; using System.Reflection; +using System.Text; using System.Windows.Input; using ManagedCommon; using Microsoft.Plugin.Shell.Properties; @@ -33,6 +34,8 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavab private readonly ShellPluginSettings _settings; private readonly PluginJsonStorage _storage; + private static readonly CompositeFormat WoxPluginCmdCmdHasBeenExecutedTimes = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times); + private string IconPath { get; set; } public string Name => Properties.Resources.wox_plugin_cmd_plugin_name; @@ -71,6 +74,7 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu, ISavab }; private PluginInitContext _context; + private static readonly char[] Separator = new[] { ' ' }; public Main() { @@ -123,7 +127,7 @@ private List GetHistoryCmds(string cmd, Result result) if (m.Key == cmd) { // Using CurrentCulture since this is user facing - result.SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times, m.Value); + result.SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value); return null; } @@ -132,7 +136,7 @@ private List GetHistoryCmds(string cmd, Result result) Title = m.Key, // Using CurrentCulture since this is user facing - SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times, m.Value), + SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value), IcoPath = IconPath, Action = c => { @@ -171,7 +175,7 @@ private List ResultsFromlHistory() Title = m.Key, // Using CurrentCulture since this is user facing - SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_cmd_cmd_has_been_executed_times, m.Value), + SubTitle = Properties.Resources.wox_plugin_cmd_plugin_name + ": " + string.Format(CultureInfo.CurrentCulture, WoxPluginCmdCmdHasBeenExecutedTimes, m.Value), IcoPath = IconPath, Action = c => { @@ -285,7 +289,7 @@ private ProcessStartInfo PrepareProcessStartInfo(string command, RunAsType runAs } else { - var parts = command.Split(new[] { ' ' }, 2); + var parts = command.Split(Separator, 2); if (parts.Length == 2) { var filename = parts[0]; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj index b8218ad90f41..7e16361c9cdf 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj @@ -40,6 +40,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs index bf506e0efa2d..6edf30817b6f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/ShellPluginSettings.cs @@ -25,13 +25,13 @@ public class ShellPluginSettings public void AddCmdHistory(string cmdName) { - if (Count.ContainsKey(cmdName)) + if (Count.TryGetValue(cmdName, out int currentCount)) { - Count[cmdName] += 1; + Count[cmdName] = currentCount + 1; } else { - Count.Add(cmdName, 1); + Count[cmdName] = 1; } } } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj index cff4db6b21b2..dc10119fe005 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj @@ -37,6 +37,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs index c2ffebddf3a1..1792793d6e68 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ContextMenuHelper.cs @@ -51,8 +51,8 @@ internal static List GetContextMenuResults(in Result result) // Hide menu if Explorer.exe is the shell process or the process name is ApplicationFrameHost.exe // In the first case we would crash the windows ui and in the second case we would kill the generic process for uwp apps. - if (!windowData.Process.IsShellProcess && !(windowData.Process.IsUwpApp & windowData.Process.Name.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "applicationframehost.exe") - && !(windowData.Process.IsFullAccessDenied & WindowWalkerSettings.Instance.HideKillProcessOnElevatedProcesses)) + if (!windowData.Process.IsShellProcess && !(windowData.Process.IsUwpApp && string.Equals(windowData.Process.Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase)) + && !(windowData.Process.IsFullAccessDenied && WindowWalkerSettings.Instance.HideKillProcessOnElevatedProcesses)) { contextMenu.Add(new ContextMenuResult { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs index b7b4484dfc9a..05f5f5d8e950 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs @@ -1,6 +1,7 @@ // 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 Microsoft.Plugin.WindowWalker.Properties; using Wox.Infrastructure; @@ -26,7 +27,7 @@ internal static List GetResultList(List searchControllerRe foreach (SearchResult x in searchControllerResults) { - if (x.Result.Process.Name.ToLower(System.Globalization.CultureInfo.InvariantCulture) == "explorer.exe" && x.Result.Process.IsShellProcess) + if (string.Equals(x.Result.Process.Name, "explorer.exe", StringComparison.OrdinalIgnoreCase) && x.Result.Process.IsShellProcess) { addExplorerInfo = true; } diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs index 8fd3c129b9b6..28ca9e062a64 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/Window.cs @@ -378,7 +378,7 @@ private static WindowProcess CreateWindowProcessInstance(IntPtr hWindow) // Correct the process data if the window belongs to a uwp app hosted by 'ApplicationFrameHost.exe' // (This only works if the window isn't minimized. For minimized windows the required child window isn't assigned.) - if (_handlesToProcessCache[hWindow].Name.ToUpperInvariant() == "APPLICATIONFRAMEHOST.EXE") + if (string.Equals(_handlesToProcessCache[hWindow].Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase)) { new Task(() => { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs index c7d7bd1d1f46..b7bf00e96292 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/WindowProcess.cs @@ -113,7 +113,7 @@ internal bool IsFullAccessDenied internal WindowProcess(uint pid, uint tid, string name) { UpdateProcessInfo(pid, tid, name); - _isUwpApp = Name.ToUpperInvariant().Equals("APPLICATIONFRAMEHOST.EXE", StringComparison.Ordinal); + _isUwpApp = string.Equals(Name, "ApplicationFrameHost.exe", StringComparison.OrdinalIgnoreCase); } /// diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj index cbb35f51c2f1..5077812e5cd2 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj @@ -43,6 +43,17 @@ + + + + runtime + + + + runtime + + + True diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs index b5bee39a8a02..6073278a364c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs @@ -245,5 +245,30 @@ public void Interpret_MustReturnExpectedResult_WhenCalled(string input, decimal Assert.IsNotNull(result); Assert.AreEqual(expectedResult, result.Result); } + + private static IEnumerable Interpret_TestScientificNotation_WhenCalled_Data => + new[] + { + new object[] { "0.2E1", "en-US", 2M }, + new object[] { "0,2E1", "pt-PT", 2M }, + }; + + [DataTestMethod] + [DynamicData(nameof(Interpret_TestScientificNotation_WhenCalled_Data))] + public void Interpret_TestScientificNotation_WhenCalled(string input, string sourceCultureName, decimal expectedResult) + { + // Arrange + var translator = NumberTranslator.Create(new CultureInfo(sourceCultureName, false), new CultureInfo("en-US", false)); + var engine = new CalculateEngine(); + + // Act + // Using en-us culture to have a fixed number style + var translatedInput = translator.Translate(input); + var result = engine.Interpret(translatedInput, new CultureInfo("en-US", false), out _); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(expectedResult, result.Result); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs index a12744b68417..92bc41794765 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs @@ -145,6 +145,15 @@ public void NoErrorForDivisionByNumberWithDecimalDigits(string typedString) [DataRow("pipipie", "pi * pi * pi * e")] [DataRow("(1+1)(3+2)(1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] [DataRow("(1+1) (3+2) (1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] + [DataRow("1.0E2", "(1.0 * 10^(2))")] + [DataRow("-1.0E-2", "(-1.0 * 10^(-2))")] + [DataRow("1.2E2", "(1.2 * 10^(2))")] + [DataRow("5/1.0E2", "5/(1.0 * 10^(2))")] + [DataRow("0.1E2", "(0.1 * 10^(2))")] + [DataRow(".1E2", "(.1 * 10^(2))")] + [DataRow(".1E2", "(.1 * 10^(2))")] + [DataRow("5/5E3", "5/(5 * 10^(3))")] + [DataRow("1.E2", "(1. * 10^(2))")] public void RightHumanMultiplicationExpressionTransformation(string typedString, string expectedQuery) { // Setup @@ -200,6 +209,12 @@ public void NoErrorForHumanMultiplicationExpressions(string typedString) [DataRow("pilog(100)", 6.2831853072)] [DataRow("3log(100)", 6)] [DataRow("2e", 5.4365636569)] + [DataRow("2E2", 200)] + [DataRow("2E-2", 0.02)] + [DataRow("1.2E2", 120)] + [DataRow("1.2E-1", 0.12)] + [DataRow("5/5E3", 0.001)] + [DataRow("-5/5E3", -0.001)] [DataRow("(1+1)(3+2)", 10)] [DataRow("(1+1)cos(pi)", -2)] [DataRow("log(100)cos(pi)", -2)] diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/BracketHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/BracketHelper.cs index 4845c9967434..de689bbf2a51 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/BracketHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/BracketHelper.cs @@ -45,12 +45,12 @@ public static bool IsBracketComplete(string query) continue; default: { - throw new ArgumentOutOfRangeException(nameof(direction), direction, "Can't process value"); + throw new ArgumentOutOfRangeException($"Can't process value (Parameter direction: {direction})"); } } } - return !trailTest.Any(); + return trailTest.Count == 0; } private static (TrailDirection Direction, TrailType Type) BracketTrail(char @char) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index 50b94b1e541b..f63be439e8de 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Globalization; using System.Text.RegularExpressions; namespace Microsoft.PowerToys.Run.Plugin.Calculator @@ -18,6 +20,7 @@ public static class CalculateHelper @"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" + @"pi|" + @"==|~=|&&|\|\||" + + @"((-?(\d+(\.\d*)?)|-?(\.\d+))[E](-?\d+))|" + /* expression from CheckScientificNotation between parenthesis */ @"e|[0-9]|0x[0-9a-fA-F]+|0b[01]+|[\+\-\*\/\^\., ""]|[\(\)\|\!\[\]]" + @")+$", RegexOptions.Compiled); @@ -51,7 +54,8 @@ public static bool InputValid(string input) public static string FixHumanMultiplicationExpressions(string input) { - var output = CheckNumberOrConstantThenParenthesisExpr(input); + var output = CheckScientificNotation(input); + output = CheckNumberOrConstantThenParenthesisExpr(output); output = CheckNumberOrConstantThenFunc(output); output = CheckParenthesisExprThenFunc(output); output = CheckParenthesisExprThenParenthesisExpr(output); @@ -60,6 +64,22 @@ public static string FixHumanMultiplicationExpressions(string input) return output; } + private static string CheckScientificNotation(string input) + { + /** + * NOTE: By the time the expression gets to us, it's already in English format. + * + * Regex explanation: + * (-?(\d+({0}\d*)?)|-?({0}\d+)): Used to capture one of two types: + * -?(\d+({0}\d*)?): Captures a decimal number starting with a number (e.g. "-1.23") + * -?({0}\d+): Captures a decimal number without leading number (e.g. ".23") + * E: Captures capital 'E' + * (-?\d+): Captures an integer number (e.g. "-1" or "23") + */ + var p = @"(-?(\d+(\.\d*)?)|-?(\.\d+))E(-?\d+)"; + return Regex.Replace(input, p, "($1 * 10^($5))"); + } + /* * num (exp) * const (exp) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs index ce0d314862a3..3a1d0fec7864 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Main.cs @@ -34,6 +34,9 @@ public class Main : IPlugin, IPluginI18n, IDisposable, ISettingProvider private bool _disposed; + private static readonly CompositeFormat WoxPluginCalculatorInEnFormatDescription = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_calculator_in_en_format_description); + private static readonly CompositeFormat WoxPluginCalculatorOutEnFormatDescription = System.Text.CompositeFormat.Parse(Properties.Resources.wox_plugin_calculator_out_en_format_description); + public IEnumerable AdditionalOptions => new List() { // The number examples has to be created at runtime to prevent translation. @@ -41,14 +44,14 @@ public class Main : IPlugin, IPluginI18n, IDisposable, ISettingProvider { Key = "InputUseEnglishFormat", DisplayLabel = Resources.wox_plugin_calculator_in_en_format, - DisplayDescription = string.Format(CultureInfo.CurrentCulture, Resources.wox_plugin_calculator_in_en_format_description, 1000.55.ToString("N2", new CultureInfo("en-us"))), + DisplayDescription = string.Format(CultureInfo.CurrentCulture, WoxPluginCalculatorInEnFormatDescription, 1000.55.ToString("N2", new CultureInfo("en-us"))), Value = false, }, new PluginAdditionalOption() { Key = "OutputUseEnglishFormat", DisplayLabel = Resources.wox_plugin_calculator_out_en_format, - DisplayDescription = string.Format(CultureInfo.CurrentCulture, Resources.wox_plugin_calculator_out_en_format_description, 1000.55.ToString("G", new CultureInfo("en-us"))), + DisplayDescription = string.Format(CultureInfo.CurrentCulture, WoxPluginCalculatorOutEnFormatDescription, 1000.55.ToString("G", new CultureInfo("en-us"))), Value = false, }, }; diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj index 62b691a4935c..cb639ef60ecc 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj @@ -45,6 +45,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs index 9278f109128c..ce27a728bc9f 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/NumberTranslator.cs @@ -66,35 +66,47 @@ public string TranslateBack(string input) private static string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex) { var outputBuilder = new StringBuilder(); + var hexRegex = new Regex(@"(?:(0x[\da-fA-F]+))"); - string[] tokens = splitRegex.Split(input); - foreach (string token in tokens) + string[] hexTokens = hexRegex.Split(input); + + foreach (string hexToken in hexTokens) { - int leadingZeroCount = 0; + if (hexToken.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + { + outputBuilder.Append(hexToken); + continue; + } - // Count leading zero characters. - foreach (char c in token) + string[] tokens = splitRegex.Split(hexToken); + foreach (string token in tokens) { - if (c != '0') + int leadingZeroCount = 0; + + // Count leading zero characters. + foreach (char c in token) { - break; - } + if (c != '0') + { + break; + } - leadingZeroCount++; - } + leadingZeroCount++; + } - // number is all zero characters. no need to add zero characters at the end. - if (token.Length == leadingZeroCount) - { - leadingZeroCount = 0; - } + // number is all zero characters. no need to add zero characters at the end. + if (token.Length == leadingZeroCount) + { + leadingZeroCount = 0; + } - decimal number; + decimal number; - outputBuilder.Append( - decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) - ? (new string('0', leadingZeroCount) + number.ToString(cultureTo)) - : token); + outputBuilder.Append( + decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) + ? (new string('0', leadingZeroCount) + number.ToString(cultureTo)) + : token); + } } return outputBuilder.ToString(); @@ -102,7 +114,7 @@ private static string Translate(string input, CultureInfo cultureFrom, CultureIn private static Regex GetSplitRegex(CultureInfo culture) { - var splitPattern = $"((?:\\d|[a-fA-F]|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; + var splitPattern = $"((?:\\d|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator)) { splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}"; diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj index 31ba17e1069b..d1d5af59189f 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj @@ -52,6 +52,33 @@ + + runtime + + + + runtime + + + + runtime + + + + runtime + + + + runtime + + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj index 06c24f50070a..9a882257ff12 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj @@ -53,6 +53,14 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj index f56985e9ffea..057a41460d15 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj @@ -49,6 +49,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs index 32d94595a174..d23e62ec0ae3 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/RegistryHelper.cs @@ -152,7 +152,7 @@ internal static void OpenRegistryKey(in string fullKey) /// The parent-key, also the root to start the search /// The sub-key to find /// A list with all found registry sub-keys - private static ICollection FindSubKey(in RegistryKey parentKey, in string searchSubKey) + private static Collection FindSubKey(in RegistryKey parentKey, in string searchSubKey) { var list = new Collection(); @@ -204,7 +204,7 @@ private static ICollection FindSubKey(in RegistryKey parentKey, i /// The registry parent-key /// (optional) The maximum count of the results /// A list with all found registry sub-keys - private static ICollection GetAllSubKeys(in RegistryKey parentKey, in int maxCount = 50) + private static Collection GetAllSubKeys(in RegistryKey parentKey, in int maxCount = 50) { var list = new Collection(); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs index 6afb6fef5ee3..2714d6e47086 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Helper/ResultHelper.cs @@ -80,7 +80,7 @@ internal static List GetValuesFromKey(in RegistryKey? key, in string ico return new List(0); } - ICollection> valueList = new List>(key.ValueCount); + List> valueList = new List>(key.ValueCount); var resultList = new List(); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj index 6a95fe6800c8..b59ea0d6f79c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj @@ -34,6 +34,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj index 1e22a25ae21a..7b9bde7411ee 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj @@ -34,6 +34,14 @@ + + + runtime + + + + runtime + diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs index aaba05682efe..e7999d6564f2 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Components/NetworkConnectionProperties.cs @@ -10,6 +10,7 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using System.Text; using Microsoft.PowerToys.Run.Plugin.System.Properties; namespace Microsoft.PowerToys.Run.Plugin.System.Components @@ -114,6 +115,9 @@ internal sealed class NetworkConnectionProperties /// internal IPAddressCollection WinsServers { get; private set; } + private static readonly CompositeFormat MicrosoftPluginSysGbps = CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_sys_Gbps); + private static readonly CompositeFormat MicrosoftPluginSysMbps = CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_sys_Mbps); + /// /// Initializes a new instance of the class. /// This private constructor is used when we crete the list of adapter (properties) by calling . @@ -286,7 +290,7 @@ private string GetAdapterTypeAsString(NetworkInterfaceType type) /// A formatted string like `100 MB/s` private static string GetFormattedSpeedValue(long speed) { - return (speed >= 1000000000) ? string.Format(CultureInfo.InvariantCulture, Resources.Microsoft_plugin_sys_Gbps, speed / 1000000000) : string.Format(CultureInfo.InvariantCulture, Resources.Microsoft_plugin_sys_Mbps, speed / 1000000); + return (speed >= 1000000000) ? string.Format(CultureInfo.InvariantCulture, MicrosoftPluginSysGbps, speed / 1000000000) : string.Format(CultureInfo.InvariantCulture, MicrosoftPluginSysMbps, speed / 1000000); } /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj index 26b17adf3c2a..56843e6d8155 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj @@ -33,6 +33,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Main.cs index cc0a3dc100e5..5d951d7b682e 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Main.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Text; using System.Windows.Controls; using System.Windows.Input; using ManagedCommon; @@ -27,6 +28,8 @@ public class Main : IPlugin, IPluginI18n, ISettingProvider, IContextMenu public static string PluginID => "5D69806A5A474115821C3E4C56B9C793"; + private static readonly CompositeFormat MicrosoftPluginTimedatePluginDescription = System.Text.CompositeFormat.Parse(Properties.Resources.Microsoft_plugin_timedate_plugin_description); + public IEnumerable AdditionalOptions { get @@ -93,7 +96,7 @@ public string GetTranslatedPluginDescription() string timeExample = Resources.Microsoft_plugin_timedate_plugin_description_example_time + "::" + DateTime.Now.ToString("T", CultureInfo.CurrentCulture); string dayExample = Resources.Microsoft_plugin_timedate_plugin_description_example_day + "::" + DateTime.Now.ToString("d", CultureInfo.CurrentCulture); string calendarWeekExample = Resources.Microsoft_plugin_timedate_plugin_description_example_calendarWeek + "::" + DateTime.Now.ToString("d", CultureInfo.CurrentCulture); - return string.Format(CultureInfo.CurrentCulture, Resources.Microsoft_plugin_timedate_plugin_description, Resources.Microsoft_plugin_timedate_plugin_description_example_day, dayExample, timeExample, calendarWeekExample); + return string.Format(CultureInfo.CurrentCulture, MicrosoftPluginTimedatePluginDescription, Resources.Microsoft_plugin_timedate_plugin_description_example_day, dayExample, timeExample, calendarWeekExample); } public string GetTranslatedPluginTitle() diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj index e6b18cf4a90c..a6b86ea8d986 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj @@ -33,6 +33,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/JsonSettingsListHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/JsonSettingsListHelper.cs index e80075faf902..07c759fa2a60 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/JsonSettingsListHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Helper/JsonSettingsListHelper.cs @@ -22,6 +22,10 @@ internal static class JsonSettingsListHelper /// private const string _settingsFile = "WindowsSettings.json"; + private static readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + }; + /// /// Read all possible Windows settings. /// @@ -42,7 +46,7 @@ internal static WindowsSettings ReadAllPossibleSettings() throw new ArgumentNullException(nameof(stream), "stream is null"); } - var options = new JsonSerializerOptions(); + var options = _serializerOptions; options.Converters.Add(new JsonStringEnumConverter()); using var reader = new StreamReader(stream); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj index b463d5d77efd..bf0bde34155b 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj @@ -42,6 +42,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Main.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Main.cs index dce70b499653..1abe65c33614 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Main.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Main.cs @@ -23,7 +23,7 @@ public class Main : IPlugin, IContextMenu, IPluginI18n, ISettingProvider private const string OpenNewTab = nameof(OpenNewTab); private const string OpenQuake = nameof(OpenQuake); private const string ShowHiddenProfiles = nameof(ShowHiddenProfiles); - private readonly ITerminalQuery _terminalQuery = new TerminalQuery(); + private readonly TerminalQuery _terminalQuery = new TerminalQuery(); private PluginInitContext _context; private bool _openNewTab; private bool _openQuake; @@ -199,7 +199,7 @@ public void UpdateSettings(PowerLauncherPluginSettings settings) _showHiddenProfiles = showHiddenProfiles; } - private ImageSource GetLogo(TerminalPackage terminal) + private BitmapImage GetLogo(TerminalPackage terminal) { var aumid = terminal.AppUserModelId; diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj index 3728ab8e3068..7bd545a21ee3 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj @@ -33,6 +33,17 @@ + + + + runtime + + + + runtime + + + PreserveNewest diff --git a/src/modules/launcher/PowerLauncher/Converters/HighlightTextConverter.cs b/src/modules/launcher/PowerLauncher/Converters/HighlightTextConverter.cs index 6e5a213bf7f4..f78b8534e923 100644 --- a/src/modules/launcher/PowerLauncher/Converters/HighlightTextConverter.cs +++ b/src/modules/launcher/PowerLauncher/Converters/HighlightTextConverter.cs @@ -20,7 +20,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur var highlightData = values[1] as List; var selected = values[2] as bool? == true; - if (highlightData == null || !highlightData.Any()) + if (highlightData == null || highlightData.Count == 0) { // No highlight data, just return the text return new Run(text); diff --git a/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs b/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs index c6dd2c33adc2..5e6ff90c9565 100644 --- a/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs +++ b/src/modules/launcher/PowerLauncher/Helper/NativeMethods.cs @@ -69,6 +69,9 @@ internal static class NativeMethods [DllImport("user32.DLL", CharSet = CharSet.Unicode)] internal static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + [DllImport("shell32.dll")] + public static extern int SHQueryUserNotificationState(out UserNotificationState state); + public static string[] CommandLineToArgvW(string cmdLine) { IntPtr argv = IntPtr.Zero; @@ -242,4 +245,15 @@ internal enum WM TRAYMOUSEMESSAGE = 0x800, // WM_USER + 1024 APP = 0x8000, } + + internal enum UserNotificationState : int + { + QUNS_NOT_PRESENT = 1, + QUNS_BUSY, + QUNS_RUNNING_D3D_FULL_SCREEN, + QUNS_PRESENTATION_MODE, + QUNS_ACCEPTS_NOTIFICATIONS, + QUNS_QUIET_TIME, + QUNS_APP, + } } diff --git a/src/modules/launcher/PowerLauncher/Helper/SingleInstance`1.cs b/src/modules/launcher/PowerLauncher/Helper/SingleInstance`1.cs index 9f1ebd3b24eb..b42ad45fdf39 100644 --- a/src/modules/launcher/PowerLauncher/Helper/SingleInstance`1.cs +++ b/src/modules/launcher/PowerLauncher/Helper/SingleInstance`1.cs @@ -92,7 +92,7 @@ internal static void Cleanup() /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. /// /// List of command line arg strings. - private static IList GetCommandLineArgs(string uniqueApplicationName) + private static List GetCommandLineArgs(string uniqueApplicationName) { string[] args = null; @@ -116,7 +116,7 @@ private static IList GetCommandLineArgs(string uniqueApplicationName) { try { - using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) + using (StreamReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) { args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); } diff --git a/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs b/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs index 4c0819f20321..612476daff59 100644 --- a/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs +++ b/src/modules/launcher/PowerLauncher/Helper/WindowsInteropHelper.cs @@ -112,6 +112,15 @@ internal enum INPUTTYPE : uint public static bool IsWindowFullscreen() { + // First, check to see if a game is fullscreen, if so, we definitely have + // a full-screen window + UserNotificationState state; + if (Marshal.GetExceptionForHR(NativeMethods.SHQueryUserNotificationState(out state)) == null && + state == UserNotificationState.QUNS_RUNNING_D3D_FULL_SCREEN) + { + return true; + } + // get current active window IntPtr hWnd = NativeMethods.GetForegroundWindow(); diff --git a/src/modules/launcher/PowerLauncher/LauncherControl.xaml b/src/modules/launcher/PowerLauncher/LauncherControl.xaml index 661905fb86f1..365127c0de21 100644 --- a/src/modules/launcher/PowerLauncher/LauncherControl.xaml +++ b/src/modules/launcher/PowerLauncher/LauncherControl.xaml @@ -33,7 +33,7 @@ diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/KeyVisual/KeyVisual.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/KeyVisual/KeyVisual.xaml index 469a8be795ea..00192a215add 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/KeyVisual/KeyVisual.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/KeyVisual/KeyVisual.xaml @@ -1,11 +1,11 @@  + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"> 16 12 - - - - - + + + - + - + @@ -70,7 +70,7 @@ + ItemsSource="{x:Bind Shortcut, Mode=TwoWay}"> @@ -78,7 +78,7 @@ - - + @@ -132,7 +132,7 @@ - - - - + @@ -206,7 +206,7 @@ - - - - @@ -394,7 +394,7 @@ - diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml index b8030fa9f530..7d61d944da6c 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/EnvironmentVariablesPage.xaml @@ -3,44 +3,40 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" - xmlns:controls1="using:CommunityToolkit.WinUI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="using:CommunityToolkit.WinUI.UI" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:ui="using:CommunityToolkit.WinUI" mc:Ignorable="d"> - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - + - - - + HeaderIcon="{ui:FontIcon Glyph=}" + IsEnabled="{x:Bind ViewModel.LaunchAdministratorEnabled, Mode=OneWay}"> + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml index ce02fd168cff..7c7cf18e0c22 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/FancyZonesPage.xaml @@ -2,194 +2,193 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.FancyZonesPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - - + + - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - + - - - + + + - - + + - - + + - - - - - - - - - - - - - - - - - - - + IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}"> + + + + + + + + + + + + + + + + + + - - - + + + - - + - - - + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - + + + + + + + + + + - - - + + + + SelectedIndex="{x:Bind ViewModel.MoveWindowsBasedOnPosition, Mode=TwoWay, Converter={StaticResource BoolToComboBoxIndexConverter}}"> - + @@ -197,40 +196,40 @@ - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - + + - - - - + + + + - + - - - - + + + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml index 4eda4d9625a3..41a1921809fe 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/FileLocksmithPage.xaml @@ -2,50 +2,49 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.FileLocksmithPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:converters="using:CommunityToolkit.WinUI.Converters" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - - + + - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - - + + + - + - + - - - - - + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml index dd21099ba43a..2a5fb163857b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml @@ -2,30 +2,30 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.GeneralPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:localConverters="using:Microsoft.PowerToys.Settings.UI.Converters" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - - + + - - + + - - - + + + - + - - + + + Visibility="{x:Bind ViewModel.IsNewVersionDownloading, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}"> - + @@ -85,7 +85,7 @@ - - + @@ -290,7 +293,7 @@ Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}" TextWrapping="WrapWholeWords"> - + @@ -301,7 +304,7 @@ Grid.Row="1" Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}" - Text="{Binding LastSettingsBackupFileName, Mode=OneWay}" + Text="{x:Bind ViewModel.LastSettingsBackupFileName, Mode=OneWay}" TextWrapping="WrapWholeWords" /> @@ -309,53 +312,53 @@ Grid.Row="2" Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}" - Text="{Binding LastSettingsBackupSource, Mode=OneWay}" + Text="{x:Bind ViewModel.LastSettingsBackupSource, Mode=OneWay}" TextWrapping="WrapWholeWords" /> - - - - + + + + - - - + IsOpen="{x:Bind ViewModel.SettingsBackupRestoreMessageVisible, Mode=OneWay}" + IsTabStop="{x:Bind ViewModel.SettingsBackupRestoreMessageVisible, Mode=OneWay}" + Severity="{x:Bind ViewModel.BackupRestoreMessageSeverity, Converter={StaticResource StringToInfoBarSeverityConverter}}" /> + + + - - - + + + - + - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml index ab083d3caaa9..f99c21dd4625 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/HostsPage.xaml @@ -2,69 +2,68 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.HostsPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" mc:Ignorable="d"> - - + + - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - + - - - - - - - + IsEnabled="{x:Bind ViewModel.LaunchAdministratorEnabled, Mode=OneWay}"> + + + + + + - - + + - - - - - + + + + + - - + + - + - - - - + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml index 775f7da4cc31..ad4a3b868bd8 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml @@ -2,12 +2,12 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.ImageResizerPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:Microsoft.PowerToys.Settings.UI.Library" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:toolkitconverters="using:CommunityToolkit.WinUI.UI.Converters" xmlns:ui="using:CommunityToolkit.WinUI" x:Name="RootPage" @@ -21,30 +21,30 @@ FalseValue="1" TrueValue="0" /> - - + + - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - + + - + - + - - - + + + @@ -188,26 +188,26 @@ - + - + - + Value="{x:Bind ViewModel.JPEGQualityLevel, Mode=TwoWay}" /> + - - + + - + - - + + @@ -216,16 +216,16 @@ - - + + - - + + + Text="{x:Bind ViewModel.FileName, Mode=TwoWay}" /> - + - - + + - - + + - + - - - - - - - + + + + + + + \ No newline at end of file diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml index f4ef3b5ece3e..f4ac4d744843 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml @@ -3,27 +3,27 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Lib="using:Microsoft.PowerToys.Settings.UI.Library" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:converters="using:CommunityToolkit.WinUI.Converters" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - + - + - + - - + + - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - + + - - + @@ -84,7 +83,7 @@ Visibility="{x:Bind Path=ViewModel.RemapKeys, Mode=OneWay, Converter={StaticResource CollectionVisibilityConverter}}"> - + - - + - + - - + @@ -138,7 +136,7 @@ Visibility="{x:Bind Path=ViewModel.RemapShortcuts, Mode=OneWay, Converter={StaticResource CollectionVisibilityConverter}}"> - + - - + - + - - - - - + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml index 80a4ce4aeb7c..65ab8bc363a0 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MeasureToolPage.xaml @@ -2,34 +2,34 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.MeasureToolPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - - + + - + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - - - - + + + + + @@ -37,56 +37,56 @@ - + - + - - + + - + - + - + - + - + Value="{x:Bind ViewModel.PixelTolerance, Mode=TwoWay}" /> + - + --> - + - + - - - - + + + + - - - - - - - - + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml index 96c58488898e..39aa3e6e95dd 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml @@ -2,39 +2,39 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.MouseUtilsPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" - xmlns:converters="using:CommunityToolkit.WinUI.Converters" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" + xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:localConverters="using:Microsoft.PowerToys.Settings.UI.Converters" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> - - + - - + + - - + + IsEnabled="{x:Bind ViewModel.IsFindMyMouseEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - - + + - - + + + + + + + + - - - + HeaderIcon="{ui:FontIcon Glyph=}" + Visibility="{x:Bind ViewModel.FindMyMouseActivationMethod, Converter={StaticResource FindMyMouseActivationIntToVisibilityConverter}, Mode=OneWay, ConverterParameter=3}"> + + + - - - + + + - - - + + - - - - - - - - + Value="{x:Bind ViewModel.FindMyMouseOverlayOpacity, Mode=TwoWay}" /> + + + + + + + + - - + Value="{x:Bind ViewModel.FindMyMouseSpotlightRadius, Mode=TwoWay}" /> + + - - + Value="{x:Bind ViewModel.FindMyMouseSpotlightInitialZoom, Mode=TwoWay}" /> + + - - - + Value="{x:Bind ViewModel.FindMyMouseAnimationDurationMs, Mode=TwoWay}" /> + + + - + - - + + - - - - + + + + - - + + IsEnabled="{x:Bind ViewModel.IsHighlighterEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - - - + + + - - - - + + + - - - - - - - - - - - + + + + + + + + + + + - - + Value="{x:Bind ViewModel.MouseHighlighterRadius, Mode=TwoWay}" /> + + - - + Value="{x:Bind ViewModel.MouseHighlighterFadeDelayMs, Mode=TwoWay}" /> + + - - - - + Value="{x:Bind ViewModel.MouseHighlighterFadeDurationMs, Mode=TwoWay}" /> + + + + - - + + IsEnabled="{x:Bind ViewModel.IsJumpEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - - - + + - + - + - - + + - - + + IsEnabled="{x:Bind ViewModel.IsMousePointerCrosshairsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + - - - - + + + - - - + + + - - - - - - + + + + + - + Value="{x:Bind ViewModel.MousePointerCrosshairsOpacity, Mode=TwoWay}" /> + - + - + Value="{x:Bind ViewModel.MousePointerCrosshairsRadius, Mode=TwoWay}" /> + - + - + Value="{x:Bind ViewModel.MousePointerCrosshairsThickness, Mode=TwoWay}" /> + - - - + + + - + - + Value="{x:Bind ViewModel.MousePointerCrosshairsBorderSize, Mode=TwoWay}" /> + - + - + - + - + - + - - - - + Value="{x:Bind ViewModel.MousePointerCrosshairsFixedLength, Mode=TwoWay}" /> + + + + - - - - - - - - - + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml index 74678b3d3826..493a94f0d071 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml @@ -2,11 +2,11 @@ x:Class="Microsoft.PowerToys.Settings.UI.Views.MouseWithoutBordersPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:CommunityToolkit.WinUI.Controls" + xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:converters="using:CommunityToolkit.WinUI.UI.Converters" - xmlns:custom="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" xmlns:ui="using:CommunityToolkit.WinUI" AutomationProperties.LandmarkType="Main" mc:Ignorable="d"> @@ -17,34 +17,34 @@ FalseValue="2" TrueValue="4" /> - - + + - - + + IsEnabled="{x:Bind ViewModel.IsEnabledGpoConfigured, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"> - + IsEnabled="{x:Bind ViewModel.CanBeEnabled, Mode=OneWay}" + IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" /> + - - - + + - - + IsExpanded="{x:Bind ViewModel.ConnectFieldsVisible, Mode=TwoWay}"> + + - - - - + + + @@ -123,11 +123,11 @@ Width="136" Height="90" Margin="4" - AllowDrop="{Binding Mode=OneWay, Path=Item.CanDragDrop}" + AllowDrop="{Binding Item.CanDragDrop, Mode=OneWay}" Background="{ThemeResource SolidBackgroundFillColorBaseAltBrush}" BorderBrush="{Binding Item.StatusBrush}" BorderThickness="2" - CanDrag="{Binding Mode=OneWay, Path=Item.CanDragDrop}" + CanDrag="{Binding Item.CanDragDrop, Mode=OneWay}" CornerRadius="4" DataContext="{Binding}" DragOver="Device_DragOver" @@ -157,7 +157,7 @@ -