mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-04 19:36:57 +01:00
Compare commits
41 Commits
shawn/mcps
...
feature/lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16cce864a5 | ||
|
|
b72d8bb46e | ||
|
|
25e1a5414b | ||
|
|
dc94e28212 | ||
|
|
a4d4a9a3d9 | ||
|
|
6ceb908d86 | ||
|
|
aef46481d9 | ||
|
|
08a3ae2dee | ||
|
|
d07f40eec3 | ||
|
|
4dab8e1eaa | ||
|
|
d0a67823e1 | ||
|
|
6b05db4de0 | ||
|
|
3f87a0c408 | ||
|
|
314a6e73eb | ||
|
|
55251607a7 | ||
|
|
13da00640f | ||
|
|
7aa02561c2 | ||
|
|
5cbebad63f | ||
|
|
91fcebdca8 | ||
|
|
f4d4c9aabe | ||
|
|
db590d6c04 | ||
|
|
6ec8ab700b | ||
|
|
830a32fc1f | ||
|
|
0edf06bb5f | ||
|
|
f995e414b7 | ||
|
|
aae601aa36 | ||
|
|
51caa5b50b | ||
|
|
8edaa44cee | ||
|
|
879e03b436 | ||
|
|
bb706fb5f1 | ||
|
|
2ce76b861f | ||
|
|
e88b4aa1a2 | ||
|
|
0cb7cc6df2 | ||
|
|
76fb464832 | ||
|
|
818db17838 | ||
|
|
a575cd00e0 | ||
|
|
5747e5e537 | ||
|
|
2a98211240 | ||
|
|
48ca0cc2d1 | ||
|
|
d60106539f | ||
|
|
d8de2e5c1c |
7
.github/actions/spell-check/expect.txt
vendored
7
.github/actions/spell-check/expect.txt
vendored
@@ -27,6 +27,7 @@ admx
|
||||
advancedpaste
|
||||
advancedpasteui
|
||||
advancedpasteuishortcut
|
||||
advapi
|
||||
advfirewall
|
||||
AFeature
|
||||
affordances
|
||||
@@ -481,6 +482,7 @@ examplehandler
|
||||
examplepowertoy
|
||||
EXAND
|
||||
EXCLUDEFROMCAPTURE
|
||||
EXECUTEDEFAULT
|
||||
executionpolicy
|
||||
exename
|
||||
exf
|
||||
@@ -917,6 +919,7 @@ LWA
|
||||
lwin
|
||||
LZero
|
||||
MAGTRANSFORM
|
||||
MAJORMINOR
|
||||
MAKEINTRESOURCE
|
||||
MAKEINTRESOURCEA
|
||||
MAKEINTRESOURCEW
|
||||
@@ -995,6 +998,9 @@ mousepointercrosshairs
|
||||
mouseutils
|
||||
MOVESIZEEND
|
||||
MOVESIZESTART
|
||||
muxx
|
||||
muxxc
|
||||
muxxh
|
||||
MRM
|
||||
MRT
|
||||
mru
|
||||
@@ -1806,6 +1812,7 @@ ULONGLONG
|
||||
ums
|
||||
uncompilable
|
||||
UNCPRIORITY
|
||||
undefining
|
||||
UNDNAME
|
||||
UNICODETEXT
|
||||
unins
|
||||
|
||||
19
.github/workflows/automatic-issue-deduplication.yml
vendored
Normal file
19
.github/workflows/automatic-issue-deduplication.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Automatic New Issue Deduplication
|
||||
on:
|
||||
issues:
|
||||
types: [opened, reopened]
|
||||
permissions:
|
||||
models: read
|
||||
issues: write
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.issue.number }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
deduplicate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run Deduplicate Action
|
||||
uses: pelikhan/action-genai-issue-dedup@v0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
label_as_duplicate: true
|
||||
38
.pipelines/v2/ci-nightly.yml
Normal file
38
.pipelines/v2/ci-nightly.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
# .pipelines/v2/nightly-prewarm.yml
|
||||
# Nightly pre-warm that reuses your existing ci.yml as-is
|
||||
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
# (18:00 UTC) — adjust as you like
|
||||
schedules:
|
||||
- cron: "0 18 * * *" # UTC
|
||||
displayName: Nightly pre-warm (main)
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
always: true
|
||||
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
|
||||
parameters:
|
||||
- name: buildPlatforms
|
||||
type: object
|
||||
default:
|
||||
- x64
|
||||
- arm64
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
displayName: "Enable MSBuild Caching"
|
||||
default: true
|
||||
- name: msBuildCacheIsReadOnly
|
||||
type: boolean
|
||||
displayName: "MSBuild Cache Read Only"
|
||||
default: false
|
||||
|
||||
extends:
|
||||
template: templates/pipeline-ci-build.yml
|
||||
parameters:
|
||||
buildPlatforms: ${{ parameters.buildPlatforms }}
|
||||
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
|
||||
msBuildCacheIsReadOnly: ${{ parameters.msBuildCacheIsReadOnly }}
|
||||
@@ -32,7 +32,7 @@ parameters:
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
displayName: "Enable MSBuild Caching"
|
||||
default: true
|
||||
default: false
|
||||
- name: runTests
|
||||
type: boolean
|
||||
displayName: "Run Tests"
|
||||
|
||||
@@ -50,6 +50,9 @@ parameters:
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
default: false
|
||||
- name: msBuildCacheIsReadOnly
|
||||
type: boolean
|
||||
default: true
|
||||
- name: runTests
|
||||
type: boolean
|
||||
default: true
|
||||
@@ -154,6 +157,11 @@ jobs:
|
||||
$MSBuildCacheParameters += " -reportfileaccesses"
|
||||
$MSBuildCacheParameters += " -p:MSBuildCacheEnabled=true"
|
||||
$MSBuildCacheParameters += " -p:MSBuildCacheLogDirectory=$(LogOutputDirectory)\MSBuildCacheLogs"
|
||||
# Cache read-only policy controlled by parameter
|
||||
$cacheIsReadOnly = "${{ parameters.msBuildCacheIsReadOnly }}"
|
||||
if ($cacheIsReadOnly -eq "True") {
|
||||
$MSBuildCacheParameters += " /p:MSBuildCacheRemoteCacheIsReadOnly=true"
|
||||
}
|
||||
Write-Host "MSBuildCacheParameters: $MSBuildCacheParameters"
|
||||
Write-Host "##vso[task.setvariable variable=MSBuildCacheParameters]$MSBuildCacheParameters"
|
||||
displayName: Prepare MSBuildCache variables
|
||||
@@ -411,9 +419,28 @@ jobs:
|
||||
!**\obj\**
|
||||
|
||||
- pwsh: |-
|
||||
$Package = (Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix" | Select -First 1)
|
||||
$PackageFilename = $Package.FullName
|
||||
Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}"
|
||||
$Packages = Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix"
|
||||
Write-Host "Found $($Packages.Count) CmdPal MSIX package(s):"
|
||||
foreach ($pkg in $Packages) {
|
||||
Write-Host " - $($pkg.FullName)"
|
||||
}
|
||||
|
||||
if ($Packages.Count -gt 0) {
|
||||
# Priority: Look for platform-specific MSIX (x64/arm64) first, then fall back to any
|
||||
$PlatformPackage = $Packages | Where-Object { $_.Name -match "Microsoft\.CmdPal\.UI_.*_(x64|arm64)\.msix$" } | Select-Object -First 1
|
||||
if ($PlatformPackage) {
|
||||
$Package = $PlatformPackage
|
||||
Write-Host "Using platform-specific package: $($Package.FullName)"
|
||||
} else {
|
||||
$Package = $Packages | Select-Object -First 1
|
||||
Write-Host "Using first available package: $($Package.FullName)"
|
||||
}
|
||||
|
||||
$PackageFilename = $Package.FullName
|
||||
Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}"
|
||||
} else {
|
||||
Write-Warning "No CmdPal MSIX packages found!"
|
||||
}
|
||||
displayName: Locate the CmdPal MSIX
|
||||
|
||||
- ${{ if eq(parameters.codeSign, true) }}:
|
||||
|
||||
@@ -13,6 +13,9 @@ parameters:
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
default: false
|
||||
- name: msBuildCacheIsReadOnly
|
||||
type: boolean
|
||||
default: true
|
||||
- name: runTests
|
||||
type: boolean
|
||||
default: true
|
||||
@@ -52,6 +55,7 @@ stages:
|
||||
buildConfigurations: [Release]
|
||||
enablePackageCaching: true
|
||||
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
|
||||
msBuildCacheIsReadOnly: ${{ parameters.msBuildCacheIsReadOnly }}
|
||||
runTests: ${{ parameters.runTests }}
|
||||
useVSPreview: ${{ parameters.useVSPreview }}
|
||||
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
|
||||
|
||||
@@ -132,6 +132,39 @@ steps:
|
||||
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
|
||||
|
||||
#### END MSI
|
||||
|
||||
#### BUILDING AND SIGNING SilentFilesInUseBAFunction DLL
|
||||
- task: VSBuild@1
|
||||
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build SilentFilesInUseBAFunction
|
||||
inputs:
|
||||
solution: "**/installer/PowerToysSetup.sln"
|
||||
vsVersion: 17.0
|
||||
msbuildArgs: >-
|
||||
/t:SilentFilesInUseBAFunction
|
||||
/p:RunBuildEvents=true;PerUser=${{parameters.buildUserInstaller}};RestorePackagesConfig=true;CIBuild=true
|
||||
/p:InstallerSuffix=${{ parameters.installerSuffix }}
|
||||
-restore -graph
|
||||
/bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-SilentFilesInUseBAFunction.binlog
|
||||
${{ parameters.additionalBuildOptions }}
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: false # don't undo our hard work above by deleting the msi
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
|
||||
- ${{ if eq(parameters.codeSign, true) }}:
|
||||
- template: steps-esrp-signing.yml
|
||||
parameters:
|
||||
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Sign SilentFilesInUseBAFunction
|
||||
signingIdentity: ${{ parameters.signingIdentity }}
|
||||
inputs:
|
||||
FolderPath: 'installer/$(BuildPlatform)/$(BuildConfiguration)'
|
||||
signType: batchSigning
|
||||
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
|
||||
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
|
||||
|
||||
#### END BUILDING AND SIGNING SilentFilesInUseBAFunction DLL
|
||||
|
||||
#### BOOTSTRAP BUILDING AND SIGNING
|
||||
- task: VSBuild@1
|
||||
displayName: ${{replace(replace(parameters.buildUserInstaller,'True','👤'),'False','💻')}} Build VNext Bootstrapper
|
||||
@@ -148,7 +181,7 @@ steps:
|
||||
${{ parameters.additionalBuildOptions }}
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
clean: false # don't undo our hard work above by deleting the MSI
|
||||
clean: false # don't undo our hard work above by deleting the MSI nor SilentFilesInUseBAFunction
|
||||
msbuildArchitecture: x64
|
||||
maximumCpuCount: true
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
<_PropertySheetDisplayName>PowerToys.Root.Props</_PropertySheetDisplayName>
|
||||
<ForceImportBeforeCppProps>$(MsbuildThisFileDirectory)\Cpp.Build.props</ForceImportBeforeCppProps>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(MSBuildProjectExtension)' == '.csproj'">
|
||||
<PackageReference Include="StyleCop.Analyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -3,4 +3,9 @@
|
||||
|
||||
<Import Project="$(MSBuildCachePackageRoot)\build\$(MSBuildCachePackageName).targets" Condition="'$(MSBuildCacheEnabled)' == 'true'" />
|
||||
<Import Project="$(MSBuildCacheSharedCompilationPackageRoot)\build\Microsoft.MSBuildCache.SharedCompilation.targets" Condition="'$(MSBuildCacheEnabled)' == 'true'" />
|
||||
|
||||
<!-- Override ManifestTool to the x64 host tool under WindowsSdkDir for all projects once the SDK path is known. -->
|
||||
<PropertyGroup Label="ManifestToolOverride">
|
||||
<ManifestTool Condition="Exists('$(WindowsSdkDir)bin\x64\mt.exe')">$(WindowsSdkDir)bin\x64\mt.exe</ManifestTool>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AdaptiveCards.ObjectModel.WinUI3" Version="2.0.0-beta" />
|
||||
@@ -8,7 +9,7 @@
|
||||
<PackageVersion Include="AdaptiveCards.Templating" Version="2.0.5" />
|
||||
<PackageVersion Include="Microsoft.Bot.AdaptiveExpressions.Core" Version="4.23.0" />
|
||||
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
|
||||
<PackageVersion Include="Azure.AI.OpenAI" Version="2.2.0-beta.4" />
|
||||
<PackageVersion Include="CoenM.ImageSharp.ImageHash" Version="1.3.6" />
|
||||
<PackageVersion Include="CommunityToolkit.Common" Version="8.4.0" />
|
||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
@@ -21,11 +22,11 @@
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.WinUI.UI.Controls.Markdown" Version="7.1.2" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250703-build.2173" />
|
||||
<PackageVersion Include="CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock" Version="0.1.250910-build.2249" />
|
||||
<PackageVersion Include="ControlzEx" Version="6.0.0" />
|
||||
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
|
||||
<PackageVersion Include="HelixToolkit.Core.Wpf" Version="2.24.0" />
|
||||
<PackageVersion Include="HtmlAgilityPack" Version="1.12.3" />
|
||||
<PackageVersion Include="hyjiacan.pinyin4net" Version="4.1.1" />
|
||||
<PackageVersion Include="Interop.Microsoft.Office.Interop.OneNote" Version="1.1.0.2" />
|
||||
<PackageVersion Include="LazyCache" Version="2.4.0" />
|
||||
@@ -37,15 +38,16 @@
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.8" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.Windows.CppWinRT" Version="2.0.240111.5" />
|
||||
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.46.0" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.8" />
|
||||
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
|
||||
@@ -57,8 +59,8 @@
|
||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4948" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||
@@ -69,7 +71,7 @@
|
||||
<PackageVersion Include="NLog" Version="5.2.8" />
|
||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
|
||||
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
|
||||
<PackageVersion Include="OpenAI" Version="2.0.0" />
|
||||
<PackageVersion Include="OpenAI" Version="2.2.0-beta.4" />
|
||||
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
|
||||
<PackageVersion Include="RtfPipe" Version="2.0.7677.4303" />
|
||||
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
|
||||
|
||||
@@ -1509,7 +1509,6 @@ SOFTWARE.
|
||||
- CommunityToolkit.WinUI.Converters
|
||||
- CommunityToolkit.WinUI.Extensions
|
||||
- CommunityToolkit.WinUI.UI.Controls.DataGrid
|
||||
- CommunityToolkit.WinUI.UI.Controls.Markdown
|
||||
- ControlzEx
|
||||
- HelixToolkit
|
||||
- HelixToolkit.Core.Wpf
|
||||
|
||||
@@ -2932,7 +2932,6 @@ Global
|
||||
{D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
|
||||
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9} = {264B412F-DB8B-4CF8-A74B-96998B183045}
|
||||
{1AFB6476-670D-4E80-A464-657E01DFF482} = {557C4636-D7E1-4838-A504-7D19B725EE95}
|
||||
{1A066C63-64B3-45F8-92FE-664E1CCE8077} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
|
||||
{89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
<Fragment>
|
||||
<DirectoryRef Id="WinUI3AppsInstallFolder">
|
||||
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
|
||||
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
|
||||
<?else ?>
|
||||
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
|
||||
<?endif ?>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
|
||||
@@ -33,41 +26,14 @@
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
|
||||
<Component Id="Module_CmdPal_Deps" Win64="yes" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?else ?>
|
||||
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
|
||||
<Component Id="Module_CmdPal_Deps" Win64="yes" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?endif ?>
|
||||
|
||||
<ComponentGroup Id="CmdPalComponentGroup">
|
||||
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall"/>
|
||||
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall"/>
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall"/>
|
||||
<?else ?>
|
||||
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall"/>
|
||||
<?endif ?>
|
||||
</Component>
|
||||
<ComponentRef Id="Module_CmdPal" />
|
||||
<ComponentRef Id="Module_CmdPal_Deps" />
|
||||
</ComponentGroup>
|
||||
|
||||
</Fragment>
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
<Fragment>
|
||||
<DirectoryRef Id="WinUI3AppsInstallFolder">
|
||||
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
|
||||
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
|
||||
<?else?>
|
||||
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
|
||||
<?endif?>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="CmdPalInstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test">
|
||||
@@ -25,40 +18,14 @@
|
||||
<?endif?>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
|
||||
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Id="Microsoft.VCLibs.x64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?else?>
|
||||
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
|
||||
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Id="Microsoft.VCLibs.ARM64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?endif?>
|
||||
<ComponentGroup Id="CmdPalComponentGroup">
|
||||
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall" />
|
||||
<?if $(sys.BUILDARCH) = x64 ?>
|
||||
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall" />
|
||||
<?else?>
|
||||
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall" />
|
||||
<?endif?>
|
||||
</Component>
|
||||
<ComponentRef Id="Module_CmdPal" />
|
||||
<ComponentRef Id="Module_CmdPal_Deps" />
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<TargetName>SilentFilesInUseBAFunction</TargetName>
|
||||
<ProjectName>PowerToysSetupCustomActionsVNext</ProjectName>
|
||||
<ProjectModuleDefinitionFile>bafunctions.def</ProjectModuleDefinitionFile>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
@@ -91,5 +92,31 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<!-- C++ source compile-specific things for Debug/Release configurations -->
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
</configuration>
|
||||
@@ -19,7 +19,9 @@ namespace ManagedCommon
|
||||
private static readonly string Error = "Error";
|
||||
private static readonly string Warning = "Warning";
|
||||
private static readonly string Info = "Info";
|
||||
#if DEBUG
|
||||
private static readonly string Debug = "Debug";
|
||||
#endif
|
||||
private static readonly string TraceFlag = "Trace";
|
||||
|
||||
private static readonly string Version = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version ?? "Unknown";
|
||||
@@ -151,7 +153,9 @@ namespace ManagedCommon
|
||||
|
||||
public static void LogDebug(string message, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
{
|
||||
#if DEBUG
|
||||
Log(message, Debug, memberName, sourceFilePath, sourceLineNumber);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void LogTrace([System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
<PackageReference Include="Appium.WebDriver" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
<PackageReference Include="CoenM.ImageSharp.ImageHash" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -14,9 +14,11 @@ using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Services.OpenAI;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.UnitTests.Mocks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace AdvancedPaste.UnitTests.ServicesTests;
|
||||
@@ -130,10 +132,11 @@ public sealed class AIServiceBatchIntegrationTests
|
||||
|
||||
private static async Task<DataPackage> GetOutputDataPackageAsync(BatchTestInput batchTestInput, PasteFormats format)
|
||||
{
|
||||
Mock<IUserSettings> userSettings = new();
|
||||
VaultCredentialsProvider credentialsProvider = new();
|
||||
PromptModerationService promptModerationService = new(credentialsProvider);
|
||||
PromptModerationService promptModerationService = new(userSettings.Object, credentialsProvider);
|
||||
NoOpProgress progress = new();
|
||||
CustomTextTransformService customTextTransformService = new(credentialsProvider, promptModerationService);
|
||||
CustomTextTransformService customTextTransformService = new(userSettings.Object, credentialsProvider, promptModerationService);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
@@ -142,7 +145,7 @@ public sealed class AIServiceBatchIntegrationTests
|
||||
|
||||
case PasteFormats.KernelQuery:
|
||||
var clipboardData = DataPackageHelpers.CreateFromText(batchTestInput.Clipboard).GetView();
|
||||
KernelService kernelService = new(new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, customTextTransformService);
|
||||
KernelService kernelService = new(userSettings.Object, new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, customTextTransformService);
|
||||
return await kernelService.TransformClipboardAsync(batchTestInput.Prompt, clipboardData, isSavedQuery: false, CancellationToken.None, progress);
|
||||
|
||||
default:
|
||||
|
||||
@@ -12,10 +12,12 @@ using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Services.OpenAI;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.Telemetry;
|
||||
using AdvancedPaste.UnitTests.Mocks;
|
||||
using AdvancedPaste.UnitTests.Utils;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace AdvancedPaste.UnitTests.ServicesTests;
|
||||
@@ -29,14 +31,17 @@ public sealed class KernelServiceIntegrationTests : IDisposable
|
||||
private const string StandardImageFile = "image_with_text_example.png";
|
||||
private KernelService _kernelService;
|
||||
private AdvancedPasteEventListener _eventListener;
|
||||
private Mock<IUserSettings> _userSettings;
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
_userSettings = new();
|
||||
VaultCredentialsProvider credentialsProvider = new();
|
||||
PromptModerationService promptModerationService = new(credentialsProvider);
|
||||
PromptModerationService promptModerationService = new(_userSettings.Object, credentialsProvider);
|
||||
CustomTextTransformService customTextTransformService = new(_userSettings.Object, credentialsProvider, promptModerationService);
|
||||
|
||||
_kernelService = new KernelService(new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, new CustomTextTransformService(credentialsProvider, promptModerationService));
|
||||
_kernelService = new KernelService(_userSettings.Object, new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, customTextTransformService);
|
||||
_eventListener = new();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,156 +12,6 @@
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Other app resources here -->
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SystemColorHighlightTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SystemColorGrayTextColor" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Style x:Key="SubtleButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AnimatedIcon.State="Normal"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -153,49 +153,60 @@
|
||||
x:FieldModifier="public"
|
||||
TabIndex="0">
|
||||
<controls:PromptBox.Footer>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="AIMistakeNote" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="4,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Hyperlink
|
||||
x:Name="TermsHyperlink"
|
||||
NavigateUri="https://openai.com/policies/terms-of-use"
|
||||
TabIndex="3">
|
||||
<Run x:Uid="TermsLink" />
|
||||
</Hyperlink>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="https://openai.com/policies/terms-of-use" />
|
||||
</ToolTipService.ToolTip>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
ToolTipService.ToolTip="">
|
||||
<Run x:Uid="AIFooterSeparator" Foreground="{ThemeResource TextFillColorSecondaryBrush}">|</Run>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Hyperlink NavigateUri="https://openai.com/policies/privacy-policy" TabIndex="3">
|
||||
<Run x:Uid="PrivacyLink" />
|
||||
</Hyperlink>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="https://openai.com/policies/privacy-policy" />
|
||||
</ToolTipService.ToolTip>
|
||||
</TextBlock>
|
||||
<StackPanel Orientation="Vertical" Spacing="2">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="AIMistakeNote" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="4,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Hyperlink
|
||||
x:Name="TermsHyperlink"
|
||||
NavigateUri="https://openai.com/policies/terms-of-use"
|
||||
TabIndex="3">
|
||||
<Run x:Uid="TermsLink" />
|
||||
</Hyperlink>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="https://openai.com/policies/terms-of-use" />
|
||||
</ToolTipService.ToolTip>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
ToolTipService.ToolTip="">
|
||||
<Run x:Uid="AIFooterSeparator" Foreground="{ThemeResource TextFillColorSecondaryBrush}">|</Run>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Hyperlink NavigateUri="https://openai.com/policies/privacy-policy" TabIndex="3">
|
||||
<Run x:Uid="PrivacyLink" />
|
||||
</Hyperlink>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="https://openai.com/policies/privacy-policy" />
|
||||
</ToolTipService.ToolTip>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Visibility="{x:Bind ViewModel.IsLocalModelMode, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
Margin="0,0,2,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}">
|
||||
<Run x:Uid="CustomAIMistakeNote" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</controls:PromptBox.Footer>
|
||||
</controls:PromptBox>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -22,19 +23,24 @@ using Windows.System;
|
||||
|
||||
namespace AdvancedPaste.Pages
|
||||
{
|
||||
public sealed partial class MainPage : Page
|
||||
public sealed partial class MainPage : Page, INotifyPropertyChanged
|
||||
{
|
||||
private readonly ObservableCollection<ClipboardItem> clipboardHistory;
|
||||
private readonly Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
|
||||
private (VirtualKey Key, DateTime Timestamp) _lastKeyEvent = (VirtualKey.None, DateTime.MinValue);
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public OptionsViewModel ViewModel { get; private set; }
|
||||
|
||||
public bool IsLocalModelModeVisible => ViewModel?.IsLocalModelMode == true;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
ViewModel = App.GetService<OptionsViewModel>();
|
||||
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
|
||||
|
||||
clipboardHistory = new ObservableCollection<ClipboardItem>();
|
||||
|
||||
@@ -42,6 +48,19 @@ namespace AdvancedPaste.Pages
|
||||
Clipboard.HistoryChanged += LoadClipboardHistoryEvent;
|
||||
}
|
||||
|
||||
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(OptionsViewModel.IsLocalModelMode))
|
||||
{
|
||||
OnPropertyChanged(nameof(IsLocalModelModeVisible));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void LoadClipboardHistoryEvent(object sender, object e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
|
||||
@@ -14,6 +14,16 @@ namespace AdvancedPaste.Settings
|
||||
{
|
||||
public bool IsAdvancedAIEnabled { get; }
|
||||
|
||||
public AdvancedPasteAIMode AIMode { get; }
|
||||
|
||||
public bool IsLocalModelMode { get; }
|
||||
|
||||
public string CustomEndpoint { get; }
|
||||
|
||||
public string CustomModelName { get; }
|
||||
|
||||
public bool DisableModeration { get; }
|
||||
|
||||
public bool ShowCustomPreview { get; }
|
||||
|
||||
public bool CloseAfterLosingFocus { get; }
|
||||
|
||||
@@ -35,6 +35,16 @@ namespace AdvancedPaste.Settings
|
||||
|
||||
public bool IsAdvancedAIEnabled { get; private set; }
|
||||
|
||||
public AdvancedPasteAIMode AIMode { get; private set; }
|
||||
|
||||
public bool IsLocalModelMode => AIMode == AdvancedPasteAIMode.LocalModel;
|
||||
|
||||
public string CustomEndpoint { get; private set; }
|
||||
|
||||
public string CustomModelName { get; private set; }
|
||||
|
||||
public bool DisableModeration { get; private set; }
|
||||
|
||||
public bool ShowCustomPreview { get; private set; }
|
||||
|
||||
public bool CloseAfterLosingFocus { get; private set; }
|
||||
@@ -48,6 +58,10 @@ namespace AdvancedPaste.Settings
|
||||
_settingsUtils = new SettingsUtils(fileSystem);
|
||||
|
||||
IsAdvancedAIEnabled = false;
|
||||
AIMode = AdvancedPasteAIMode.Disabled;
|
||||
CustomEndpoint = string.Empty;
|
||||
CustomModelName = string.Empty;
|
||||
DisableModeration = false;
|
||||
ShowCustomPreview = true;
|
||||
CloseAfterLosingFocus = false;
|
||||
_additionalActions = [];
|
||||
@@ -99,6 +113,34 @@ namespace AdvancedPaste.Settings
|
||||
var properties = settings.Properties;
|
||||
|
||||
IsAdvancedAIEnabled = properties.IsAdvancedAIEnabled;
|
||||
|
||||
// Handle backwards compatibility for AIMode
|
||||
if (properties.AIMode == AdvancedPasteAIMode.Disabled)
|
||||
{
|
||||
// Check if user has custom endpoint/model configured (local model mode)
|
||||
if (!string.IsNullOrWhiteSpace(properties.CustomEndpoint) || !string.IsNullOrWhiteSpace(properties.CustomModelName))
|
||||
{
|
||||
AIMode = AdvancedPasteAIMode.LocalModel;
|
||||
}
|
||||
|
||||
// Check if user has OpenAI key configured
|
||||
else if (IsOpenAIKeyConfigured())
|
||||
{
|
||||
AIMode = AdvancedPasteAIMode.OpenAI;
|
||||
}
|
||||
else
|
||||
{
|
||||
AIMode = AdvancedPasteAIMode.Disabled;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AIMode = properties.AIMode;
|
||||
}
|
||||
|
||||
CustomEndpoint = properties.CustomEndpoint;
|
||||
CustomModelName = properties.CustomModelName;
|
||||
DisableModeration = properties.DisableModeration;
|
||||
ShowCustomPreview = properties.ShowCustomPreview;
|
||||
CloseAfterLosingFocus = properties.CloseAfterLosingFocus;
|
||||
|
||||
@@ -144,6 +186,20 @@ namespace AdvancedPaste.Settings
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsOpenAIKeyConfigured()
|
||||
{
|
||||
try
|
||||
{
|
||||
var vault = new Windows.Security.Credentials.PasswordVault();
|
||||
vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
@@ -3,54 +3,67 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.Telemetry;
|
||||
using Azure;
|
||||
using Azure.AI.OpenAI;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace AdvancedPaste.Services.OpenAI;
|
||||
|
||||
public sealed class CustomTextTransformService(IAICredentialsProvider aiCredentialsProvider, IPromptModerationService promptModerationService) : ICustomTextTransformService
|
||||
public sealed class CustomTextTransformService(IUserSettings userSettings, IAICredentialsProvider aiCredentialsProvider, IPromptModerationService promptModerationService) : ICustomTextTransformService
|
||||
{
|
||||
private const string ModelName = "gpt-3.5-turbo-instruct";
|
||||
private readonly IUserSettings _userSettings = userSettings;
|
||||
|
||||
private string ModelName => string.IsNullOrWhiteSpace(_userSettings.CustomModelName) ? "gpt-3.5-turbo-instruct" : _userSettings.CustomModelName;
|
||||
|
||||
private readonly IAICredentialsProvider _aiCredentialsProvider = aiCredentialsProvider;
|
||||
private readonly IPromptModerationService _promptModerationService = promptModerationService;
|
||||
|
||||
private async Task<Completions> GetAICompletionAsync(string systemInstructions, string userMessage, CancellationToken cancellationToken)
|
||||
private async Task<ChatCompletion> GetAICompletionAsync(string systemInstructions, string userMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
var fullPrompt = systemInstructions + "\n\n" + userMessage;
|
||||
|
||||
await _promptModerationService.ValidateAsync(fullPrompt, cancellationToken);
|
||||
|
||||
OpenAIClient azureAIClient = new(_aiCredentialsProvider.Key);
|
||||
OpenAIClientOptions clientOptions = new();
|
||||
if (!string.IsNullOrWhiteSpace(_userSettings.CustomEndpoint))
|
||||
{
|
||||
if (!Uri.TryCreate(_userSettings.CustomEndpoint, UriKind.Absolute, out var endpoint))
|
||||
{
|
||||
throw new ArgumentException($"Invalid custom endpoint URL: '{_userSettings.CustomEndpoint}'. Please ensure the URL includes the protocol (e.g., https://your-server.com/api) and is properly formatted.");
|
||||
}
|
||||
|
||||
var response = await azureAIClient.GetCompletionsAsync(
|
||||
clientOptions.Endpoint = endpoint;
|
||||
}
|
||||
|
||||
OpenAIClient openAIClient = new(new ApiKeyCredential(_aiCredentialsProvider.Key), clientOptions);
|
||||
|
||||
var response = await openAIClient.GetChatClient(ModelName).CompleteChatAsync(
|
||||
[
|
||||
new SystemChatMessage(systemInstructions),
|
||||
new UserChatMessage(userMessage)
|
||||
],
|
||||
new()
|
||||
{
|
||||
DeploymentName = ModelName,
|
||||
Prompts =
|
||||
{
|
||||
fullPrompt,
|
||||
},
|
||||
Temperature = 0.01F,
|
||||
MaxTokens = 2000,
|
||||
MaxOutputTokenCount = 2000,
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
if (response.Value.Choices[0].FinishReason == "length")
|
||||
if (response.Value.FinishReason == ChatFinishReason.Length)
|
||||
{
|
||||
Logger.LogDebug("Cut off due to length constraints");
|
||||
}
|
||||
|
||||
return response;
|
||||
return response.Value;
|
||||
}
|
||||
|
||||
public async Task<string> TransformTextAsync(string prompt, string inputText, CancellationToken cancellationToken, IProgress<double> progress)
|
||||
@@ -85,13 +98,13 @@ Output:
|
||||
var response = await GetAICompletionAsync(systemInstructions, userMessage, cancellationToken);
|
||||
|
||||
var usage = response.Usage;
|
||||
AdvancedPasteGenerateCustomFormatEvent telemetryEvent = new(usage.PromptTokens, usage.CompletionTokens, ModelName);
|
||||
AdvancedPasteGenerateCustomFormatEvent telemetryEvent = new(usage.InputTokenCount, usage.OutputTokenCount, ModelName);
|
||||
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
|
||||
var logEvent = new AIServiceFormatEvent(telemetryEvent);
|
||||
|
||||
Logger.LogDebug($"{nameof(TransformTextAsync)} complete; {logEvent.ToJsonString()}");
|
||||
|
||||
return response.Choices[0].Text;
|
||||
return response.Content[0].Text;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -106,7 +119,7 @@ Output:
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new PasteActionException(ErrorHelpers.TranslateErrorText((ex as RequestFailedException)?.Status ?? -1), ex);
|
||||
throw new PasteActionException(ErrorHelpers.TranslateErrorText((ex as ClientResultException)?.Status ?? -1), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
// 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 AdvancedPaste.Models;
|
||||
using Azure.AI.OpenAI;
|
||||
using AdvancedPaste.Settings;
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Connectors.OpenAI;
|
||||
using ChatTokenUsage = OpenAI.Chat.ChatTokenUsage;
|
||||
|
||||
namespace AdvancedPaste.Services.OpenAI;
|
||||
|
||||
public sealed class KernelService(IKernelQueryCacheService queryCacheService, IAICredentialsProvider aiCredentialsProvider, IPromptModerationService promptModerationService, ICustomTextTransformService customTextTransformService) :
|
||||
public sealed class KernelService(IUserSettings userSettings, IKernelQueryCacheService queryCacheService, IAICredentialsProvider aiCredentialsProvider, IPromptModerationService promptModerationService, ICustomTextTransformService customTextTransformService) :
|
||||
KernelServiceBase(queryCacheService, promptModerationService, customTextTransformService)
|
||||
{
|
||||
private readonly IUserSettings _userSettings = userSettings;
|
||||
private readonly IAICredentialsProvider _aiCredentialsProvider = aiCredentialsProvider;
|
||||
|
||||
protected override string ModelName => "gpt-4o";
|
||||
protected override string ModelName => string.IsNullOrWhiteSpace(_userSettings.CustomModelName) ? "gpt-4o" : _userSettings.CustomModelName;
|
||||
|
||||
protected override PromptExecutionSettings PromptExecutionSettings =>
|
||||
new OpenAIPromptExecutionSettings()
|
||||
@@ -25,10 +29,25 @@ public sealed class KernelService(IKernelQueryCacheService queryCacheService, IA
|
||||
Temperature = 0.01,
|
||||
};
|
||||
|
||||
protected override void AddChatCompletionService(IKernelBuilder kernelBuilder) => kernelBuilder.AddOpenAIChatCompletion(ModelName, _aiCredentialsProvider.Key);
|
||||
protected override void AddChatCompletionService(IKernelBuilder kernelBuilder)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_userSettings.CustomEndpoint))
|
||||
{
|
||||
kernelBuilder.AddOpenAIChatCompletion(ModelName, _aiCredentialsProvider.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Uri.TryCreate(_userSettings.CustomEndpoint, UriKind.Absolute, out var endpoint))
|
||||
{
|
||||
throw new ArgumentException($"Invalid custom endpoint URL: '{_userSettings.CustomEndpoint}'. Please ensure the URL includes the protocol (e.g., https://your-server.com/api) and is properly formatted.");
|
||||
}
|
||||
|
||||
kernelBuilder.AddOpenAIChatCompletion(ModelName, endpoint, _aiCredentialsProvider.Key);
|
||||
}
|
||||
}
|
||||
|
||||
protected override AIServiceUsage GetAIServiceUsage(ChatMessageContent chatMessage) =>
|
||||
chatMessage.Metadata?.GetValueOrDefault("Usage") is CompletionsUsage completionsUsage
|
||||
? new(PromptTokens: completionsUsage.PromptTokens, CompletionTokens: completionsUsage.CompletionTokens)
|
||||
chatMessage.Metadata?.GetValueOrDefault("Usage") is ChatTokenUsage completionsUsage
|
||||
? new(PromptTokens: completionsUsage.InputTokenCount, CompletionTokens: completionsUsage.OutputTokenCount)
|
||||
: AIServiceUsage.None;
|
||||
}
|
||||
|
||||
@@ -2,28 +2,50 @@
|
||||
// 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.ClientModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Settings;
|
||||
using ManagedCommon;
|
||||
using OpenAI;
|
||||
using OpenAI.Moderations;
|
||||
|
||||
namespace AdvancedPaste.Services.OpenAI;
|
||||
|
||||
public sealed class PromptModerationService(IAICredentialsProvider aiCredentialsProvider) : IPromptModerationService
|
||||
public sealed class PromptModerationService(IUserSettings userSettings, IAICredentialsProvider aiCredentialsProvider) : IPromptModerationService
|
||||
{
|
||||
private readonly IUserSettings _userSettings = userSettings;
|
||||
|
||||
private const string ModelName = "omni-moderation-latest";
|
||||
|
||||
private readonly IAICredentialsProvider _aiCredentialsProvider = aiCredentialsProvider;
|
||||
|
||||
public async Task ValidateAsync(string fullPrompt, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_userSettings.DisableModeration)
|
||||
{
|
||||
Logger.LogDebug($"{nameof(PromptModerationService)}.{nameof(ValidateAsync)} skipped; moderation is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ModerationClient moderationClient = new(ModelName, _aiCredentialsProvider.Key);
|
||||
OpenAIClientOptions clientOptions = new();
|
||||
if (!string.IsNullOrWhiteSpace(_userSettings.CustomEndpoint))
|
||||
{
|
||||
if (!Uri.TryCreate(_userSettings.CustomEndpoint, UriKind.Absolute, out var endpoint))
|
||||
{
|
||||
throw new ArgumentException($"Invalid custom endpoint URL: {_userSettings.CustomEndpoint}");
|
||||
}
|
||||
|
||||
clientOptions.Endpoint = endpoint;
|
||||
}
|
||||
|
||||
ModerationClient moderationClient = new(ModelName, new(_aiCredentialsProvider.Key), clientOptions);
|
||||
var moderationClientResult = await moderationClient.ClassifyTextAsync(fullPrompt, cancellationToken);
|
||||
var moderationResult = moderationClientResult.Value;
|
||||
|
||||
|
||||
@@ -120,6 +120,9 @@
|
||||
<data name="AIMistakeNote.Text" xml:space="preserve">
|
||||
<value>AI can make mistakes.</value>
|
||||
</data>
|
||||
<data name="CustomAIMistakeNote.Text" xml:space="preserve">
|
||||
<value>You are using a custom model endpoint please verify all answers.</value>
|
||||
</data>
|
||||
<data name="ClipboardEmptyWarning" xml:space="preserve">
|
||||
<value>Clipboard does not contain any usable formats</value>
|
||||
</data>
|
||||
|
||||
@@ -85,6 +85,14 @@ namespace AdvancedPaste.ViewModels
|
||||
|
||||
public bool IsAdvancedAIEnabled => IsCustomAIServiceEnabled && _userSettings.IsAdvancedAIEnabled;
|
||||
|
||||
public bool IsLocalModelMode => _userSettings.IsLocalModelMode;
|
||||
|
||||
public string CustomEndpoint => _userSettings.CustomEndpoint;
|
||||
|
||||
public string CustomModelName => _userSettings.CustomModelName;
|
||||
|
||||
public bool DisableModeration => _userSettings.DisableModeration;
|
||||
|
||||
public bool ClipboardHasData => AvailableClipboardFormats != ClipboardFormat.None;
|
||||
|
||||
public bool ClipboardHasDataForCustomAI => PasteFormat.SupportsClipboardFormats(CustomAIFormat, AvailableClipboardFormats);
|
||||
@@ -167,6 +175,10 @@ namespace AdvancedPaste.ViewModels
|
||||
OnPropertyChanged(nameof(ClipboardHasDataForCustomAI));
|
||||
OnPropertyChanged(nameof(IsCustomAIAvailable));
|
||||
OnPropertyChanged(nameof(IsAdvancedAIEnabled));
|
||||
OnPropertyChanged(nameof(IsLocalModelMode));
|
||||
OnPropertyChanged(nameof(CustomEndpoint));
|
||||
OnPropertyChanged(nameof(CustomModelName));
|
||||
OnPropertyChanged(nameof(DisableModeration));
|
||||
|
||||
EnqueueRefreshPasteFormats();
|
||||
}
|
||||
@@ -275,6 +287,9 @@ namespace AdvancedPaste.ViewModels
|
||||
OnPropertyChanged(nameof(CustomAIUnavailableErrorText));
|
||||
OnPropertyChanged(nameof(IsCustomAIServiceEnabled));
|
||||
OnPropertyChanged(nameof(IsAdvancedAIEnabled));
|
||||
OnPropertyChanged(nameof(CustomEndpoint));
|
||||
OnPropertyChanged(nameof(CustomModelName));
|
||||
OnPropertyChanged(nameof(DisableModeration));
|
||||
OnPropertyChanged(nameof(IsCustomAIAvailable));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,157 +21,6 @@
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.SettingsControls/SettingsExpander/SettingsExpander.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SystemColorHighlightTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SystemColorGrayTextColor" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Style x:Key="SubtleButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AnimatedIcon.State="Normal"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<x:Double x:Key="SecondaryTextFontSize">12</x:Double>
|
||||
<Style x:Key="SecondaryTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="{StaticResource SecondaryTextFontSize}" />
|
||||
|
||||
@@ -9,157 +9,6 @@
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SystemColorHighlightTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SystemColorGrayTextColor" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Style x:Key="SubtleButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AnimatedIcon.State="Normal"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -27,160 +27,6 @@
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SystemColorHighlightTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SystemColorGrayTextColor" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Style x:Key="SubtleButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AnimatedIcon.State="Normal"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
|
||||
<tkconverters:StringVisibilityConverter
|
||||
x:Key="StringVisibilityConverter"
|
||||
EmptyValue="Collapsed"
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -141,7 +147,13 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -153,7 +165,19 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -4,5 +4,14 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.7.250513003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -8,21 +8,28 @@
|
||||
#include "common/utils/process_path.h"
|
||||
#include "common/utils/excluded_apps.h"
|
||||
#include "common/utils/MsWindowsSettings.h"
|
||||
#include <winrt/Windows.Graphics.h>
|
||||
|
||||
#include <winrt/Microsoft.UI.Composition.Interop.h>
|
||||
#include <winrt/Microsoft.UI.Dispatching.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Controls.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Media.h>
|
||||
#include <winrt/Microsoft.UI.Xaml.Hosting.h>
|
||||
#include <winrt/Microsoft.UI.Interop.h>
|
||||
#include <winrt/Microsoft.UI.Content.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef COMPOSITION
|
||||
namespace winrt
|
||||
{
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
}
|
||||
|
||||
namespace ABI
|
||||
{
|
||||
using namespace ABI::Windows::System;
|
||||
using namespace ABI::Windows::UI::Composition::Desktop;
|
||||
}
|
||||
#endif
|
||||
namespace muxc = winrt::Microsoft::UI::Composition;
|
||||
namespace muxx = winrt::Microsoft::UI::Xaml;
|
||||
namespace muxxc = winrt::Microsoft::UI::Xaml::Controls;
|
||||
namespace muxxh = winrt::Microsoft::UI::Xaml::Hosting;
|
||||
|
||||
#pragma region Super_Sonar_Base_Code
|
||||
|
||||
@@ -70,11 +77,11 @@ protected:
|
||||
int m_sonarRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS;
|
||||
int m_sonarZoomFactor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
|
||||
DWORD m_fadeDuration = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS;
|
||||
int m_finalAlphaNumerator = FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY;
|
||||
int m_finalAlphaNumerator = 100; // legacy (root now always animates to 1.0; kept for GDI fallback compatibility)
|
||||
std::vector<std::wstring> m_excludedApps;
|
||||
int m_shakeMinimumDistance = FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE;
|
||||
static constexpr int FinalAlphaDenominator = 100;
|
||||
winrt::DispatcherQueueController m_dispatcherQueueController{ nullptr };
|
||||
winrt::Microsoft::UI::Dispatching::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;
|
||||
@@ -82,7 +89,6 @@ protected:
|
||||
int m_shakeFactor = FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR;
|
||||
|
||||
private:
|
||||
|
||||
// Save the mouse movement that occurred in any direction.
|
||||
struct PointerRecentMovement
|
||||
{
|
||||
@@ -159,7 +165,6 @@ bool SuperSonar<D>::Initialize(HINSTANCE hinst)
|
||||
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
|
||||
WNDCLASS wc{};
|
||||
|
||||
if (!GetClassInfoW(hinst, className, &wc))
|
||||
{
|
||||
wc.lpfnWndProc = s_WndProc;
|
||||
@@ -171,14 +176,28 @@ bool SuperSonar<D>::Initialize(HINSTANCE hinst)
|
||||
|
||||
if (!RegisterClassW(&wc))
|
||||
{
|
||||
Logger::error("RegisterClassW failed. GetLastError={}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// else: class already registered
|
||||
|
||||
m_hwndOwner = CreateWindow(L"static", nullptr, WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, hinst, nullptr);
|
||||
if (!m_hwndOwner)
|
||||
{
|
||||
Logger::error("Failed to create owner window. GetLastError={}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | Shim()->GetExtendedStyle();
|
||||
return CreateWindowExW(exStyle, className, windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hinst, this) != nullptr;
|
||||
DWORD exStyle = WS_EX_TOOLWINDOW | Shim()->GetExtendedStyle();
|
||||
HWND created = CreateWindowExW(exStyle, className, windowTitle, WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, m_hwndOwner, nullptr, hinst, this);
|
||||
if (!created)
|
||||
{
|
||||
Logger::error("CreateWindowExW failed. GetLastError={}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
@@ -226,7 +245,8 @@ LRESULT SuperSonar<D>::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
if(!OnSonarCreate()) return -1;
|
||||
if (!OnSonarCreate())
|
||||
return -1;
|
||||
UpdateMouseSnooping();
|
||||
return 0;
|
||||
|
||||
@@ -314,8 +334,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_activationMethod != FindMyMouseActivationMethod::DoubleRightControlKey && m_activationMethod != FindMyMouseActivationMethod::DoubleLeftControlKey)
|
||||
|| input.data.keyboard.VKey != VK_CONTROL)
|
||||
if ((m_activationMethod != FindMyMouseActivationMethod::DoubleRightControlKey && m_activationMethod != FindMyMouseActivationMethod::DoubleLeftControlKey) || input.data.keyboard.VKey != VK_CONTROL)
|
||||
{
|
||||
StopSonar();
|
||||
return;
|
||||
@@ -326,8 +345,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
bool leftCtrlPressed = (input.data.keyboard.Flags & RI_KEY_E0) == 0;
|
||||
bool rightCtrlPressed = (input.data.keyboard.Flags & RI_KEY_E0) != 0;
|
||||
|
||||
if ((m_activationMethod == FindMyMouseActivationMethod::DoubleRightControlKey && !rightCtrlPressed)
|
||||
|| (m_activationMethod == FindMyMouseActivationMethod::DoubleLeftControlKey && !leftCtrlPressed))
|
||||
if ((m_activationMethod == FindMyMouseActivationMethod::DoubleRightControlKey && !rightCtrlPressed) || (m_activationMethod == FindMyMouseActivationMethod::DoubleLeftControlKey && !leftCtrlPressed))
|
||||
{
|
||||
StopSonar();
|
||||
return;
|
||||
@@ -376,7 +394,6 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
GetCursorPos(&m_lastKeyPos);
|
||||
UpdateMouseSnooping();
|
||||
}
|
||||
Logger::info("Detecting double left control click with {} ms interval.", doubleClickInterval);
|
||||
m_lastKeyTime = now;
|
||||
m_lastKeyPos = ptCursor;
|
||||
}
|
||||
@@ -402,14 +419,13 @@ template<typename D>
|
||||
void SuperSonar<D>::DetectShake()
|
||||
{
|
||||
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; });
|
||||
|
||||
|
||||
|
||||
double distanceTravelled = 0;
|
||||
LONGLONG currentX=0, minX=0, maxX=0;
|
||||
LONGLONG currentY=0, minY=0, maxY=0;
|
||||
LONGLONG currentX = 0, minX = 0, maxX = 0;
|
||||
LONGLONG currentY = 0, minY = 0, maxY = 0;
|
||||
|
||||
for (const PointerRecentMovement& movement : m_movementHistory)
|
||||
{
|
||||
@@ -421,23 +437,22 @@ void SuperSonar<D>::DetectShake()
|
||||
minY = min(currentY, minY);
|
||||
maxY = max(currentY, maxY);
|
||||
}
|
||||
|
||||
|
||||
if (distanceTravelled < m_shakeMinimumDistance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Size of the rectangle that the pointer moved in.
|
||||
double rectangleWidth = static_cast<double>(maxX) - minX;
|
||||
double rectangleHeight = static_cast<double>(maxY) - minY;
|
||||
double rectangleWidth = static_cast<double>(maxX) - minX;
|
||||
double rectangleHeight = static_cast<double>(maxY) - minY;
|
||||
|
||||
double diagonal = sqrt(rectangleWidth * rectangleWidth + rectangleHeight * rectangleHeight);
|
||||
if (diagonal > 0 && distanceTravelled / diagonal > (m_shakeFactor/100.f))
|
||||
if (diagonal > 0 && distanceTravelled / diagonal > (m_shakeFactor / 100.f))
|
||||
{
|
||||
m_movementHistory.clear();
|
||||
StartSonar();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
@@ -453,7 +468,7 @@ void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
{
|
||||
LONG relativeX = 0;
|
||||
LONG relativeY = 0;
|
||||
if ((input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) == MOUSE_MOVE_ABSOLUTE && (input.data.mouse.lLastX!=0 || input.data.mouse.lLastY!=0))
|
||||
if ((input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) == MOUSE_MOVE_ABSOLUTE && (input.data.mouse.lLastX != 0 || input.data.mouse.lLastY != 0))
|
||||
{
|
||||
// Getting absolute mouse coordinates. Likely inside a VM / RDP session.
|
||||
if (m_seenAnAbsoluteMousePosition)
|
||||
@@ -482,7 +497,7 @@ void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_movementHistory.push_back({ .diff = { .x=relativeX, .y=relativeY }, .tick = GetTickCount64() });
|
||||
m_movementHistory.push_back({ .diff = { .x = relativeX, .y = relativeY }, .tick = GetTickCount64() });
|
||||
// Mouse movement changed directions. Take the opportunity do detect shake.
|
||||
DetectShake();
|
||||
}
|
||||
@@ -491,7 +506,6 @@ void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
{
|
||||
m_movementHistory.push_back({ .diff = { .x = relativeX, .y = relativeY }, .tick = GetTickCount64() });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (input.data.mouse.usButtonFlags)
|
||||
@@ -518,7 +532,6 @@ void SuperSonar<D>::StartSonar()
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info("Focusing the sonar on the mouse cursor.");
|
||||
Trace::MousePointerFocused();
|
||||
// Cover the entire virtual screen.
|
||||
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
|
||||
@@ -633,12 +646,26 @@ struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
|
||||
|
||||
DWORD GetExtendedStyle()
|
||||
{
|
||||
return WS_EX_NOREDIRECTIONBITMAP;
|
||||
// Remove WS_EX_NOREDIRECTIONBITMAP for Composition/XAML to allow DWM redirection.
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AfterMoveSonar()
|
||||
{
|
||||
m_spotlight.Offset({ static_cast<float>(m_sonarPos.x), static_cast<float>(m_sonarPos.y), 0.0f });
|
||||
const float scale = static_cast<float>(m_surface.XamlRoot().RasterizationScale());
|
||||
// Move gradient center
|
||||
if (m_spotlightMaskGradient)
|
||||
{
|
||||
m_spotlightMaskGradient.EllipseCenter({ static_cast<float>(m_sonarPos.x) / scale,
|
||||
static_cast<float>(m_sonarPos.y) / scale });
|
||||
}
|
||||
// Move spotlight visual (color fill) below masked backdrop
|
||||
if (m_spotlight)
|
||||
{
|
||||
m_spotlight.Offset({ static_cast<float>(m_sonarPos.x) / scale,
|
||||
static_cast<float>(m_sonarPos.y) / scale,
|
||||
0.0f });
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
||||
@@ -646,24 +673,29 @@ struct CompositionSpotlight : SuperSonar<CompositionSpotlight>
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
return OnCompositionCreate() && BaseWndProc(message, wParam, lParam);
|
||||
if (!OnCompositionCreate())
|
||||
return -1;
|
||||
return BaseWndProc(message, wParam, lParam);
|
||||
|
||||
case WM_OPACITY_ANIMATION_COMPLETED:
|
||||
OnOpacityAnimationCompleted();
|
||||
break;
|
||||
case WM_SIZE:
|
||||
UpdateIslandSize();
|
||||
break;
|
||||
}
|
||||
return BaseWndProc(message, wParam, lParam);
|
||||
}
|
||||
|
||||
void SetSonarVisibility(bool visible)
|
||||
{
|
||||
m_batch = m_compositor.GetCommitBatch(winrt::CompositionBatchTypes::Animation);
|
||||
m_batch = m_compositor.GetCommitBatch(muxc::CompositionBatchTypes::Animation);
|
||||
BOOL isEnabledAnimations = GetAnimationsEnabled();
|
||||
m_animation.Duration(std::chrono::milliseconds{ isEnabledAnimations ? m_fadeDuration : 1 });
|
||||
m_batch.Completed([hwnd = m_hwnd](auto&&, auto&&) {
|
||||
PostMessage(hwnd, WM_OPACITY_ANIMATION_COMPLETED, 0, 0);
|
||||
});
|
||||
m_root.Opacity(visible ? static_cast<float>(m_finalAlphaNumerator) / FinalAlphaDenominator : 0.0f);
|
||||
m_root.Opacity(visible ? 1.0f : 0.0f);
|
||||
if (visible)
|
||||
{
|
||||
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
|
||||
@@ -679,54 +711,138 @@ private:
|
||||
bool OnCompositionCreate()
|
||||
try
|
||||
{
|
||||
// We need a dispatcher queue.
|
||||
DispatcherQueueOptions options = {
|
||||
sizeof(options),
|
||||
DQTYPE_THREAD_CURRENT,
|
||||
DQTAT_COM_ASTA,
|
||||
};
|
||||
ABI::IDispatcherQueueController* controller;
|
||||
winrt::check_hresult(CreateDispatcherQueueController(options, &controller));
|
||||
*winrt::put_abi(m_dispatcherQueueController) = controller;
|
||||
// Creating composition resources
|
||||
// Ensure a DispatcherQueue bound to this thread (required by WinAppSDK composition/XAML)
|
||||
if (!m_dispatcherQueueController)
|
||||
{
|
||||
// Ensure COM is initialized
|
||||
try
|
||||
{
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
// COM STA initialized
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error("Failed to initialize COM apartment: {}", winrt::to_string(e.message()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the compositor for our window.
|
||||
m_compositor = winrt::Compositor();
|
||||
ABI::IDesktopWindowTarget* target;
|
||||
winrt::check_hresult(m_compositor.as<ABI::ICompositorDesktopInterop>()->CreateDesktopWindowTarget(m_hwnd, false, &target));
|
||||
*winrt::put_abi(m_target) = target;
|
||||
try
|
||||
{
|
||||
m_dispatcherQueueController =
|
||||
winrt::Microsoft::UI::Dispatching::DispatcherQueueController::CreateOnCurrentThread();
|
||||
// DispatcherQueueController created
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error("Failed to create DispatcherQueueController: {}", winrt::to_string(e.message()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Our composition tree:
|
||||
// 1) Create a XAML island and attach it to this HWND
|
||||
try
|
||||
{
|
||||
m_island = winrt::Microsoft::UI::Xaml::Hosting::DesktopWindowXamlSource{};
|
||||
auto windowId = winrt::Microsoft::UI::GetWindowIdFromWindow(m_hwnd);
|
||||
m_island.Initialize(windowId);
|
||||
// Xaml source initialized
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error("Failed to create XAML island: {}", winrt::to_string(e.message()));
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateIslandSize();
|
||||
// Island size set
|
||||
|
||||
// 2) Create a XAML container to host the Composition child visual
|
||||
m_surface = winrt::Microsoft::UI::Xaml::Controls::Grid{};
|
||||
|
||||
// A transparent background keeps hit-testing consistent vs. null brush
|
||||
m_surface.Background(winrt::Microsoft::UI::Xaml::Media::SolidColorBrush{
|
||||
winrt::Microsoft::UI::Colors::Transparent() });
|
||||
m_surface.HorizontalAlignment(muxx::HorizontalAlignment::Stretch);
|
||||
m_surface.VerticalAlignment(muxx::VerticalAlignment::Stretch);
|
||||
|
||||
m_island.Content(m_surface);
|
||||
|
||||
// 3) Get the compositor from the XAML visual tree (pure MUXC path)
|
||||
try
|
||||
{
|
||||
auto elementVisual =
|
||||
winrt::Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(m_surface);
|
||||
m_compositor = elementVisual.Compositor();
|
||||
// Compositor acquired
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error("Failed to get compositor: {}", winrt::to_string(e.message()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4) Build the composition tree
|
||||
//
|
||||
// [root] ContainerVisual
|
||||
// \ LayerVisual
|
||||
// \[gray backdrop]
|
||||
// [spotlight]
|
||||
// [root] ContainerVisual (fills host)
|
||||
// \ LayerVisual
|
||||
// \ [backdrop dim * radial gradient mask (hole)]
|
||||
m_root = m_compositor.CreateContainerVisual();
|
||||
m_root.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
|
||||
m_root.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
||||
m_root.Opacity(0.0f);
|
||||
m_target.Root(m_root);
|
||||
|
||||
// Insert our root as a hand-in Visual under the XAML element
|
||||
winrt::Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(m_surface, m_root);
|
||||
|
||||
auto layer = m_compositor.CreateLayerVisual();
|
||||
layer.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
|
||||
layer.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
||||
m_root.Children().InsertAtTop(layer);
|
||||
|
||||
m_backdrop = m_compositor.CreateSpriteVisual();
|
||||
m_backdrop.RelativeSizeAdjustment({ 1.0f, 1.0f }); // fill the parent
|
||||
m_backdrop.Brush(m_compositor.CreateColorBrush(m_backgroundColor));
|
||||
layer.Children().InsertAtTop(m_backdrop);
|
||||
const float scale = static_cast<float>(m_surface.XamlRoot().RasterizationScale());
|
||||
const float rDip = m_sonarRadiusFloat / scale;
|
||||
const float zoom = static_cast<float>(m_sonarZoomFactor);
|
||||
|
||||
m_circleGeometry = m_compositor.CreateEllipseGeometry(); // radius set via expression animation
|
||||
// Spotlight shape (below backdrop, visible through hole)
|
||||
m_circleGeometry = m_compositor.CreateEllipseGeometry();
|
||||
m_circleShape = m_compositor.CreateSpriteShape(m_circleGeometry);
|
||||
m_circleShape.FillBrush(m_compositor.CreateColorBrush(m_spotlightColor));
|
||||
m_circleShape.Offset({ m_sonarRadiusFloat * m_sonarZoomFactor, m_sonarRadiusFloat * m_sonarZoomFactor });
|
||||
m_circleShape.Offset({ rDip * zoom, rDip * zoom });
|
||||
m_spotlight = m_compositor.CreateShapeVisual();
|
||||
m_spotlight.Size({ m_sonarRadiusFloat * 2 * m_sonarZoomFactor, m_sonarRadiusFloat * 2 * m_sonarZoomFactor });
|
||||
m_spotlight.Size({ rDip * 2 * zoom, rDip * 2 * zoom });
|
||||
m_spotlight.AnchorPoint({ 0.5f, 0.5f });
|
||||
m_spotlight.Shapes().Append(m_circleShape);
|
||||
|
||||
layer.Children().InsertAtTop(m_spotlight);
|
||||
|
||||
// Implicitly animate the alpha.
|
||||
// Dim color (source)
|
||||
m_dimColorBrush = m_compositor.CreateColorBrush(m_backgroundColor);
|
||||
// Radial gradient mask (center transparent, outer opaque)
|
||||
m_spotlightMaskGradient = m_compositor.CreateRadialGradientBrush();
|
||||
m_spotlightMaskGradient.MappingMode(muxc::CompositionMappingMode::Absolute);
|
||||
m_maskStopCenter = m_compositor.CreateColorGradientStop();
|
||||
m_maskStopCenter.Offset(0.0f);
|
||||
m_maskStopCenter.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
||||
m_maskStopInner = m_compositor.CreateColorGradientStop();
|
||||
m_maskStopInner.Offset(0.995f);
|
||||
m_maskStopInner.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
||||
m_maskStopOuter = m_compositor.CreateColorGradientStop();
|
||||
m_maskStopOuter.Offset(1.0f);
|
||||
m_maskStopOuter.Color(winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255));
|
||||
m_spotlightMaskGradient.ColorStops().Append(m_maskStopCenter);
|
||||
m_spotlightMaskGradient.ColorStops().Append(m_maskStopInner);
|
||||
m_spotlightMaskGradient.ColorStops().Append(m_maskStopOuter);
|
||||
m_spotlightMaskGradient.EllipseCenter({ rDip * zoom, rDip * zoom });
|
||||
m_spotlightMaskGradient.EllipseRadius({ rDip * zoom, rDip * zoom });
|
||||
|
||||
m_maskBrush = m_compositor.CreateMaskBrush();
|
||||
m_maskBrush.Source(m_dimColorBrush);
|
||||
m_maskBrush.Mask(m_spotlightMaskGradient);
|
||||
|
||||
m_backdrop = m_compositor.CreateSpriteVisual();
|
||||
m_backdrop.RelativeSizeAdjustment({ 1.0f, 1.0f });
|
||||
m_backdrop.Brush(m_maskBrush);
|
||||
layer.Children().InsertAtTop(m_backdrop);
|
||||
|
||||
// 5) Implicit opacity animation on the root
|
||||
m_animation = m_compositor.CreateScalarKeyFrameAnimation();
|
||||
m_animation.Target(L"Opacity");
|
||||
m_animation.InsertExpressionKeyFrame(1.0f, L"this.FinalValue");
|
||||
@@ -735,20 +851,31 @@ private:
|
||||
collection.Insert(L"Opacity", m_animation);
|
||||
m_root.ImplicitAnimations(collection);
|
||||
|
||||
// Radius of spotlight shrinks as opacity increases.
|
||||
// At opacity zero, it is m_sonarRadius * SonarZoomFactor.
|
||||
// At maximum opacity, it is m_sonarRadius.
|
||||
// 6) Spotlight radius shrinks as opacity increases (expression animation)
|
||||
auto radiusExpression = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression.SetReferenceParameter(L"Root", m_root);
|
||||
wchar_t expressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity * %d / %d)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius, FinalAlphaDenominator, m_finalAlphaNumerator));
|
||||
radiusExpression.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression);
|
||||
|
||||
wchar_t expressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(
|
||||
expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius));
|
||||
|
||||
radiusExpression.Expression(expressionText);
|
||||
m_spotlightMaskGradient.StartAnimation(L"EllipseRadius", radiusExpression);
|
||||
// Also animate spotlight geometry radius for visual consistency
|
||||
if (m_circleGeometry)
|
||||
{
|
||||
auto radiusExpression2 = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression2.SetReferenceParameter(L"Root", m_root);
|
||||
radiusExpression2.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression2);
|
||||
}
|
||||
|
||||
// Composition created successfully
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error("Failed to create FindMyMouse visual: {}", winrt::to_string(e.message()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -760,11 +887,27 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateIslandSize()
|
||||
{
|
||||
if (!m_island)
|
||||
return;
|
||||
|
||||
RECT rc{};
|
||||
if (!GetClientRect(m_hwnd, &rc))
|
||||
return;
|
||||
|
||||
const int width = rc.right - rc.left;
|
||||
const int height = rc.bottom - rc.top;
|
||||
|
||||
auto bridge = m_island.SiteBridge();
|
||||
bridge.MoveAndResize(winrt::Windows::Graphics::RectInt32{ 0, 0, width, height });
|
||||
}
|
||||
|
||||
public:
|
||||
void ApplySettings(const FindMyMouseSettings& settings, bool applyToRuntimeObjects) {
|
||||
void ApplySettings(const FindMyMouseSettings& settings, bool applyToRuntimeObjects)
|
||||
{
|
||||
if (!applyToRuntimeObjects)
|
||||
{
|
||||
// Runtime objects not created yet. Just update fields.
|
||||
m_sonarRadius = settings.spotlightRadius;
|
||||
m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);
|
||||
m_backgroundColor = settings.backgroundColor;
|
||||
@@ -773,7 +916,6 @@ public:
|
||||
m_includeWinKey = settings.includeWinKey;
|
||||
m_doNotActivateOnGameMode = settings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = settings.animationDurationMs > 0 ? settings.animationDurationMs : 1;
|
||||
m_finalAlphaNumerator = settings.overlayOpacity;
|
||||
m_sonarZoomFactor = settings.spotlightInitialZoom;
|
||||
m_excludedApps = settings.excludedApps;
|
||||
m_shakeMinimumDistance = settings.shakeMinimumDistance;
|
||||
@@ -782,11 +924,9 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
// Runtime objects already created. Should update in the owner thread.
|
||||
if (m_dispatcherQueueController == nullptr)
|
||||
{
|
||||
Logger::warn("Tried accessing the dispatch queue controller before it was initialized.");
|
||||
// No dispatcher Queue Controller? Means initialization still hasn't run, so settings will be applied then.
|
||||
return;
|
||||
}
|
||||
auto dispatcherQueue = m_dispatcherQueueController.DispatcherQueue();
|
||||
@@ -794,7 +934,6 @@ public:
|
||||
bool enqueueSucceeded = dispatcherQueue.TryEnqueue([=]() {
|
||||
if (!m_destroyed)
|
||||
{
|
||||
// Runtime objects not created yet. Just update fields.
|
||||
m_sonarRadius = localSettings.spotlightRadius;
|
||||
m_sonarRadiusFloat = static_cast<float>(m_sonarRadius);
|
||||
m_backgroundColor = localSettings.backgroundColor;
|
||||
@@ -803,7 +942,6 @@ public:
|
||||
m_includeWinKey = localSettings.includeWinKey;
|
||||
m_doNotActivateOnGameMode = localSettings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = localSettings.animationDurationMs > 0 ? localSettings.animationDurationMs : 1;
|
||||
m_finalAlphaNumerator = localSettings.overlayOpacity;
|
||||
m_sonarZoomFactor = localSettings.spotlightInitialZoom;
|
||||
m_excludedApps = localSettings.excludedApps;
|
||||
m_shakeMinimumDistance = localSettings.shakeMinimumDistance;
|
||||
@@ -812,20 +950,41 @@ public:
|
||||
UpdateMouseSnooping(); // For the shake mouse activation method
|
||||
|
||||
// Apply new settings to runtime composition objects.
|
||||
m_backdrop.Brush().as<winrt::CompositionColorBrush>().Color(m_backgroundColor);
|
||||
m_circleShape.FillBrush().as<winrt::CompositionColorBrush>().Color(m_spotlightColor);
|
||||
m_circleShape.Offset({ m_sonarRadiusFloat * m_sonarZoomFactor, m_sonarRadiusFloat * m_sonarZoomFactor });
|
||||
m_spotlight.Size({ m_sonarRadiusFloat * 2 * m_sonarZoomFactor, m_sonarRadiusFloat * 2 * m_sonarZoomFactor });
|
||||
m_animation.Duration(std::chrono::milliseconds{ m_fadeDuration });
|
||||
m_circleGeometry.StopAnimation(L"Radius");
|
||||
|
||||
// Update animation
|
||||
if (m_dimColorBrush)
|
||||
{
|
||||
m_dimColorBrush.Color(m_backgroundColor);
|
||||
}
|
||||
if (m_circleShape)
|
||||
{
|
||||
if (auto brush = m_circleShape.FillBrush().try_as<muxc::CompositionColorBrush>())
|
||||
{
|
||||
brush.Color(m_spotlightColor);
|
||||
}
|
||||
}
|
||||
const float scale = static_cast<float>(m_surface.XamlRoot().RasterizationScale());
|
||||
const float rDip = m_sonarRadiusFloat / scale;
|
||||
const float zoom = static_cast<float>(m_sonarZoomFactor);
|
||||
m_spotlightMaskGradient.StopAnimation(L"EllipseRadius");
|
||||
m_spotlightMaskGradient.EllipseCenter({ rDip * zoom, rDip * zoom });
|
||||
if (m_spotlight)
|
||||
{
|
||||
m_spotlight.Size({ rDip * 2 * zoom, rDip * 2 * zoom });
|
||||
m_circleShape.Offset({ rDip * zoom, rDip * zoom });
|
||||
}
|
||||
auto radiusExpression = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression.SetReferenceParameter(L"Root", m_root);
|
||||
wchar_t expressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity * %d / %d)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius, FinalAlphaDenominator, m_finalAlphaNumerator));
|
||||
winrt::check_hresult(StringCchPrintfW(expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius));
|
||||
radiusExpression.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression);
|
||||
m_spotlightMaskGradient.StartAnimation(L"EllipseRadius", radiusExpression);
|
||||
if (m_circleGeometry)
|
||||
{
|
||||
m_circleGeometry.StopAnimation(L"Radius");
|
||||
auto radiusExpression2 = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression2.SetReferenceParameter(L"Root", m_root);
|
||||
radiusExpression2.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression2);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!enqueueSucceeded)
|
||||
@@ -836,17 +995,27 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
winrt::Compositor m_compositor{ nullptr };
|
||||
winrt::Desktop::DesktopWindowTarget m_target{ nullptr };
|
||||
winrt::ContainerVisual m_root{ nullptr };
|
||||
winrt::CompositionEllipseGeometry m_circleGeometry{ nullptr };
|
||||
winrt::ShapeVisual m_spotlight{ nullptr };
|
||||
winrt::CompositionCommitBatch m_batch{ nullptr };
|
||||
winrt::SpriteVisual m_backdrop{ nullptr };
|
||||
winrt::CompositionSpriteShape m_circleShape{ nullptr };
|
||||
muxc::Compositor m_compositor{ nullptr };
|
||||
muxxh::DesktopWindowXamlSource m_island{ nullptr };
|
||||
muxxc::Grid m_surface{ nullptr };
|
||||
|
||||
muxc::ContainerVisual m_root{ nullptr };
|
||||
muxc::CompositionCommitBatch m_batch{ nullptr };
|
||||
muxc::SpriteVisual m_backdrop{ nullptr };
|
||||
// Spotlight shape visuals
|
||||
muxc::CompositionEllipseGeometry m_circleGeometry{ nullptr };
|
||||
muxc::ShapeVisual m_spotlight{ nullptr };
|
||||
muxc::CompositionSpriteShape m_circleShape{ nullptr };
|
||||
// Radial gradient mask components
|
||||
muxc::CompositionMaskBrush m_maskBrush{ nullptr };
|
||||
muxc::CompositionColorBrush m_dimColorBrush{ nullptr };
|
||||
muxc::CompositionRadialGradientBrush m_spotlightMaskGradient{ nullptr };
|
||||
muxc::CompositionColorGradientStop m_maskStopCenter{ nullptr };
|
||||
muxc::CompositionColorGradientStop m_maskStopInner{ nullptr };
|
||||
muxc::CompositionColorGradientStop m_maskStopOuter{ nullptr };
|
||||
winrt::Windows::UI::Color m_backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR;
|
||||
winrt::Windows::UI::Color m_spotlightColor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR;
|
||||
winrt::ScalarKeyFrameAnimation m_animation{ nullptr };
|
||||
muxc::ScalarKeyFrameAnimation m_animation{ nullptr };
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
@@ -1047,7 +1216,6 @@ struct GdiCrosshairs : GdiSonar<GdiCrosshairs>
|
||||
|
||||
#pragma endregion Super_Sonar_Base_Code
|
||||
|
||||
|
||||
#pragma region Super_Sonar_API
|
||||
|
||||
CompositionSpotlight* m_sonar = nullptr;
|
||||
@@ -1055,7 +1223,6 @@ void FindMyMouseApplySettings(const FindMyMouseSettings& settings)
|
||||
{
|
||||
if (m_sonar != nullptr)
|
||||
{
|
||||
Logger::info("Applying settings.");
|
||||
m_sonar->ApplySettings(settings, true);
|
||||
}
|
||||
}
|
||||
@@ -1064,7 +1231,6 @@ void FindMyMouseDisable()
|
||||
{
|
||||
if (m_sonar != nullptr)
|
||||
{
|
||||
Logger::info("Terminating a sonar instance.");
|
||||
m_sonar->Terminate();
|
||||
}
|
||||
}
|
||||
@@ -1077,7 +1243,6 @@ bool FindMyMouseIsEnabled()
|
||||
// Based on SuperSonar's original wWinMain.
|
||||
int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings)
|
||||
{
|
||||
Logger::info("Starting a sonar instance.");
|
||||
if (m_sonar != nullptr)
|
||||
{
|
||||
Logger::error("A sonar instance was still working when trying to start a new one.");
|
||||
@@ -1092,7 +1257,6 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings)
|
||||
return 0;
|
||||
}
|
||||
m_sonar = &sonar;
|
||||
Logger::info("Initialized the sonar instance.");
|
||||
|
||||
InitializeWinhookEventIds();
|
||||
|
||||
@@ -1105,7 +1269,6 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings)
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
Logger::info("Sonar message loop ended.");
|
||||
m_sonar = nullptr;
|
||||
|
||||
return (int)msg.wParam;
|
||||
|
||||
@@ -11,9 +11,9 @@ enum struct FindMyMouseActivationMethod : int
|
||||
};
|
||||
|
||||
constexpr bool FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE = true;
|
||||
// Default colors now include full alpha. Opacity is encoded directly in color alpha (legacy overlay_opacity migrated into A channel)
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 0, 0, 0);
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(255, 255, 255, 255);
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY = 50;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS = 100;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;
|
||||
@@ -30,7 +30,6 @@ struct FindMyMouseSettings
|
||||
bool doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
winrt::Windows::UI::Color backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR;
|
||||
winrt::Windows::UI::Color spotlightColor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_COLOR;
|
||||
int overlayOpacity = FIND_MY_MOUSE_DEFAULT_OVERLAY_OPACITY;
|
||||
int spotlightRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS;
|
||||
int animationDurationMs = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS;
|
||||
int spotlightInitialZoom = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
@@ -7,6 +14,14 @@
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>FindMyMouse</RootNamespace>
|
||||
<ProjectName>FindMyMouse</ProjectName>
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTEnableComponentProjection>false</CppWinRTEnableComponentProjection>
|
||||
<CppWinRTGenerateWindowsMetadata>false</CppWinRTGenerateWindowsMetadata>
|
||||
<WindowsAppSdkBootstrapInitialize>false</WindowsAppSdkBootstrapInitialize>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<WindowsAppSDKVerifyTransitiveDependencies>false</WindowsAppSDKVerifyTransitiveDependencies>
|
||||
<!-- Force NuGet to treat this project strictly as packages.config style -->
|
||||
<RestoreProjectStyle>packages.config</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
@@ -30,6 +45,7 @@
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
|
||||
@@ -79,7 +95,8 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<!-- Add Generated Files folder so #include <winrt/...> finds projected headers -->
|
||||
<AdditionalIncludeDirectories>$(GeneratedFilesDir);$(SolutionDir)src\;$(SolutionDir)src\modules;$(SolutionDir)src\common\Telemetry;$(MSBuildThisFileDirectory)..\..\..\..\src\;$(MSBuildThisFileDirectory)..\..\..\..\src\modules;$(MSBuildThisFileDirectory)..\..\..\..\src\common\Telemetry;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@@ -98,6 +115,7 @@
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="WinHookEventIDs.cpp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
@@ -112,16 +130,56 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<!-- Deduplicate WindowsAppRuntimeAutoInitializer.cpp (added twice via transitive imports causing LNK4042). Remove all then add exactly once. -->
|
||||
<ItemGroup Condition="'$(PkgMicrosoft_WindowsAppSDK)'!=''">
|
||||
<!-- Remove any transitive inclusion first -->
|
||||
<ClCompile Remove="$(PkgMicrosoft_WindowsAppSDK)\include\WindowsAppRuntimeAutoInitializer.cpp" />
|
||||
<!-- Re-add once, but disable PCH because the SDK file doesn't include our pch.h -->
|
||||
<ClCompile Include="$(PkgMicrosoft_WindowsAppSDK)\include\WindowsAppRuntimeAutoInitializer.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Target Name="RemoveManagedWebView2CoreFromNativeOutDir" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<_ToDelete Include="$(OutDir)Microsoft.Web.WebView2.Core.dll" />
|
||||
</ItemGroup>
|
||||
<Delete Files="@(_ToDelete)" Condition="Exists('%(Identity)')" />
|
||||
</Target>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Windows.CppWinRT.2.0.240111.5\\build\\native\\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.Web.WebView2.1.0.2903.40\\build\\native\\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Base.1.8.250831001\\build\\native\\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\\build\\native\\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\\build\\native\\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\\build\\native\\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\\build\\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\\..\\..\\..\\packages\\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\\build\\native\\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -18,7 +18,7 @@ namespace
|
||||
const wchar_t JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE[] = L"do_not_activate_on_game_mode";
|
||||
const wchar_t JSON_KEY_BACKGROUND_COLOR[] = L"background_color";
|
||||
const wchar_t JSON_KEY_SPOTLIGHT_COLOR[] = L"spotlight_color";
|
||||
const wchar_t JSON_KEY_OVERLAY_OPACITY[] = L"overlay_opacity";
|
||||
const wchar_t JSON_KEY_OVERLAY_OPACITY[] = L"overlay_opacity"; // legacy only (migrated into color alpha)
|
||||
const wchar_t JSON_KEY_SPOTLIGHT_RADIUS[] = L"spotlight_radius";
|
||||
const wchar_t JSON_KEY_ANIMATION_DURATION_MS[] = L"animation_duration_ms";
|
||||
const wchar_t JSON_KEY_SPOTLIGHT_INITIAL_ZOOM[] = L"spotlight_initial_zoom";
|
||||
@@ -204,6 +204,22 @@ void FindMyMouse::init_settings()
|
||||
}
|
||||
}
|
||||
|
||||
inline static uint8_t LegacyOpacityToAlpha(int overlayOpacityPercent)
|
||||
{
|
||||
if (overlayOpacityPercent < 0)
|
||||
{
|
||||
return 255; // fallback: fully opaque
|
||||
}
|
||||
|
||||
if (overlayOpacityPercent > 100)
|
||||
{
|
||||
overlayOpacityPercent = 100;
|
||||
}
|
||||
|
||||
// Round to nearest integer (0<>255)
|
||||
return static_cast<uint8_t>((overlayOpacityPercent * 255 + 50) / 100);
|
||||
}
|
||||
|
||||
void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
{
|
||||
auto settingsObject = settings.get_raw_json();
|
||||
@@ -224,14 +240,13 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
}
|
||||
else
|
||||
{
|
||||
findMyMouseSettings.activationMethod = static_cast<FindMyMouseActivationMethod>(value);
|
||||
}
|
||||
findMyMouseSettings.activationMethod = static_cast<FindMyMouseActivationMethod>(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Invalid Activation Method value");
|
||||
}
|
||||
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -255,19 +270,49 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
{
|
||||
Logger::warn("Failed to get 'do not activate on game mode' setting");
|
||||
}
|
||||
// Colors + legacy overlay opacity migration
|
||||
// Desired behavior:
|
||||
// - Old schema: colors stored as RGB (no alpha) + separate overlay_opacity (0-100). We should migrate by applying that opacity as alpha.
|
||||
// - New schema: colors stored as ARGB (alpha embedded). Ignore overlay_opacity even if still present.
|
||||
int legacyOverlayOpacity = -1;
|
||||
bool backgroundColorHadExplicitAlpha = false;
|
||||
bool spotlightColorHadExplicitAlpha = false;
|
||||
try
|
||||
{
|
||||
// Parse background color
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_BACKGROUND_COLOR);
|
||||
auto backgroundColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t r, g, b;
|
||||
if (!checkValidRGB(backgroundColor, &r, &g, &b))
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_OVERLAY_OPACITY);
|
||||
int value = static_cast<int>(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
|
||||
if (value >= 0 && value <= 100)
|
||||
{
|
||||
Logger::error("Background color RGB value is invalid. Will use default value");
|
||||
legacyOverlayOpacity = value;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// overlay_opacity may not exist anymore
|
||||
}
|
||||
try
|
||||
{
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_BACKGROUND_COLOR);
|
||||
auto backgroundColorStr = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t a = 255, r, g, b;
|
||||
bool parsed = false;
|
||||
if (checkValidARGB(backgroundColorStr, &a, &r, &g, &b))
|
||||
{
|
||||
parsed = true;
|
||||
backgroundColorHadExplicitAlpha = true; // New schema with alpha present
|
||||
}
|
||||
else if (checkValidRGB(backgroundColorStr, &r, &g, &b))
|
||||
{
|
||||
a = LegacyOpacityToAlpha(legacyOverlayOpacity);
|
||||
parsed = true; // Old schema (no alpha component)
|
||||
}
|
||||
if (parsed)
|
||||
{
|
||||
findMyMouseSettings.backgroundColor = winrt::Windows::UI::ColorHelper::FromArgb(a, r, g, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
findMyMouseSettings.backgroundColor = winrt::Windows::UI::ColorHelper::FromArgb(255, r, g, b);
|
||||
Logger::error("Background color value is invalid. Will use default");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -276,17 +321,27 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse spotlight color
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SPOTLIGHT_COLOR);
|
||||
auto spotlightColor = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t r, g, b;
|
||||
if (!checkValidRGB(spotlightColor, &r, &g, &b))
|
||||
auto spotlightColorStr = (std::wstring)jsonPropertiesObject.GetNamedString(JSON_KEY_VALUE);
|
||||
uint8_t a = 255, r, g, b;
|
||||
bool parsed = false;
|
||||
if (checkValidARGB(spotlightColorStr, &a, &r, &g, &b))
|
||||
{
|
||||
Logger::error("Spotlight color RGB value is invalid. Will use default value");
|
||||
parsed = true;
|
||||
spotlightColorHadExplicitAlpha = true;
|
||||
}
|
||||
else if (checkValidRGB(spotlightColorStr, &r, &g, &b))
|
||||
{
|
||||
a = LegacyOpacityToAlpha(legacyOverlayOpacity);
|
||||
parsed = true;
|
||||
}
|
||||
if (parsed)
|
||||
{
|
||||
findMyMouseSettings.spotlightColor = winrt::Windows::UI::ColorHelper::FromArgb(a, r, g, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
findMyMouseSettings.spotlightColor = winrt::Windows::UI::ColorHelper::FromArgb(255, r, g, b);
|
||||
Logger::error("Spotlight color value is invalid. Will use default");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
@@ -294,24 +349,6 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
Logger::warn("Failed to initialize spotlight color from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse Overlay Opacity
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_OVERLAY_OPACITY);
|
||||
int value = static_cast<int>(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
|
||||
if (value >= 0)
|
||||
{
|
||||
findMyMouseSettings.overlayOpacity = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Invalid Overlay Opacity value");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to initialize Overlay Opacity from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse Spotlight Radius
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SPOTLIGHT_RADIUS);
|
||||
@@ -492,7 +529,6 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
m_findMyMouseSettings = findMyMouseSettings;
|
||||
}
|
||||
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new FindMyMouse();
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
</packages>
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -5,15 +5,22 @@
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <hIdUsage.h>
|
||||
// Required for IUnknown and DECLARE_INTERFACE_* used by interop headers
|
||||
#include <Unknwn.h>
|
||||
|
||||
#ifdef COMPOSITION
|
||||
#include <windows.ui.composition.interop.h>
|
||||
#include <DispatcherQueue.h>
|
||||
#include <winrt/Windows.System.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.UI.Composition.Desktop.h>
|
||||
#include <winrt/Microsoft.UI.Composition.h>
|
||||
#include <winrt/Microsoft.UI.h>
|
||||
#include <winrt/Windows.UI.h>
|
||||
#endif
|
||||
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,284 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
|
||||
// <summary>
|
||||
// Initialization and clean up.
|
||||
// </summary>
|
||||
// <history>
|
||||
// 2008 created by Truong Do (ductdo).
|
||||
// 2009-... modified by Truong Do (TruongDo).
|
||||
// 2023- Included in PowerToys.
|
||||
// </history>
|
||||
using Microsoft.Win32;
|
||||
using MouseWithoutBorders.Class;
|
||||
using MouseWithoutBorders.Core;
|
||||
using MouseWithoutBorders.Form;
|
||||
using Windows.UI.Input.Preview.Injection;
|
||||
|
||||
using Thread = MouseWithoutBorders.Core.Thread;
|
||||
|
||||
namespace MouseWithoutBorders
|
||||
{
|
||||
internal partial class Common
|
||||
{
|
||||
private static bool initDone;
|
||||
internal static int REOPEN_WHEN_WSAECONNRESET = -10054;
|
||||
internal static int REOPEN_WHEN_HOTKEY = -10055;
|
||||
internal static int PleaseReopenSocket;
|
||||
internal static bool ReopenSocketDueToReadError;
|
||||
|
||||
internal static DateTime LastResumeSuspendTime { get; set; } = DateTime.UtcNow;
|
||||
|
||||
internal static bool InitDone
|
||||
{
|
||||
get => Common.initDone;
|
||||
set => Common.initDone = value;
|
||||
}
|
||||
|
||||
internal static void UpdateMachineTimeAndID()
|
||||
{
|
||||
Common.MachineName = Common.MachineName.Trim();
|
||||
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
|
||||
}
|
||||
|
||||
private static void InitializeMachinePoolFromSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
MachineInf[] info = MachinePoolHelpers.LoadMachineInfoFromMachinePoolStringSetting(Setting.Values.MachinePoolString);
|
||||
for (int i = 0; i < info.Length; i++)
|
||||
{
|
||||
info[i].Name = info[i].Name.Trim();
|
||||
}
|
||||
|
||||
MachineStuff.MachinePool.Initialize(info);
|
||||
MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex);
|
||||
MachineStuff.MachinePool.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetupMachineNameAndID()
|
||||
{
|
||||
try
|
||||
{
|
||||
GetMachineName();
|
||||
DesMachineID = MachineStuff.NewDesMachineID = MachineID;
|
||||
|
||||
// MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test
|
||||
InitializeMachinePoolFromSettings();
|
||||
|
||||
Common.MachineName = Common.MachineName.Trim();
|
||||
_ = MachineStuff.MachinePool.LearnMachine(Common.MachineName);
|
||||
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
|
||||
|
||||
MachineStuff.UpdateMachinePoolStringSetting();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(e);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
_ = Helper.GetUserName();
|
||||
Common.GeneratedKey = true;
|
||||
|
||||
try
|
||||
{
|
||||
Common.MyKey = Setting.Values.MyKey;
|
||||
int tmp = Setting.Values.MyKeyDaysToExpire;
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
Common.KeyCorrupted = true;
|
||||
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
|
||||
Logger.Log(e.Message);
|
||||
}
|
||||
catch (CryptographicException e)
|
||||
{
|
||||
Common.KeyCorrupted = true;
|
||||
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
|
||||
Logger.Log(e.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
InputSimulation.Injector = InputInjector.TryCreate();
|
||||
if (InputSimulation.Injector != null)
|
||||
{
|
||||
InputSimulation.MoveMouseRelative(0, 0);
|
||||
NativeMethods.InjectMouseInputAvailable = true;
|
||||
}
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
NativeMethods.InjectMouseInputAvailable = false;
|
||||
Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false");
|
||||
}
|
||||
|
||||
bool dummy = Setting.Values.DrawMouseEx;
|
||||
Is64bitOS = IntPtr.Size == 8;
|
||||
tcpPort = Setting.Values.TcpPort;
|
||||
GetScreenConfig();
|
||||
PackageSent = new PackageMonitor(0);
|
||||
PackageReceived = new PackageMonitor(0);
|
||||
SetupMachineNameAndID();
|
||||
InitEncryption();
|
||||
CreateHelperThreads();
|
||||
|
||||
SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
|
||||
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
|
||||
SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
|
||||
PleaseReopenSocket = 9;
|
||||
/* TODO: Telemetry for the matrix? */
|
||||
}
|
||||
|
||||
private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
Helper.WndProcCounter++;
|
||||
|
||||
if (e.Mode is PowerModes.Resume or PowerModes.Suspend)
|
||||
{
|
||||
Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information);
|
||||
LastResumeSuspendTime = DateTime.UtcNow;
|
||||
MachineStuff.SwitchToMultipleMode(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateHelperThreads()
|
||||
{
|
||||
// NOTE(@yuyoyuppe): service crashes while trying to obtain this info, disabling.
|
||||
/*
|
||||
Thread watchDogThread = new(new ThreadStart(WatchDogThread), nameof(WatchDogThread));
|
||||
watchDogThread.Priority = ThreadPriority.Highest;
|
||||
watchDogThread.Start();
|
||||
*/
|
||||
|
||||
helper = new Thread(new ThreadStart(Helper.HelperThread), "Helper Thread");
|
||||
helper.SetApartmentState(ApartmentState.STA);
|
||||
helper.Start();
|
||||
}
|
||||
|
||||
private static void AskHelperThreadsToExit(int waitTime)
|
||||
{
|
||||
Helper.signalHelperToExit = true;
|
||||
Helper.signalWatchDogToExit = true;
|
||||
_ = EvSwitch.Set();
|
||||
|
||||
int c = 0;
|
||||
if (helper != null && c < waitTime)
|
||||
{
|
||||
while (Helper.signalHelperToExit)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
helper = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
SendByeBye();
|
||||
|
||||
// UnhookClipboard();
|
||||
AskHelperThreadsToExit(500);
|
||||
MainForm.NotifyIcon.Visible = false;
|
||||
MainForm.NotifyIcon.Dispose();
|
||||
CloseAllFormsAndHooks();
|
||||
|
||||
DoSomethingInUIThread(() =>
|
||||
{
|
||||
Sk?.Close(true);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static long lastReleaseAllKeysCall;
|
||||
|
||||
internal static void ReleaseAllKeys()
|
||||
{
|
||||
if (Math.Abs(GetTick() - lastReleaseAllKeysCall) < 2000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastReleaseAllKeysCall = GetTick();
|
||||
|
||||
KEYBDDATA kd;
|
||||
kd.dwFlags = (int)LLKHF.UP;
|
||||
|
||||
VK[] keys = new VK[]
|
||||
{
|
||||
VK.LSHIFT, VK.LCONTROL, VK.LMENU, VK.LWIN, VK.RSHIFT,
|
||||
VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL,
|
||||
};
|
||||
|
||||
Logger.LogDebug("***** ReleaseAllKeys has been called! *****:");
|
||||
|
||||
foreach (VK vk in keys)
|
||||
{
|
||||
if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0)
|
||||
{
|
||||
Logger.LogDebug(vk.ToString() + " is down, release it...");
|
||||
Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl)
|
||||
kd.wVk = (int)vk;
|
||||
InputSimulation.SendKey(kd);
|
||||
Hook?.ResetLastSwitchKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
|
||||
{
|
||||
Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture));
|
||||
Helper.WndProcCounter++;
|
||||
ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable);
|
||||
}
|
||||
|
||||
private static void ScheduleReopenSocketsDueToNetworkChanges(bool closeSockets = true)
|
||||
{
|
||||
if (closeSockets)
|
||||
{
|
||||
// Slept/hibernated machine may still have the sockets' status as Connected:( (unchanged) so it would not re-connect after a timeout when waking up.
|
||||
// Closing the sockets when it is going to sleep/hibernate will trigger the reconnection faster when it wakes up.
|
||||
DoSomethingInUIThread(
|
||||
() =>
|
||||
{
|
||||
SocketStuff s = Sk;
|
||||
Sk = null;
|
||||
s?.Close(false);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
if (!Common.IsMyDesktopActive())
|
||||
{
|
||||
PleaseReopenSocket = 0;
|
||||
}
|
||||
else if (PleaseReopenSocket != 10)
|
||||
{
|
||||
PleaseReopenSocket = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
internal static string ActiveDesktop => Common.activeDesktop;
|
||||
|
||||
private static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
|
||||
internal static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
GetScreenConfig();
|
||||
}
|
||||
@@ -340,7 +340,7 @@ namespace MouseWithoutBorders
|
||||
Setting.Values.LastX = JUST_GOT_BACK_FROM_SCREEN_SAVER;
|
||||
if (cleanupIfExit)
|
||||
{
|
||||
Common.Cleanup();
|
||||
InitAndCleanup.Cleanup();
|
||||
}
|
||||
|
||||
Process.GetCurrentProcess().KillProcess();
|
||||
|
||||
@@ -33,6 +33,7 @@ using MouseWithoutBorders.Class;
|
||||
using MouseWithoutBorders.Core;
|
||||
using MouseWithoutBorders.Exceptions;
|
||||
|
||||
using Clipboard = MouseWithoutBorders.Core.Clipboard;
|
||||
using Thread = MouseWithoutBorders.Core.Thread;
|
||||
|
||||
// Log is enough
|
||||
@@ -90,8 +91,8 @@ namespace MouseWithoutBorders
|
||||
private static FrmMatrix matrixForm;
|
||||
private static FrmInputCallback inputCallbackForm;
|
||||
private static FrmAbout aboutForm;
|
||||
private static Thread helper;
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
|
||||
internal static Thread helper;
|
||||
internal static int screenWidth;
|
||||
internal static int screenHeight;
|
||||
#pragma warning restore SA1307
|
||||
@@ -121,7 +122,9 @@ namespace MouseWithoutBorders
|
||||
internal static int switchCount;
|
||||
#pragma warning restore SA1307
|
||||
private static long lastReconnectByHotKeyTime;
|
||||
private static int tcpPort;
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case names
|
||||
internal static int tcpPort;
|
||||
#pragma warning restore SA1307
|
||||
private static bool secondOpenSocketTry;
|
||||
private static string binaryName;
|
||||
|
||||
@@ -210,7 +213,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
internal static bool Is64bitOS
|
||||
{
|
||||
get; private set;
|
||||
get; set;
|
||||
|
||||
// set { Common.is64bitOS = value; }
|
||||
}
|
||||
@@ -611,7 +614,7 @@ namespace MouseWithoutBorders
|
||||
}
|
||||
* */
|
||||
|
||||
private static void SendByeBye()
|
||||
internal static void SendByeBye()
|
||||
{
|
||||
Logger.LogDebug($"{nameof(SendByeBye)}");
|
||||
SendPackage(ID.ALL, PackageType.ByeBye);
|
||||
@@ -725,7 +728,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
internal static void SendImage(string machine, string file)
|
||||
{
|
||||
LastDragDropFile = file;
|
||||
Clipboard.LastDragDropFile = file;
|
||||
|
||||
// Send ClipboardCapture
|
||||
if (machine.Equals("All", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -744,7 +747,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
internal static void SendImage(ID src, string file)
|
||||
{
|
||||
LastDragDropFile = file;
|
||||
Clipboard.LastDragDropFile = file;
|
||||
|
||||
// Send ClipboardCapture
|
||||
SendPackage(src, PackageType.ClipboardCapture);
|
||||
@@ -1291,7 +1294,7 @@ namespace MouseWithoutBorders
|
||||
});
|
||||
}
|
||||
|
||||
private static string GetMyStorageDir()
|
||||
internal static string GetMyStorageDir()
|
||||
{
|
||||
string st = string.Empty;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ using MouseWithoutBorders.Core;
|
||||
|
||||
using SystemClipboard = System.Windows.Forms.Clipboard;
|
||||
#if !MM_HELPER
|
||||
using Clipboard = MouseWithoutBorders.Core.Clipboard;
|
||||
using Thread = MouseWithoutBorders.Core.Thread;
|
||||
#endif
|
||||
|
||||
@@ -159,7 +160,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
public void SendClipboardData(ByteArrayOrString data, bool isFilePath)
|
||||
{
|
||||
_ = Common.CheckClipboardEx(data, isFilePath);
|
||||
_ = Clipboard.CheckClipboardEx(data, isFilePath);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -579,7 +579,7 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
Common.ShowToolTip("Reconnecting...", 2000);
|
||||
Common.LastReconnectByHotKeyTime = Common.GetTick();
|
||||
Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY;
|
||||
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -632,7 +632,7 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
// Common.DoSomethingInUIThread(delegate()
|
||||
{
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
}
|
||||
|
||||
// );
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
ResetModifiersState(Setting.Values.HotKeyLockMachine);
|
||||
eatKey = true;
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
_ = NativeMethods.LockWorkStation();
|
||||
}
|
||||
}
|
||||
@@ -439,7 +439,7 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
ctrlDown = altDown = false;
|
||||
eatKey = true;
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -449,7 +449,7 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
winDown = false;
|
||||
eatKey = true;
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
uint rv = NativeMethods.LockWorkStation();
|
||||
Logger.LogDebug("LockWorkStation returned " + rv.ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace MouseWithoutBorders.Class
|
||||
_ = Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
Common.Init();
|
||||
InitAndCleanup.Init();
|
||||
Core.Helper.WndProcCounter++;
|
||||
|
||||
var formScreen = new FrmScreen();
|
||||
@@ -314,7 +314,7 @@ namespace MouseWithoutBorders.Class
|
||||
MachineStuff.UpdateMachinePoolStringSetting();
|
||||
|
||||
SocketStuff.InvalidKeyFound = false;
|
||||
Common.ReopenSocketDueToReadError = true;
|
||||
InitAndCleanup.ReopenSocketDueToReadError = true;
|
||||
Common.ReopenSockets(true);
|
||||
MachineStuff.SendMachineMatrix();
|
||||
|
||||
@@ -340,7 +340,7 @@ namespace MouseWithoutBorders.Class
|
||||
public void Reconnect()
|
||||
{
|
||||
SocketStuff.InvalidKeyFound = false;
|
||||
Common.ReopenSocketDueToReadError = true;
|
||||
InitAndCleanup.ReopenSocketDueToReadError = true;
|
||||
Common.ReopenSockets(true);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
@@ -397,7 +397,7 @@ namespace MouseWithoutBorders.Class
|
||||
using var asyncFlowControl = ExecutionContext.SuppressFlow();
|
||||
|
||||
Common.InputCallbackThreadID = Thread.CurrentThread.ManagedThreadId;
|
||||
while (!Common.InitDone)
|
||||
while (!InitAndCleanup.InitDone)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace MouseWithoutBorders.Class
|
||||
if (shouldReopenSockets)
|
||||
{
|
||||
SocketStuff.InvalidKeyFound = false;
|
||||
Common.ReopenSocketDueToReadError = true;
|
||||
InitAndCleanup.ReopenSocketDueToReadError = true;
|
||||
Common.ReopenSockets(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ using MouseWithoutBorders.Core;
|
||||
// </history>
|
||||
using MouseWithoutBorders.Exceptions;
|
||||
|
||||
using Clipboard = MouseWithoutBorders.Core.Clipboard;
|
||||
using Thread = MouseWithoutBorders.Core.Thread;
|
||||
|
||||
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[])", Justification = "Dotnet port with style preservation")]
|
||||
@@ -281,7 +282,7 @@ namespace MouseWithoutBorders.Class
|
||||
* */
|
||||
|
||||
Common.GetMachineName(); // IPs might have been changed
|
||||
Common.UpdateMachineTimeAndID();
|
||||
InitAndCleanup.UpdateMachineTimeAndID();
|
||||
|
||||
Logger.LogDebug("Creating sockets...");
|
||||
|
||||
@@ -308,7 +309,7 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning);
|
||||
Program.StartService();
|
||||
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
|
||||
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1248,7 +1249,7 @@ namespace MouseWithoutBorders.Class
|
||||
// WSAECONNRESET
|
||||
if (e is ExpectedSocketException se && se.ShouldReconnect)
|
||||
{
|
||||
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
|
||||
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
|
||||
Logger.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}");
|
||||
}
|
||||
}
|
||||
@@ -1306,7 +1307,7 @@ namespace MouseWithoutBorders.Class
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
|
||||
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
|
||||
UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed);
|
||||
currentSocket.Close();
|
||||
Logger.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}");
|
||||
@@ -1353,10 +1354,10 @@ namespace MouseWithoutBorders.Class
|
||||
* In this case, we should give ONE try to reconnect.
|
||||
*/
|
||||
|
||||
if (Common.ReopenSocketDueToReadError)
|
||||
if (InitAndCleanup.ReopenSocketDueToReadError)
|
||||
{
|
||||
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
|
||||
Common.ReopenSocketDueToReadError = false;
|
||||
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
|
||||
InitAndCleanup.ReopenSocketDueToReadError = false;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1641,7 +1642,7 @@ namespace MouseWithoutBorders.Class
|
||||
|
||||
bool clientPushData = true;
|
||||
ClipboardPostAction postAction = ClipboardPostAction.Other;
|
||||
bool handShaken = Common.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction);
|
||||
bool handShaken = Clipboard.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction);
|
||||
|
||||
if (!handShaken)
|
||||
{
|
||||
@@ -1656,7 +1657,7 @@ namespace MouseWithoutBorders.Class
|
||||
|
||||
if (clientPushData)
|
||||
{
|
||||
Common.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}");
|
||||
Clipboard.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1680,23 +1681,23 @@ namespace MouseWithoutBorders.Class
|
||||
const int CLOSE_TIMEOUT = 10;
|
||||
byte[] header = new byte[1024];
|
||||
string headerString = string.Empty;
|
||||
if (Common.LastDragDropFile != null)
|
||||
if (Clipboard.LastDragDropFile != null)
|
||||
{
|
||||
string fileName = null;
|
||||
|
||||
if (!Launch.ImpersonateLoggedOnUserAndDoSomething(() =>
|
||||
{
|
||||
if (!File.Exists(Common.LastDragDropFile))
|
||||
if (!File.Exists(Clipboard.LastDragDropFile))
|
||||
{
|
||||
headerString = Directory.Exists(Common.LastDragDropFile)
|
||||
? $"{0}*{Common.LastDragDropFile} - Folder is not supported, zip it first!"
|
||||
: Common.LastDragDropFile.Contains("- File too big")
|
||||
? $"{0}*{Common.LastDragDropFile}"
|
||||
: $"{0}*{Common.LastDragDropFile} not found!";
|
||||
headerString = Directory.Exists(Clipboard.LastDragDropFile)
|
||||
? $"{0}*{Clipboard.LastDragDropFile} - Folder is not supported, zip it first!"
|
||||
: Clipboard.LastDragDropFile.Contains("- File too big")
|
||||
? $"{0}*{Clipboard.LastDragDropFile}"
|
||||
: $"{0}*{Clipboard.LastDragDropFile} not found!";
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName = Common.LastDragDropFile;
|
||||
fileName = Clipboard.LastDragDropFile;
|
||||
headerString = $"{new FileInfo(fileName).Length}*{fileName}";
|
||||
}
|
||||
}))
|
||||
@@ -1739,11 +1740,11 @@ namespace MouseWithoutBorders.Class
|
||||
Logger.Log(log);
|
||||
}
|
||||
}
|
||||
else if (!Common.IsClipboardDataImage && Common.LastClipboardData != null)
|
||||
else if (!Clipboard.IsClipboardDataImage && Clipboard.LastClipboardData != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] data = Common.LastClipboardData;
|
||||
byte[] data = Clipboard.LastClipboardData;
|
||||
|
||||
headerString = $"{data.Length}*{"text"}";
|
||||
Common.GetBytesU(headerString).CopyTo(header, 0);
|
||||
@@ -1773,9 +1774,9 @@ namespace MouseWithoutBorders.Class
|
||||
Logger.Log(log);
|
||||
}
|
||||
}
|
||||
else if (Common.LastClipboardData != null && Common.LastClipboardData.Length > 0)
|
||||
else if (Clipboard.LastClipboardData != null && Clipboard.LastClipboardData.Length > 0)
|
||||
{
|
||||
byte[] data = Common.LastClipboardData;
|
||||
byte[] data = Clipboard.LastClipboardData;
|
||||
|
||||
headerString = $"{data.Length}*{"image"}";
|
||||
Common.GetBytesU(headerString).CopyTo(header, 0);
|
||||
@@ -1984,8 +1985,8 @@ namespace MouseWithoutBorders.Class
|
||||
{
|
||||
tcp = null;
|
||||
Setting.Values.MachineId = Common.Ran.Next();
|
||||
Common.UpdateMachineTimeAndID();
|
||||
Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY;
|
||||
InitAndCleanup.UpdateMachineTimeAndID();
|
||||
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY;
|
||||
|
||||
Logger.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information);
|
||||
}
|
||||
|
||||
1155
src/modules/MouseWithoutBorders/App/Core/Clipboard.cs
Normal file
1155
src/modules/MouseWithoutBorders/App/Core/Clipboard.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -83,7 +83,7 @@ internal static class DragDrop
|
||||
if (wParam == Common.WM_RBUTTONUP && IsDropping)
|
||||
{
|
||||
IsDropping = false;
|
||||
Common.LastIDWithClipboardData = ID.NONE;
|
||||
Clipboard.LastIDWithClipboardData = ID.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ internal static class DragDrop
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dragFileName) && (File.Exists(dragFileName) || Directory.Exists(dragFileName)))
|
||||
{
|
||||
Common.LastDragDropFile = dragFileName;
|
||||
Clipboard.LastDragDropFile = dragFileName;
|
||||
/*
|
||||
* possibleDropMachineID is used as desID sent in DragDropStep06();
|
||||
* */
|
||||
@@ -270,7 +270,7 @@ internal static class DragDrop
|
||||
else
|
||||
{
|
||||
IsDragging = false;
|
||||
Common.LastIDWithClipboardData = ID.NONE;
|
||||
Clipboard.LastIDWithClipboardData = ID.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,7 +280,7 @@ internal static class DragDrop
|
||||
Logger.LogDebug("DragDropStep10: Hide the form and get data...");
|
||||
IsDropping = false;
|
||||
IsDragging = false;
|
||||
Common.LastIDWithClipboardData = ID.NONE;
|
||||
Clipboard.LastIDWithClipboardData = ID.NONE;
|
||||
|
||||
Common.DoSomethingInUIThread(() =>
|
||||
{
|
||||
@@ -288,7 +288,7 @@ internal static class DragDrop
|
||||
});
|
||||
|
||||
PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersDragAndDropEvent());
|
||||
Common.GetRemoteClipboard("desktop");
|
||||
Clipboard.GetRemoteClipboard("desktop");
|
||||
}
|
||||
|
||||
internal static void DragDropStep11()
|
||||
@@ -298,8 +298,8 @@ internal static class DragDrop
|
||||
IsDropping = false;
|
||||
IsDragging = false;
|
||||
DragMachine = (ID)1;
|
||||
Common.LastIDWithClipboardData = ID.NONE;
|
||||
Common.LastDragDropFile = null;
|
||||
Clipboard.LastIDWithClipboardData = ID.NONE;
|
||||
Clipboard.LastDragDropFile = null;
|
||||
MouseDown = false;
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ internal static class DragDrop
|
||||
{
|
||||
Logger.LogDebug("DragDropStep12: ClipboardDragDropEnd received");
|
||||
IsDropping = false;
|
||||
Common.LastIDWithClipboardData = ID.NONE;
|
||||
Clipboard.LastIDWithClipboardData = ID.NONE;
|
||||
|
||||
Common.DoSomethingInUIThread(() =>
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ internal static class Event
|
||||
// if they are, check that there is no application running in fullscreen mode before switching.
|
||||
if (!p.IsEmpty && Common.IsEasyMouseSwitchAllowed())
|
||||
{
|
||||
Common.HasSwitchedMachineSinceLastCopy = true;
|
||||
Clipboard.HasSwitchedMachineSinceLastCopy = true;
|
||||
|
||||
Logger.LogDebug(string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
@@ -218,10 +218,10 @@ internal static class Event
|
||||
|
||||
if (MachineStuff.desMachineID == Common.MachineID)
|
||||
{
|
||||
if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT)
|
||||
if (Common.GetTick() - Clipboard.clipboardCopiedTime < Clipboard.BIG_CLIPBOARD_DATA_TIMEOUT)
|
||||
{
|
||||
Common.clipboardCopiedTime = 0;
|
||||
Common.GetRemoteClipboard("PrepareToSwitchToMachine");
|
||||
Clipboard.clipboardCopiedTime = 0;
|
||||
Clipboard.GetRemoteClipboard("PrepareToSwitchToMachine");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -119,7 +119,7 @@ internal static class Helper
|
||||
|
||||
if (MachineStuff.NewDesMachineID == Common.MachineID)
|
||||
{
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,7 +317,7 @@ internal static class Helper
|
||||
Common.GetInputDesktop(),
|
||||
0);
|
||||
|
||||
Common.HasSwitchedMachineSinceLastCopy = true;
|
||||
Clipboard.HasSwitchedMachineSinceLastCopy = true;
|
||||
|
||||
// Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0);
|
||||
var processes = Process.GetProcessesByName(HelperProcessName);
|
||||
|
||||
278
src/modules/MouseWithoutBorders/App/Core/InitAndCleanup.cs
Normal file
278
src/modules/MouseWithoutBorders/App/Core/InitAndCleanup.cs
Normal file
@@ -0,0 +1,278 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
|
||||
using Microsoft.Win32;
|
||||
using MouseWithoutBorders.Class;
|
||||
using Windows.UI.Input.Preview.Injection;
|
||||
|
||||
// <summary>
|
||||
// Initialization and clean up.
|
||||
// </summary>
|
||||
// <history>
|
||||
// 2008 created by Truong Do (ductdo).
|
||||
// 2009-... modified by Truong Do (TruongDo).
|
||||
// 2023- Included in PowerToys.
|
||||
// </history>
|
||||
namespace MouseWithoutBorders.Core;
|
||||
|
||||
internal static class InitAndCleanup
|
||||
{
|
||||
private static bool initDone;
|
||||
internal static int REOPEN_WHEN_WSAECONNRESET = -10054;
|
||||
internal static int REOPEN_WHEN_HOTKEY = -10055;
|
||||
internal static int PleaseReopenSocket;
|
||||
internal static bool ReopenSocketDueToReadError;
|
||||
|
||||
private static DateTime LastResumeSuspendTime { get; set; } = DateTime.UtcNow;
|
||||
|
||||
internal static bool InitDone
|
||||
{
|
||||
get => InitAndCleanup.initDone;
|
||||
set => InitAndCleanup.initDone = value;
|
||||
}
|
||||
|
||||
internal static void UpdateMachineTimeAndID()
|
||||
{
|
||||
Common.MachineName = Common.MachineName.Trim();
|
||||
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
|
||||
}
|
||||
|
||||
private static void InitializeMachinePoolFromSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
MachineInf[] info = MachinePoolHelpers.LoadMachineInfoFromMachinePoolStringSetting(Setting.Values.MachinePoolString);
|
||||
for (int i = 0; i < info.Length; i++)
|
||||
{
|
||||
info[i].Name = info[i].Name.Trim();
|
||||
}
|
||||
|
||||
MachineStuff.MachinePool.Initialize(info);
|
||||
MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(ex);
|
||||
MachineStuff.MachinePool.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetupMachineNameAndID()
|
||||
{
|
||||
try
|
||||
{
|
||||
Common.GetMachineName();
|
||||
Common.DesMachineID = MachineStuff.NewDesMachineID = Common.MachineID;
|
||||
|
||||
// MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test
|
||||
InitializeMachinePoolFromSettings();
|
||||
|
||||
Common.MachineName = Common.MachineName.Trim();
|
||||
_ = MachineStuff.MachinePool.LearnMachine(Common.MachineName);
|
||||
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
|
||||
|
||||
MachineStuff.UpdateMachinePoolStringSetting();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(e);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
_ = Helper.GetUserName();
|
||||
Common.GeneratedKey = true;
|
||||
|
||||
try
|
||||
{
|
||||
Common.MyKey = Setting.Values.MyKey;
|
||||
int tmp = Setting.Values.MyKeyDaysToExpire;
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
Common.KeyCorrupted = true;
|
||||
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
|
||||
Logger.Log(e.Message);
|
||||
}
|
||||
catch (CryptographicException e)
|
||||
{
|
||||
Common.KeyCorrupted = true;
|
||||
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
|
||||
Logger.Log(e.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
InputSimulation.Injector = InputInjector.TryCreate();
|
||||
if (InputSimulation.Injector != null)
|
||||
{
|
||||
InputSimulation.MoveMouseRelative(0, 0);
|
||||
NativeMethods.InjectMouseInputAvailable = true;
|
||||
}
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
NativeMethods.InjectMouseInputAvailable = false;
|
||||
Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false");
|
||||
}
|
||||
|
||||
bool dummy = Setting.Values.DrawMouseEx;
|
||||
Common.Is64bitOS = IntPtr.Size == 8;
|
||||
Common.tcpPort = Setting.Values.TcpPort;
|
||||
Common.GetScreenConfig();
|
||||
Common.PackageSent = new PackageMonitor(0);
|
||||
Common.PackageReceived = new PackageMonitor(0);
|
||||
SetupMachineNameAndID();
|
||||
Common.InitEncryption();
|
||||
CreateHelperThreads();
|
||||
|
||||
SystemEvents.DisplaySettingsChanged += new EventHandler(Common.SystemEvents_DisplaySettingsChanged);
|
||||
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
|
||||
SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
|
||||
PleaseReopenSocket = 9;
|
||||
/* TODO: Telemetry for the matrix? */
|
||||
}
|
||||
|
||||
private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
||||
{
|
||||
Helper.WndProcCounter++;
|
||||
|
||||
if (e.Mode is PowerModes.Resume or PowerModes.Suspend)
|
||||
{
|
||||
Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information);
|
||||
LastResumeSuspendTime = DateTime.UtcNow;
|
||||
MachineStuff.SwitchToMultipleMode(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateHelperThreads()
|
||||
{
|
||||
// NOTE(@yuyoyuppe): service crashes while trying to obtain this info, disabling.
|
||||
/*
|
||||
Thread watchDogThread = new(new ThreadStart(WatchDogThread), nameof(WatchDogThread));
|
||||
watchDogThread.Priority = ThreadPriority.Highest;
|
||||
watchDogThread.Start();
|
||||
*/
|
||||
|
||||
Common.helper = new Thread(new ThreadStart(Helper.HelperThread), "Helper Thread");
|
||||
Common.helper.SetApartmentState(ApartmentState.STA);
|
||||
Common.helper.Start();
|
||||
}
|
||||
|
||||
private static void AskHelperThreadsToExit(int waitTime)
|
||||
{
|
||||
Helper.signalHelperToExit = true;
|
||||
Helper.signalWatchDogToExit = true;
|
||||
_ = Common.EvSwitch.Set();
|
||||
|
||||
int c = 0;
|
||||
if (Common.helper != null && c < waitTime)
|
||||
{
|
||||
while (Helper.signalHelperToExit)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
Common.helper = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
Common.SendByeBye();
|
||||
|
||||
// UnhookClipboard();
|
||||
AskHelperThreadsToExit(500);
|
||||
Common.MainForm.NotifyIcon.Visible = false;
|
||||
Common.MainForm.NotifyIcon.Dispose();
|
||||
Common.CloseAllFormsAndHooks();
|
||||
|
||||
Common.DoSomethingInUIThread(() =>
|
||||
{
|
||||
Common.Sk?.Close(true);
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static long lastReleaseAllKeysCall;
|
||||
|
||||
internal static void ReleaseAllKeys()
|
||||
{
|
||||
if (Math.Abs(Common.GetTick() - lastReleaseAllKeysCall) < 2000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastReleaseAllKeysCall = Common.GetTick();
|
||||
|
||||
KEYBDDATA kd;
|
||||
kd.dwFlags = (int)Common.LLKHF.UP;
|
||||
|
||||
VK[] keys = new VK[]
|
||||
{
|
||||
VK.LSHIFT, VK.LCONTROL, VK.LMENU, VK.LWIN, VK.RSHIFT,
|
||||
VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL,
|
||||
};
|
||||
|
||||
Logger.LogDebug("***** ReleaseAllKeys has been called! *****:");
|
||||
|
||||
foreach (VK vk in keys)
|
||||
{
|
||||
if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0)
|
||||
{
|
||||
Logger.LogDebug(vk.ToString() + " is down, release it...");
|
||||
Common.Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl)
|
||||
kd.wVk = (int)vk;
|
||||
InputSimulation.SendKey(kd);
|
||||
Common.Hook?.ResetLastSwitchKeys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
|
||||
{
|
||||
Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture));
|
||||
Helper.WndProcCounter++;
|
||||
ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable);
|
||||
}
|
||||
|
||||
private static void ScheduleReopenSocketsDueToNetworkChanges(bool closeSockets = true)
|
||||
{
|
||||
if (closeSockets)
|
||||
{
|
||||
// Slept/hibernated machine may still have the sockets' status as Connected:( (unchanged) so it would not re-connect after a timeout when waking up.
|
||||
// Closing the sockets when it is going to sleep/hibernate will trigger the reconnection faster when it wakes up.
|
||||
Common.DoSomethingInUIThread(
|
||||
() =>
|
||||
{
|
||||
SocketStuff s = Common.Sk;
|
||||
Common.Sk = null;
|
||||
s?.Close(false);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
if (!Common.IsMyDesktopActive())
|
||||
{
|
||||
PleaseReopenSocket = 0;
|
||||
}
|
||||
else if (PleaseReopenSocket != 10)
|
||||
{
|
||||
PleaseReopenSocket = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,22 +247,24 @@ internal static class Logger
|
||||
|
||||
internal static void DumpStaticTypes(StringBuilder sb, int level)
|
||||
{
|
||||
sb.AppendLine($"[{nameof(DragDrop)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(DragDrop), 0, level);
|
||||
sb.AppendLine($"[{nameof(Event)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(Event), 0, level);
|
||||
sb.AppendLine($"[{nameof(Helper)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(Helper), 0, level);
|
||||
sb.AppendLine($"[{nameof(Launch)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(Launch), 0, level);
|
||||
sb.AppendLine($"[{nameof(Logger)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(Logger), 0, level);
|
||||
sb.AppendLine($"[{nameof(MachineStuff)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(MachineStuff), 0, level);
|
||||
sb.AppendLine($"[{nameof(Receiver)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(Receiver), 0, level);
|
||||
sb.AppendLine($"[{nameof(Service)}]\r\n===============");
|
||||
Logger.DumpType(sb, typeof(Service), 0, level);
|
||||
var staticTypes = new List<Type>
|
||||
{
|
||||
typeof(Clipboard),
|
||||
typeof(DragDrop),
|
||||
typeof(Event),
|
||||
typeof(InitAndCleanup),
|
||||
typeof(Helper),
|
||||
typeof(Launch),
|
||||
typeof(Logger),
|
||||
typeof(MachineStuff),
|
||||
typeof(Receiver),
|
||||
typeof(Service),
|
||||
};
|
||||
foreach (var staticType in staticTypes)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"[{staticType.Name}]\r\n===============");
|
||||
Logger.DumpType(sb, staticType, 0, level);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop)
|
||||
|
||||
@@ -992,7 +992,7 @@ internal static class MachineStuff
|
||||
Setting.Values.MatrixOneRow = !((package.Type & PackageType.MatrixTwoRowFlag) == PackageType.MatrixTwoRowFlag);
|
||||
MachineMatrix = MachineMatrix; // Save
|
||||
|
||||
Common.ReopenSocketDueToReadError = true;
|
||||
InitAndCleanup.ReopenSocketDueToReadError = true;
|
||||
|
||||
UpdateClientSockets("UpdateMachineMatrix");
|
||||
|
||||
@@ -1044,7 +1044,7 @@ internal static class MachineStuff
|
||||
Common.MoveMouseToCenter();
|
||||
}
|
||||
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
|
||||
Common.UpdateMultipleModeIconAndMenu();
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ internal static class Receiver
|
||||
|
||||
if (!p.IsEmpty)
|
||||
{
|
||||
Common.HasSwitchedMachineSinceLastCopy = true;
|
||||
Clipboard.HasSwitchedMachineSinceLastCopy = true;
|
||||
|
||||
Logger.LogDebug(string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
@@ -274,7 +274,7 @@ internal static class Receiver
|
||||
Common.PackageReceived.Clipboard++;
|
||||
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
|
||||
{
|
||||
Common.clipboardCopiedTime = Common.GetTick();
|
||||
Clipboard.clipboardCopiedTime = Common.GetTick();
|
||||
GetNameOfMachineWithClipboardData(package);
|
||||
SignalBigClipboardData();
|
||||
}
|
||||
@@ -282,10 +282,10 @@ internal static class Receiver
|
||||
break;
|
||||
|
||||
case PackageType.MachineSwitched:
|
||||
if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID))
|
||||
if (Common.GetTick() - Clipboard.clipboardCopiedTime < Clipboard.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID))
|
||||
{
|
||||
Common.clipboardCopiedTime = 0;
|
||||
Common.GetRemoteClipboard("PackageType.MachineSwitched");
|
||||
Clipboard.clipboardCopiedTime = 0;
|
||||
Clipboard.GetRemoteClipboard("PackageType.MachineSwitched");
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -297,7 +297,7 @@ internal static class Receiver
|
||||
if (package.Des == Common.MachineID || package.Des == ID.ALL)
|
||||
{
|
||||
GetNameOfMachineWithClipboardData(package);
|
||||
Common.GetRemoteClipboard("mspaint," + Common.LastMachineWithClipboardData);
|
||||
Clipboard.GetRemoteClipboard("mspaint," + Clipboard.LastMachineWithClipboardData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,10 +326,10 @@ internal static class Receiver
|
||||
Thread.UpdateThreads(thread);
|
||||
|
||||
string remoteMachine = package.MachineName;
|
||||
System.Net.Sockets.TcpClient client = Common.ConnectToRemoteClipboardSocket(remoteMachine);
|
||||
System.Net.Sockets.TcpClient client = Clipboard.ConnectToRemoteClipboardSocket(remoteMachine);
|
||||
bool clientPushData = true;
|
||||
|
||||
if (Common.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction))
|
||||
if (Clipboard.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction))
|
||||
{
|
||||
SocketStuff.SendClipboardData(client.Client, enStream);
|
||||
}
|
||||
@@ -360,7 +360,7 @@ internal static class Receiver
|
||||
|
||||
case PackageType.ClipboardText:
|
||||
case PackageType.ClipboardImage:
|
||||
Common.clipboardCopiedTime = 0;
|
||||
Clipboard.clipboardCopiedTime = 0;
|
||||
if (package.Type == PackageType.ClipboardImage)
|
||||
{
|
||||
Common.PackageReceived.ClipboardImage++;
|
||||
@@ -372,7 +372,7 @@ internal static class Receiver
|
||||
|
||||
if (tcp != null)
|
||||
{
|
||||
Common.ReceiveClipboardDataUsingTCP(
|
||||
Clipboard.ReceiveClipboardDataUsingTCP(
|
||||
package,
|
||||
package.Type == PackageType.ClipboardImage,
|
||||
tcp);
|
||||
@@ -381,10 +381,10 @@ internal static class Receiver
|
||||
break;
|
||||
|
||||
case PackageType.HideMouse:
|
||||
Common.HasSwitchedMachineSinceLastCopy = true;
|
||||
Clipboard.HasSwitchedMachineSinceLastCopy = true;
|
||||
Common.HideMouseCursor(true);
|
||||
Helper.MainFormDotEx(false);
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -405,11 +405,11 @@ internal static class Receiver
|
||||
|
||||
internal static void GetNameOfMachineWithClipboardData(DATA package)
|
||||
{
|
||||
Common.LastIDWithClipboardData = package.Src;
|
||||
List<MachineInf> matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Common.LastIDWithClipboardData);
|
||||
Clipboard.LastIDWithClipboardData = package.Src;
|
||||
List<MachineInf> matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Clipboard.LastIDWithClipboardData);
|
||||
if (matchingMachines.Count >= 1)
|
||||
{
|
||||
Common.LastMachineWithClipboardData = matchingMachines[0].Name.Trim();
|
||||
Clipboard.LastMachineWithClipboardData = matchingMachines[0].Name.Trim();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace MouseWithoutBorders
|
||||
if ((connectedClientSocket = Common.GetConnectedClientSocket()) != null)
|
||||
{
|
||||
ShowStatus($"Connected from local IP Address: {connectedClientSocket.Address}.");
|
||||
Common.UpdateMachineTimeAndID();
|
||||
InitAndCleanup.UpdateMachineTimeAndID();
|
||||
|
||||
Common.MMSleep(1);
|
||||
connected = true;
|
||||
|
||||
@@ -22,6 +22,8 @@ using Microsoft.PowerToys.Telemetry;
|
||||
// </history>
|
||||
using MouseWithoutBorders.Class;
|
||||
using MouseWithoutBorders.Core;
|
||||
|
||||
using Clipboard = MouseWithoutBorders.Core.Clipboard;
|
||||
using Timer = System.Windows.Forms.Timer;
|
||||
|
||||
[module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#buttonOK_Click(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")]
|
||||
@@ -110,7 +112,7 @@ namespace MouseWithoutBorders
|
||||
{
|
||||
SocketStuff.InvalidKeyFound = false;
|
||||
showInvalidKeyMessage = false;
|
||||
Common.ReopenSocketDueToReadError = true;
|
||||
InitAndCleanup.ReopenSocketDueToReadError = true;
|
||||
Common.ReopenSockets(true);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
@@ -780,7 +782,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
ShowUpdateMessage();
|
||||
|
||||
Common.HasSwitchedMachineSinceLastCopy = true;
|
||||
Clipboard.HasSwitchedMachineSinceLastCopy = true;
|
||||
}
|
||||
|
||||
private void CheckBoxDisableCAD_CheckedChanged(object sender, EventArgs e)
|
||||
|
||||
@@ -139,13 +139,13 @@ namespace MouseWithoutBorders
|
||||
{
|
||||
if (cleanup)
|
||||
{
|
||||
Common.Cleanup();
|
||||
InitAndCleanup.Cleanup();
|
||||
}
|
||||
|
||||
Helper.WndProcCounter++;
|
||||
if (!Common.RunOnScrSaverDesktop)
|
||||
{
|
||||
Common.ReleaseAllKeys();
|
||||
InitAndCleanup.ReleaseAllKeys();
|
||||
}
|
||||
|
||||
Helper.RunDDHelper(true);
|
||||
@@ -412,7 +412,7 @@ namespace MouseWithoutBorders
|
||||
|
||||
count = 0;
|
||||
|
||||
Common.InitDone = true;
|
||||
InitAndCleanup.InitDone = true;
|
||||
#if SHOW_ON_WINLOGON
|
||||
if (Common.RunOnLogonDesktop)
|
||||
{
|
||||
@@ -423,39 +423,39 @@ namespace MouseWithoutBorders
|
||||
|
||||
if ((count % 2) == 0)
|
||||
{
|
||||
if (Common.PleaseReopenSocket == 10 || (Common.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0))
|
||||
if (InitAndCleanup.PleaseReopenSocket == 10 || (InitAndCleanup.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0))
|
||||
{
|
||||
if (!Common.AtLeastOneSocketEstablished() || Common.PleaseReopenSocket == 10)
|
||||
if (!Common.AtLeastOneSocketEstablished() || InitAndCleanup.PleaseReopenSocket == 10)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
if (Common.PleaseReopenSocket > 0)
|
||||
if (InitAndCleanup.PleaseReopenSocket > 0)
|
||||
{
|
||||
Common.PleaseReopenSocket--;
|
||||
InitAndCleanup.PleaseReopenSocket--;
|
||||
}
|
||||
|
||||
// Double check.
|
||||
if (!Common.AtLeastOneSocketEstablished())
|
||||
{
|
||||
Common.GetMachineName();
|
||||
Logger.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture));
|
||||
Logger.LogDebug("Common.pleaseReopenSocket: " + InitAndCleanup.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture));
|
||||
Common.ReopenSockets(false);
|
||||
MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Common.PleaseReopenSocket = 0;
|
||||
InitAndCleanup.PleaseReopenSocket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_HOTKEY)
|
||||
if (InitAndCleanup.PleaseReopenSocket == InitAndCleanup.REOPEN_WHEN_HOTKEY)
|
||||
{
|
||||
Common.PleaseReopenSocket = 0;
|
||||
InitAndCleanup.PleaseReopenSocket = 0;
|
||||
Common.ReopenSockets(true);
|
||||
}
|
||||
else if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_WSAECONNRESET)
|
||||
else if (InitAndCleanup.PleaseReopenSocket == InitAndCleanup.REOPEN_WHEN_WSAECONNRESET)
|
||||
{
|
||||
Common.PleaseReopenSocket = 0;
|
||||
InitAndCleanup.PleaseReopenSocket = 0;
|
||||
Thread.Sleep(1000);
|
||||
MachineStuff.UpdateClientSockets("REOPEN_WHEN_WSAECONNRESET");
|
||||
}
|
||||
|
||||
@@ -4,28 +4,6 @@
|
||||
[Other Logs]
|
||||
===============
|
||||
= MouseWithoutBorders.Common
|
||||
Comma = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
Star = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
NullSeparator = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
lastClipboardEventTime = 0
|
||||
clipboardCopiedTime = 0
|
||||
<LastIDWithClipboardData>k__BackingField = NONE
|
||||
<NextClipboardViewer>k__BackingField = 0
|
||||
<IsClipboardDataImage>k__BackingField = False
|
||||
lastClipboardObject =
|
||||
<HasSwitchedMachineSinceLastCopy>k__BackingField = False
|
||||
ClipboardThreadOldLock = Lock
|
||||
--_owningThreadId = 0
|
||||
--_state = 0
|
||||
--_recursionCount = 0
|
||||
--_spinCount = 22
|
||||
--_waiterStartTimeMs = 0
|
||||
--s_contentionCount = 0
|
||||
--s_maxSpinCount = 22
|
||||
--s_minSpinCountForAdaptiveSpin = -100
|
||||
screenWidth = 0
|
||||
screenHeight = 0
|
||||
lastX = 0
|
||||
@@ -99,17 +77,6 @@ LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte
|
||||
--_budget = ????????????
|
||||
--_growLockArray = True
|
||||
--_comparerIsDefaultForClasses = False
|
||||
initDone = False
|
||||
REOPEN_WHEN_WSAECONNRESET = -10054
|
||||
REOPEN_WHEN_HOTKEY = -10055
|
||||
PleaseReopenSocket = 0
|
||||
ReopenSocketDueToReadError = False
|
||||
<LastResumeSuspendTime>k__BackingField = ????????????
|
||||
--_dateData = ????????????
|
||||
--MinValue = 01/01/0001 00:00:00
|
||||
--MaxValue = 31/12/9999 23:59:59
|
||||
--UnixEpoch = 01/01/1970 00:00:00
|
||||
lastReleaseAllKeysCall = 0
|
||||
PackageSent = MouseWithoutBorders.PackageMonitor
|
||||
--Keyboard = 0
|
||||
--Mouse = 0
|
||||
@@ -153,12 +120,6 @@ p = {X=0,Y=0}
|
||||
--y = 0
|
||||
--Empty = {X=0,Y=0}
|
||||
<IpcChannelCreated>k__BackingField = False
|
||||
BIG_CLIPBOARD_DATA_TIMEOUT = 30000
|
||||
MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
|
||||
MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
|
||||
TEXT_HEADER_SIZE = 12
|
||||
DATA_SIZE = 48
|
||||
TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
|
||||
TOGGLE_ICONS_SIZE = 4
|
||||
ICON_ONE = 0
|
||||
ICON_ALL = 1
|
||||
@@ -195,6 +156,36 @@ WM_KEYDOWN = 256
|
||||
WM_KEYUP = 257
|
||||
WM_SYSKEYDOWN = 260
|
||||
WM_SYSKEYUP = 261
|
||||
[Clipboard]
|
||||
===============
|
||||
Comma = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
Star = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
NullSeparator = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
lastClipboardEventTime = 0
|
||||
clipboardCopiedTime = 0
|
||||
<LastIDWithClipboardData>k__BackingField = NONE
|
||||
<NextClipboardViewer>k__BackingField = 0
|
||||
<IsClipboardDataImage>k__BackingField = False
|
||||
lastClipboardObject =
|
||||
<HasSwitchedMachineSinceLastCopy>k__BackingField = False
|
||||
ClipboardThreadOldLock = Lock
|
||||
--_owningThreadId = 0
|
||||
--_state = 0
|
||||
--_recursionCount = 0
|
||||
--_spinCount = 22
|
||||
--_waiterStartTimeMs = 0
|
||||
--s_contentionCount = 0
|
||||
--s_maxSpinCount = 22
|
||||
--s_minSpinCountForAdaptiveSpin = -100
|
||||
BIG_CLIPBOARD_DATA_TIMEOUT = 30000
|
||||
MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
|
||||
MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
|
||||
TEXT_HEADER_SIZE = 12
|
||||
DATA_SIZE = 48
|
||||
TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
|
||||
[DragDrop]
|
||||
===============
|
||||
isDragging = False
|
||||
@@ -249,6 +240,19 @@ actualLastPos = {X=0,Y=0}
|
||||
--Empty = {X=0,Y=0}
|
||||
myLastX = 0
|
||||
myLastY = 0
|
||||
[InitAndCleanup]
|
||||
===============
|
||||
initDone = False
|
||||
REOPEN_WHEN_WSAECONNRESET = -10054
|
||||
REOPEN_WHEN_HOTKEY = -10055
|
||||
PleaseReopenSocket = 0
|
||||
ReopenSocketDueToReadError = False
|
||||
<LastResumeSuspendTime>k__BackingField = ????????????
|
||||
--_dateData = ????????????
|
||||
--MinValue = 01/01/0001 00:00:00
|
||||
--MaxValue = 31/12/9999 23:59:59
|
||||
--UnixEpoch = 01/01/1970 00:00:00
|
||||
lastReleaseAllKeysCall = 0
|
||||
[Helper]
|
||||
===============
|
||||
signalHelperToExit = False
|
||||
|
||||
@@ -121,8 +121,8 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
|
||||
LTEXT "ZoomIt v9.0",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright <EFBFBD> 2006-2024 Mark Russinovich",IDC_COPYRIGHT,42,17,166,8
|
||||
LTEXT "ZoomIt v9.01",IDC_VERSION,42,7,73,10
|
||||
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,166,8
|
||||
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
|
||||
"SysLink",WS_TABSTOP,42,26,150,9
|
||||
ICON "APPICON",IDC_STATIC,12,9,20,20
|
||||
|
||||
@@ -157,9 +157,33 @@ namespace Awake
|
||||
|
||||
pidOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !int.TryParse(result.Tokens[0].Value, out _))
|
||||
if (result.Tokens.Count == 0)
|
||||
{
|
||||
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {result.Tokens[0].Value}.";
|
||||
return;
|
||||
}
|
||||
|
||||
string tokenValue = result.Tokens[0].Value;
|
||||
|
||||
if (!int.TryParse(tokenValue, out int parsed))
|
||||
{
|
||||
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {tokenValue}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed <= 0)
|
||||
{
|
||||
string errorMessage = $"PID value in --pid must be a positive integer. Value used: {parsed}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
// Process existence check. (We also re-validate just before binding.)
|
||||
if (!ProcessExists(parsed))
|
||||
{
|
||||
string errorMessage = $"No running process found with an ID of {parsed}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
@@ -216,6 +240,25 @@ namespace Awake
|
||||
Manager.CompleteExit(exitCode);
|
||||
}
|
||||
|
||||
private static bool ProcessExists(int processId)
|
||||
{
|
||||
if (processId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Throws if the Process ID is not found.
|
||||
using var p = Process.GetProcessById(processId);
|
||||
return !p.HasExited;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt, bool useParentPid)
|
||||
{
|
||||
if (pid == 0 && !useParentPid)
|
||||
@@ -271,6 +314,12 @@ namespace Awake
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
if (!ProcessExists(pid))
|
||||
{
|
||||
Logger.LogError($"PID {pid} does not exist or is not accessible. Exiting.");
|
||||
Exit(Resources.AWAKE_EXIT_PROCESS_BINDING_FAILURE_MESSAGE, 1);
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Bound to target process while also using PowerToys settings: {pid}");
|
||||
|
||||
RunnerHelper.WaitForPowerToysRunner(pid, () =>
|
||||
@@ -287,28 +336,7 @@ namespace Awake
|
||||
}
|
||||
else if (pid != 0 || useParentPid)
|
||||
{
|
||||
// Second, we snap to process-based execution. Because this is something that
|
||||
// is snapped to a running entity, we only want to enable the ability to set
|
||||
// indefinite keep-awake with the display settings that the user wants to set.
|
||||
// In this context, manual (explicit) PID takes precedence over parent PID.
|
||||
int targetPid = pid != 0 ? pid : useParentPid ? Manager.GetParentProcess()?.Id ?? 0 : 0;
|
||||
|
||||
if (targetPid != 0)
|
||||
{
|
||||
Logger.LogInfo($"Bound to target process: {targetPid}");
|
||||
|
||||
Manager.SetIndefiniteKeepAwake(displayOn, targetPid);
|
||||
|
||||
RunnerHelper.WaitForPowerToysRunner(targetPid, () =>
|
||||
{
|
||||
Logger.LogInfo($"Triggered PID-based exit handler for PID {targetPid}.");
|
||||
Exit(Resources.AWAKE_EXIT_BINDING_HOOK_MESSAGE, 0);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError("Not binding to any process.");
|
||||
}
|
||||
HandleProcessScopedKeepAwake(pid, useParentPid, displayOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -344,6 +372,62 @@ namespace Awake
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a process-scoped keep-awake session. The application will keep the system awake
|
||||
/// indefinitely until the target process terminates.
|
||||
/// </summary>
|
||||
/// <param name="pid">The explicit process ID to monitor.</param>
|
||||
/// <param name="useParentPid">A flag indicating whether the application should monitor its
|
||||
/// parent process.</param>
|
||||
/// <param name="displayOn">Whether to keep the display on during the session.</param>
|
||||
private static void HandleProcessScopedKeepAwake(int pid, bool useParentPid, bool displayOn)
|
||||
{
|
||||
int targetPid = 0;
|
||||
|
||||
// We prioritize a user-provided PID over the parent PID. If both are given on the
|
||||
// command line, the --pid value will be used.
|
||||
if (pid != 0)
|
||||
{
|
||||
if (pid == Environment.ProcessId)
|
||||
{
|
||||
Logger.LogError("Awake cannot bind to itself, as this would lead to an indefinite keep-awake state.");
|
||||
Exit(Resources.AWAKE_EXIT_BIND_TO_SELF_FAILURE_MESSAGE, 1);
|
||||
}
|
||||
|
||||
if (!ProcessExists(pid))
|
||||
{
|
||||
Logger.LogError($"PID {pid} does not exist or is not accessible. Exiting.");
|
||||
Exit(Resources.AWAKE_EXIT_PROCESS_BINDING_FAILURE_MESSAGE, 1);
|
||||
}
|
||||
|
||||
targetPid = pid;
|
||||
}
|
||||
else if (useParentPid)
|
||||
{
|
||||
targetPid = Manager.GetParentProcess()?.Id ?? 0;
|
||||
|
||||
if (targetPid == 0)
|
||||
{
|
||||
// The parent process could not be identified.
|
||||
Logger.LogError("Failed to identify a parent process for binding.");
|
||||
Exit(Resources.AWAKE_EXIT_PARENT_BINDING_FAILURE_MESSAGE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// We have a valid non-zero PID to monitor.
|
||||
Logger.LogInfo($"Bound to target process: {targetPid}");
|
||||
|
||||
// Sets the keep-awake plan and updates the tray icon.
|
||||
Manager.SetIndefiniteKeepAwake(displayOn, targetPid);
|
||||
|
||||
// Synchronize with the target process, and trigger Exit() when it finishes.
|
||||
RunnerHelper.WaitForPowerToysRunner(targetPid, () =>
|
||||
{
|
||||
Logger.LogInfo($"Triggered PID-based exit handler for PID {targetPid}.");
|
||||
Exit(Resources.AWAKE_EXIT_BINDING_HOOK_MESSAGE, 0);
|
||||
});
|
||||
}
|
||||
|
||||
private static void AllocateLocalConsole()
|
||||
{
|
||||
Manager.AllocateConsole();
|
||||
|
||||
@@ -132,6 +132,15 @@ namespace Awake.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exiting because the provided process ID is Awake's own..
|
||||
/// </summary>
|
||||
internal static string AWAKE_EXIT_BIND_TO_SELF_FAILURE_MESSAGE {
|
||||
get {
|
||||
return ResourceManager.GetString("AWAKE_EXIT_BIND_TO_SELF_FAILURE_MESSAGE", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Terminating from process binding hook..
|
||||
/// </summary>
|
||||
@@ -150,6 +159,24 @@ namespace Awake.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exiting because the parent process ID could not be found..
|
||||
/// </summary>
|
||||
internal static string AWAKE_EXIT_PARENT_BINDING_FAILURE_MESSAGE {
|
||||
get {
|
||||
return ResourceManager.GetString("AWAKE_EXIT_PARENT_BINDING_FAILURE_MESSAGE", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exiting because the requested process ID could not be found..
|
||||
/// </summary>
|
||||
internal static string AWAKE_EXIT_PROCESS_BINDING_FAILURE_MESSAGE {
|
||||
get {
|
||||
return ResourceManager.GetString("AWAKE_EXIT_PROCESS_BINDING_FAILURE_MESSAGE", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Received a signal to end the process. Making sure we quit....
|
||||
/// </summary>
|
||||
|
||||
@@ -226,4 +226,13 @@
|
||||
<data name="AWAKE_SCREEN_OFF" xml:space="preserve">
|
||||
<value>Off</value>
|
||||
</data>
|
||||
<data name="AWAKE_EXIT_PARENT_BINDING_FAILURE_MESSAGE" xml:space="preserve">
|
||||
<value>Exiting because the parent process ID could not be found.</value>
|
||||
</data>
|
||||
<data name="AWAKE_EXIT_PROCESS_BINDING_FAILURE_MESSAGE" xml:space="preserve">
|
||||
<value>Exiting because the requested process ID could not be found.</value>
|
||||
</data>
|
||||
<data name="AWAKE_EXIT_BIND_TO_SELF_FAILURE_MESSAGE" xml:space="preserve">
|
||||
<value>Exiting because the provided process ID is Awake's own.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2,28 +2,44 @@
|
||||
# You can modify the rules from these initially generated values to suit your own policies.
|
||||
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference.
|
||||
|
||||
[*.cs]
|
||||
##################################################
|
||||
# Global settings
|
||||
##################################################
|
||||
|
||||
file_header_template = Copyright (c) Microsoft Corporation\r\nThe Microsoft Corporation licenses this file to you under the MIT license.\r\nSee the LICENSE file in the project root for more information.
|
||||
|
||||
#Core editorconfig formatting - indentation
|
||||
|
||||
#use soft tabs (spaces) for indentation
|
||||
[*.{cs,vb}]
|
||||
tab_width = 4
|
||||
indent_size = 4
|
||||
end_of_line = crlf
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
file_header_template = Copyright (c) Microsoft Corporation\nThe Microsoft Corporation licenses this file to you under the MIT license.\nSee the LICENSE file in the project root for more information.
|
||||
|
||||
#Formatting - new line options
|
||||
##################################################
|
||||
# C# specific formatting
|
||||
##################################################
|
||||
|
||||
[*.cs]
|
||||
# ----------------------------------------------
|
||||
# Core editorconfig formatting - indentation
|
||||
# ----------------------------------------------
|
||||
|
||||
#place else statements on a new line
|
||||
csharp_new_line_before_else = true
|
||||
#require braces to be on a new line for lambdas, methods, control_blocks, types, properties, and accessors (also known as "Allman" style)
|
||||
csharp_new_line_before_open_brace = all
|
||||
|
||||
#Formatting - organize using options
|
||||
# ----------------------------------------------
|
||||
# Formatting - organize using options
|
||||
# ----------------------------------------------
|
||||
|
||||
#sort System.* using directives alphabetically, and place them before other usings
|
||||
# sort System.* using directives alphabetically, and place them before other usings
|
||||
dotnet_sort_system_directives_first = true
|
||||
# Do not place System.* using directives before other using directives.
|
||||
dotnet_separate_import_directive_groups = false
|
||||
|
||||
#Formatting - spacing options
|
||||
# ----------------------------------------------
|
||||
# Formatting - spacing options
|
||||
# ----------------------------------------------
|
||||
|
||||
#require NO space between a cast and the value
|
||||
csharp_space_after_cast = false
|
||||
@@ -44,17 +60,29 @@ csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
|
||||
#Formatting - wrapping options
|
||||
# ----------------------------------------------
|
||||
# Formatting - wrapping options
|
||||
# ----------------------------------------------
|
||||
|
||||
#leave code block on separate lines
|
||||
csharp_preserve_single_line_blocks = true
|
||||
#put each statement on a separate line
|
||||
csharp_preserve_single_line_statements = false
|
||||
|
||||
#Style - Code block preferences
|
||||
##################################################
|
||||
# C# style rules
|
||||
##################################################
|
||||
|
||||
# ----------------------------------------------
|
||||
# Style - Code block preferences
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer curly braces even for one line of code
|
||||
csharp_prefer_braces = true:suggestion
|
||||
|
||||
#Style - expression bodied member options
|
||||
# ----------------------------------------------
|
||||
# Style - expression bodied member options
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer expression bodies for accessors
|
||||
csharp_style_expression_bodied_accessors = true:warning
|
||||
@@ -65,55 +93,73 @@ csharp_style_expression_bodied_methods = when_on_single_line:silent
|
||||
#prefer expression-bodied members for properties
|
||||
csharp_style_expression_bodied_properties = true:warning
|
||||
|
||||
#Style - expression level options
|
||||
# ----------------------------------------------
|
||||
# Style - expression level options
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer out variables to be declared before the method call
|
||||
csharp_style_inlined_variable_declaration = false:suggestion
|
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
#Style - Expression-level preferences
|
||||
# ----------------------------------------------
|
||||
# Style - Expression-level preferences
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer default over default(T)
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
#prefer objects to be initialized using object initializers when possible
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
|
||||
#Style - implicit and explicit types
|
||||
# ----------------------------------------------
|
||||
# Style - implicit and explicit types
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer var over explicit type in all cases, unless overridden by another code style rule
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
#prefer var is used to declare variables with built-in system types such as int
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_for_built_in_types = true:warning
|
||||
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
#Style - language keyword and framework type options
|
||||
# ----------------------------------------------
|
||||
# Style - language keyword and framework type options
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
|
||||
#Style - Language rules
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
|
||||
csharp_style_var_for_built_in_types = true:warning
|
||||
# ----------------------------------------------
|
||||
# Style - Language rules
|
||||
# ----------------------------------------------
|
||||
|
||||
#Style - modifier options
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
|
||||
|
||||
# ----------------------------------------------
|
||||
# Style - modifier options
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
|
||||
#Style - Modifier preferences
|
||||
# ----------------------------------------------
|
||||
# Style - Modifier preferences
|
||||
# ----------------------------------------------
|
||||
|
||||
#when this rule is set to a list of modifiers, prefer the specified ordering.
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,async,readonly,override,sealed,abstract,virtual:warning
|
||||
dotnet_style_readonly_field = true:warning
|
||||
|
||||
#Style - Pattern matching
|
||||
# ----------------------------------------------
|
||||
# Style - Pattern matching
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer pattern matching instead of is expression with type casts
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:warning
|
||||
|
||||
#Style - qualification options
|
||||
# ----------------------------------------------
|
||||
# Style - qualification options
|
||||
# ----------------------------------------------
|
||||
|
||||
#prefer events not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
@@ -123,20 +169,26 @@ dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
#prefer properties not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_prefer_simple_using_statement = true:warning
|
||||
csharp_style_namespace_declarations = file_scoped:warning
|
||||
|
||||
# ----------------------------------------------
|
||||
# Style - expression bodies
|
||||
# ----------------------------------------------
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
|
||||
# ----------------------------------------------
|
||||
# Style - Miscellaneous preferences
|
||||
# ----------------------------------------------
|
||||
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
csharp_prefer_simple_using_statement = true:warning
|
||||
csharp_style_namespace_declarations = file_scoped:warning
|
||||
|
||||
[*.{cs,vb}]
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
tab_width = 4
|
||||
indent_size = 4
|
||||
end_of_line = crlf
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
@@ -146,12 +198,13 @@ dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||
[*.{cs,vb}]
|
||||
|
||||
#Style - Unnecessary code rules
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:warning
|
||||
|
||||
#### Naming styles ####
|
||||
##################################################
|
||||
# Naming rules
|
||||
##################################################
|
||||
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Naming rules
|
||||
|
||||
@@ -203,7 +256,11 @@ dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_compound_assignment = true:warning
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
|
||||
# Diagnostic configuration
|
||||
|
||||
##################################################
|
||||
# Diagnostics
|
||||
##################################################
|
||||
|
||||
[*.{cs,vb}]
|
||||
# CS8305: Type is for evaluation purposes only and is subject to change or removal in future updates.
|
||||
dotnet_diagnostic.CS8305.severity = suggestion
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
|
||||
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -106,47 +107,57 @@ public partial class ContextMenuViewModel : ObservableObject,
|
||||
return 0;
|
||||
}
|
||||
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, item.Title);
|
||||
var nameMatch = FuzzyStringMatcher.ScoreFuzzy(query, item.Title);
|
||||
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, item.Subtitle);
|
||||
var descriptionMatch = FuzzyStringMatcher.ScoreFuzzy(query, item.Subtitle);
|
||||
|
||||
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
|
||||
return new[] { nameMatch, (descriptionMatch - 4) / 2, 0 }.Max();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a mapping of key -> command item for this particular item's
|
||||
/// MoreCommands. (This won't include the primary Command, but it will
|
||||
/// include the secondary one). This map can be used to quickly check if a
|
||||
/// shortcut key was pressed
|
||||
/// shortcut key was pressed. In case there are duplicate keybindings, the first
|
||||
/// one is used and the rest are ignored.
|
||||
/// </summary>
|
||||
/// <returns>a dictionary of KeyChord -> Context commands, for all commands
|
||||
/// that have a shortcut key set.</returns>
|
||||
public Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
|
||||
private Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
|
||||
{
|
||||
if (CurrentContextMenu is null)
|
||||
var result = new Dictionary<KeyChord, CommandContextItemViewModel>();
|
||||
|
||||
var menu = CurrentContextMenu;
|
||||
if (menu is null)
|
||||
{
|
||||
return [];
|
||||
return result;
|
||||
}
|
||||
|
||||
return CurrentContextMenu
|
||||
.OfType<CommandContextItemViewModel>()
|
||||
.Where(c => c.HasRequestedShortcut)
|
||||
.ToDictionary(
|
||||
c => c.RequestedShortcut ?? new KeyChord(0, 0, 0),
|
||||
c => c);
|
||||
foreach (var item in menu)
|
||||
{
|
||||
if (item is CommandContextItemViewModel cmd && cmd.HasRequestedShortcut)
|
||||
{
|
||||
var key = cmd.RequestedShortcut ?? new KeyChord(0, 0, 0);
|
||||
var added = result.TryAdd(key, cmd);
|
||||
if (!added)
|
||||
{
|
||||
Logger.LogWarning($"Ignoring duplicate keyboard shortcut {KeyChordHelpers.FormatForDebug(key)} on command '{cmd.Title ?? cmd.Name ?? "(unknown)"}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ContextKeybindingResult? CheckKeybinding(bool ctrl, bool alt, bool shift, bool win, VirtualKey key)
|
||||
{
|
||||
var keybindings = Keybindings();
|
||||
if (keybindings is not null)
|
||||
|
||||
// Does the pressed key match any of the keybindings?
|
||||
var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0);
|
||||
if (keybindings.TryGetValue(pressedKeyChord, out var item))
|
||||
{
|
||||
// Does the pressed key match any of the keybindings?
|
||||
var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0);
|
||||
if (keybindings.TryGetValue(pressedKeyChord, out var item))
|
||||
{
|
||||
return InvokeCommand(item);
|
||||
}
|
||||
return InvokeCommand(item);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -51,6 +51,36 @@ public abstract partial class ExtensionObjectViewModel : ObservableObject
|
||||
DoOnUiThread(() => OnPropertyChanged(propertyName));
|
||||
}
|
||||
|
||||
protected void UpdateProperty(string propertyName1, string propertyName2)
|
||||
{
|
||||
DoOnUiThread(() =>
|
||||
{
|
||||
OnPropertyChanged(propertyName1);
|
||||
OnPropertyChanged(propertyName2);
|
||||
});
|
||||
}
|
||||
|
||||
protected void UpdateProperty(string propertyName1, string propertyName2, string propertyName3)
|
||||
{
|
||||
DoOnUiThread(() =>
|
||||
{
|
||||
OnPropertyChanged(propertyName1);
|
||||
OnPropertyChanged(propertyName2);
|
||||
OnPropertyChanged(propertyName3);
|
||||
});
|
||||
}
|
||||
|
||||
protected void UpdateProperty(params string[] propertyNames)
|
||||
{
|
||||
DoOnUiThread(() =>
|
||||
{
|
||||
foreach (var propertyName in propertyNames)
|
||||
{
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void ShowException(Exception ex, string? extensionHint = null)
|
||||
{
|
||||
if (PageContext.TryGetTarget(out var pageContext))
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
|
||||
@@ -10,14 +9,11 @@ namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
public partial class FiltersViewModel : ExtensionObjectViewModel
|
||||
{
|
||||
private readonly ExtensionObject<IFilters> _filtersModel = new(null);
|
||||
private readonly ExtensionObject<IFilters> _filtersModel;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string CurrentFilterId { get; set; } = string.Empty;
|
||||
public string CurrentFilterId { get; private set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ShouldShowFilters))]
|
||||
public partial IFilterItemViewModel[] Filters { get; set; } = [];
|
||||
public IFilterItemViewModel[] Filters { get; private set; } = [];
|
||||
|
||||
public bool ShouldShowFilters => Filters.Length > 0;
|
||||
|
||||
@@ -34,23 +30,11 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
|
||||
if (_filtersModel.Unsafe is not null)
|
||||
{
|
||||
var filters = _filtersModel.Unsafe.GetFilters();
|
||||
Filters = filters.Select<IFilterItem, IFilterItemViewModel>(filter =>
|
||||
{
|
||||
var filterItem = filter as IFilter;
|
||||
if (filterItem != null)
|
||||
{
|
||||
var filterVM = new FilterItemViewModel(filterItem!, PageContext);
|
||||
filterVM.InitializeProperties();
|
||||
Filters = BuildFilters(filters ?? []);
|
||||
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters));
|
||||
|
||||
return filterVM;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SeparatorViewModel();
|
||||
}
|
||||
}).ToArray();
|
||||
|
||||
CurrentFilterId = _filtersModel.Unsafe.CurrentFilterId;
|
||||
CurrentFilterId = _filtersModel.Unsafe.CurrentFilterId ?? string.Empty;
|
||||
UpdateProperty(nameof(CurrentFilterId));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -61,7 +45,27 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
|
||||
}
|
||||
|
||||
Filters = [];
|
||||
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters));
|
||||
|
||||
CurrentFilterId = string.Empty;
|
||||
UpdateProperty(nameof(CurrentFilterId));
|
||||
}
|
||||
|
||||
private IFilterItemViewModel[] BuildFilters(IFilterItem[] filters)
|
||||
{
|
||||
return [..filters.Select<IFilterItem, IFilterItemViewModel>(filter =>
|
||||
{
|
||||
if (filter is IFilter filterItem)
|
||||
{
|
||||
var filterItemViewModel = new FilterItemViewModel(filterItem!, PageContext);
|
||||
filterItemViewModel.InitializeProperties();
|
||||
return filterItemViewModel;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SeparatorViewModel();
|
||||
}
|
||||
})];
|
||||
}
|
||||
|
||||
public override void SafeCleanup()
|
||||
@@ -70,9 +74,9 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
|
||||
|
||||
foreach (var filter in Filters)
|
||||
{
|
||||
if (filter is FilterItemViewModel filterVM)
|
||||
if (filter is FilterItemViewModel filterItemViewModel)
|
||||
{
|
||||
filterVM.SafeCleanup();
|
||||
filterItemViewModel.SafeCleanup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
@@ -109,8 +108,6 @@ public partial class ListItemViewModel(IListItem model, WeakReference<IPageConte
|
||||
|
||||
// TODO: Do we want filters to match descriptions and other properties? Tags, etc... Yes?
|
||||
// TODO: Do we want to save off the score here so we can sort by it in our ListViewModel?
|
||||
public bool MatchesFilter(string filter) => StringMatcher.FuzzySearch(filter, Title).Success || StringMatcher.FuzzySearch(filter, Subtitle).Success;
|
||||
|
||||
public override string ToString() => $"{Name} ListItemViewModel";
|
||||
|
||||
public override bool Equals(object? obj) => obj is ListItemViewModel vm && vm.Model.Equals(this.Model);
|
||||
@@ -132,7 +129,7 @@ public partial class ListItemViewModel(IListItem model, WeakReference<IPageConte
|
||||
{
|
||||
// Tags being an ObservableCollection instead of a List lead to
|
||||
// many COM exception issues.
|
||||
Tags = new(newTags);
|
||||
Tags = [.. newTags];
|
||||
|
||||
UpdateProperty(nameof(Tags));
|
||||
UpdateProperty(nameof(HasTags));
|
||||
|
||||
@@ -173,12 +173,18 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
try
|
||||
{
|
||||
// Check for cancellation before starting expensive operations
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newItems = _model.Unsafe!.GetItems();
|
||||
|
||||
// Check for cancellation after getting items from extension
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO we can probably further optimize this by also keeping a
|
||||
// HashSet of every ExtensionObject we currently have, and only
|
||||
@@ -186,7 +192,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
foreach (var item in newItems)
|
||||
{
|
||||
// Check for cancellation during item processing
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ListItemViewModel viewModel = new(item, new(this));
|
||||
|
||||
@@ -198,12 +207,19 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
|
||||
// Check for cancellation before initializing first twenty items
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var firstTwenty = newViewModels.Take(20);
|
||||
foreach (var item in firstTwenty)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
item?.SafeInitializeProperties();
|
||||
}
|
||||
|
||||
@@ -211,7 +227,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
_cancellationTokenSource?.Cancel();
|
||||
|
||||
// Check for cancellation before updating the list
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<ListItemViewModel> removedItems = [];
|
||||
lock (_listLock)
|
||||
@@ -264,13 +283,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
_initializeItemsTask = new Task(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
InitializeItemsTask(_cancellationTokenSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
InitializeItemsTask(_cancellationTokenSource.Token);
|
||||
});
|
||||
_initializeItemsTask.Start();
|
||||
|
||||
@@ -304,7 +317,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
private void InitializeItemsTask(CancellationToken ct)
|
||||
{
|
||||
// Were we already canceled?
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ListItemViewModel[] iterable;
|
||||
lock (_listLock)
|
||||
@@ -314,7 +330,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
foreach (var item in iterable)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: GH #502
|
||||
// We should probably remove the item from the list if it
|
||||
@@ -323,7 +342,10 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
// at once.
|
||||
item.SafeInitializeProperties();
|
||||
|
||||
ct.ThrowIfCancellationRequested();
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,9 +367,9 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
return 1;
|
||||
}
|
||||
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
|
||||
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
|
||||
var nameMatch = FuzzyStringMatcher.ScoreFuzzy(query, listItem.Title);
|
||||
var descriptionMatch = FuzzyStringMatcher.ScoreFuzzy(query, listItem.Subtitle);
|
||||
return new[] { nameMatch, (descriptionMatch - 4) / 2, 0 }.Max();
|
||||
}
|
||||
|
||||
private struct ScoredListItemViewModel
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
@@ -32,12 +34,28 @@ public interface IContextMenuContext : INotifyPropertyChanged
|
||||
/// that have a shortcut key set.</returns>
|
||||
public Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
|
||||
{
|
||||
return MoreCommands
|
||||
.OfType<CommandContextItemViewModel>()
|
||||
.Where(c => c.HasRequestedShortcut)
|
||||
.ToDictionary(
|
||||
c => c.RequestedShortcut ?? new KeyChord(0, 0, 0),
|
||||
c => c);
|
||||
var result = new Dictionary<KeyChord, CommandContextItemViewModel>();
|
||||
|
||||
var menu = MoreCommands;
|
||||
if (menu is null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var item in menu)
|
||||
{
|
||||
if (item is CommandContextItemViewModel cmd && cmd.HasRequestedShortcut)
|
||||
{
|
||||
var key = cmd.RequestedShortcut ?? new KeyChord(0, 0, 0);
|
||||
var added = result.TryAdd(key, cmd);
|
||||
if (!added)
|
||||
{
|
||||
Logger.LogWarning($"Ignoring duplicate keyboard shortcut {KeyChordHelpers.FormatForDebug(key)} on command '{cmd.Title ?? cmd.Name ?? "(unknown)"}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Common.Helpers;
|
||||
@@ -22,20 +23,27 @@ namespace Microsoft.CmdPal.UI.ViewModels.MainPage;
|
||||
/// </summary>
|
||||
public partial class MainListPage : DynamicListPage,
|
||||
IRecipient<ClearSearchMessage>,
|
||||
IRecipient<UpdateFallbackItemsMessage>
|
||||
IRecipient<UpdateFallbackItemsMessage>, IDisposable
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly string[] _specialFallbacks = [
|
||||
"com.microsoft.cmdpal.builtin.run",
|
||||
"com.microsoft.cmdpal.builtin.calculator"
|
||||
];
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly TopLevelCommandManager _tlcManager;
|
||||
private IEnumerable<Scored<IListItem>>? _filteredItems;
|
||||
private IEnumerable<Scored<IListItem>>? _filteredApps;
|
||||
private IEnumerable<IListItem>? _allApps;
|
||||
private IEnumerable<Scored<IListItem>>? _fallbackItems;
|
||||
private bool _includeApps;
|
||||
private bool _filteredItemsIncludesApps;
|
||||
private int _appResultLimit = 10;
|
||||
|
||||
private InterlockedBoolean _refreshRunning;
|
||||
private InterlockedBoolean _refreshRequested;
|
||||
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
|
||||
public MainListPage(IServiceProvider serviceProvider)
|
||||
{
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.scale-200.png");
|
||||
@@ -50,12 +58,12 @@ public partial class MainListPage : DynamicListPage,
|
||||
// We just want to know when it is done.
|
||||
var allApps = AllAppsCommandProvider.Page;
|
||||
allApps.PropChanged += (s, p) =>
|
||||
{
|
||||
if (p.PropertyName == nameof(allApps.IsLoading))
|
||||
{
|
||||
IsLoading = ActuallyLoading();
|
||||
}
|
||||
};
|
||||
if (p.PropertyName == nameof(allApps.IsLoading))
|
||||
{
|
||||
IsLoading = ActuallyLoading();
|
||||
}
|
||||
};
|
||||
|
||||
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
|
||||
@@ -150,10 +158,23 @@ public partial class MainListPage : DynamicListPage,
|
||||
{
|
||||
lock (_tlcManager.TopLevelCommands)
|
||||
{
|
||||
IEnumerable<Scored<IListItem>> limitedApps = Enumerable.Empty<Scored<IListItem>>();
|
||||
|
||||
// Fuzzy matching can produce a lot of results, so we want to limit the
|
||||
// number of apps we show at once if it's a large set.
|
||||
if (_filteredApps?.Any() == true)
|
||||
{
|
||||
limitedApps = _filteredApps.OrderByDescending(s => s.Score).Take(_appResultLimit);
|
||||
}
|
||||
|
||||
var items = Enumerable.Empty<Scored<IListItem>>()
|
||||
.Concat(_filteredItems is not null ? _filteredItems : [])
|
||||
.Concat(_filteredApps is not null ? _filteredApps : [])
|
||||
.Concat(limitedApps)
|
||||
.OrderByDescending(o => o.Score)
|
||||
|
||||
// Add fallback items post-sort so they are always at the end of the list
|
||||
// and eventually ordered based on user preference
|
||||
.Concat(_fallbackItems is not null ? _fallbackItems.Where(w => !string.IsNullOrEmpty(w.Item.Title)) : [])
|
||||
.Select(s => s.Item)
|
||||
.ToArray();
|
||||
return items;
|
||||
@@ -163,10 +184,29 @@ public partial class MainListPage : DynamicListPage,
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
var timer = new Stopwatch();
|
||||
timer.Start();
|
||||
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
var token = _cancellationTokenSource.Token;
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle changes to the filter text here
|
||||
if (!string.IsNullOrEmpty(SearchText))
|
||||
{
|
||||
var aliases = _serviceProvider.GetService<AliasManager>()!;
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (aliases.CheckAlias(newSearch))
|
||||
{
|
||||
if (_filteredItemsIncludesApps != _includeApps)
|
||||
@@ -176,7 +216,6 @@ public partial class MainListPage : DynamicListPage,
|
||||
_filteredItemsIncludesApps = _includeApps;
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_allApps = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,10 +223,20 @@ public partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var commands = _tlcManager.TopLevelCommands;
|
||||
lock (commands)
|
||||
{
|
||||
UpdateFallbacks(newSearch, commands.ToImmutableArray());
|
||||
UpdateFallbacks(SearchText, commands.ToImmutableArray(), token);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleared out the filter text? easy. Reset _filteredItems, and bail out.
|
||||
if (string.IsNullOrEmpty(newSearch))
|
||||
@@ -195,7 +244,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
_filteredItemsIncludesApps = _includeApps;
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_allApps = null;
|
||||
_fallbackItems = null;
|
||||
RaiseItemsChanged(commands.Count);
|
||||
return;
|
||||
}
|
||||
@@ -206,7 +255,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
{
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_allApps = null;
|
||||
_fallbackItems = null;
|
||||
}
|
||||
|
||||
// If the internal state has changed, reset _filteredItems to reset the list.
|
||||
@@ -214,61 +263,149 @@ public partial class MainListPage : DynamicListPage,
|
||||
{
|
||||
_filteredItems = null;
|
||||
_filteredApps = null;
|
||||
_allApps = null;
|
||||
_fallbackItems = null;
|
||||
}
|
||||
|
||||
var newFilteredItems = _filteredItems?.Select(s => s.Item);
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<IListItem> newFilteredItems = Enumerable.Empty<IListItem>();
|
||||
IEnumerable<IListItem> newFallbacks = Enumerable.Empty<IListItem>();
|
||||
IEnumerable<IListItem> newApps = Enumerable.Empty<IListItem>();
|
||||
|
||||
if (_filteredItems is not null)
|
||||
{
|
||||
newFilteredItems = _filteredItems.Select(s => s.Item);
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_filteredApps is not null)
|
||||
{
|
||||
newApps = _filteredApps.Select(s => s.Item);
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_fallbackItems is not null)
|
||||
{
|
||||
newFallbacks = _fallbackItems.Select(s => s.Item);
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have any previous filter results to work with, start
|
||||
// with a list of all our commands & apps.
|
||||
if (newFilteredItems is null && _filteredApps is null)
|
||||
if (!newFilteredItems.Any() && !newApps.Any())
|
||||
{
|
||||
newFilteredItems = commands;
|
||||
// We're going to start over with our fallbacks
|
||||
newFallbacks = Enumerable.Empty<IListItem>();
|
||||
|
||||
newFilteredItems = commands.Where(s => !s.IsFallback || _specialFallbacks.Contains(s.CommandProviderId));
|
||||
|
||||
// Fallbacks are always included in the list, even if they
|
||||
// don't match the search text. But we don't want to
|
||||
// consider them when filtering the list.
|
||||
newFallbacks = commands.Where(s => s.IsFallback && !_specialFallbacks.Contains(s.CommandProviderId));
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_filteredItemsIncludesApps = _includeApps;
|
||||
|
||||
if (_includeApps)
|
||||
{
|
||||
_allApps = AllAppsCommandProvider.Page.GetItems();
|
||||
newApps = AllAppsCommandProvider.Page.GetItems().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Produce a list of everything that matches the current filter.
|
||||
_filteredItems = ListHelpers.FilterListWithScores<IListItem>(newFilteredItems ?? [], SearchText, ScoreTopLevelItem);
|
||||
|
||||
// Produce a list of filtered apps with the appropriate limit
|
||||
if (_allApps is not null)
|
||||
{
|
||||
_filteredApps = ListHelpers.FilterListWithScores<IListItem>(_allApps, SearchText, ScoreTopLevelItem);
|
||||
// Defaulting scored to 1 but we'll eventually use user rankings
|
||||
_fallbackItems = newFallbacks.Select(f => new Scored<IListItem> { Item = f, Score = 1 });
|
||||
|
||||
var appResultLimit = AllAppsCommandProvider.TopLevelResultLimit;
|
||||
if (appResultLimit >= 0)
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Produce a list of filtered apps with the appropriate limit
|
||||
if (newApps.Any())
|
||||
{
|
||||
var scoredApps = ListHelpers.FilterListWithScores<IListItem>(newApps, SearchText, ScoreTopLevelItem);
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
_filteredApps = _filteredApps.Take(appResultLimit);
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll apply this limit in the GetItems method after merging with commands
|
||||
// but we need to know the limit now to avoid re-scoring apps
|
||||
var appLimit = AllAppsCommandProvider.TopLevelResultLimit;
|
||||
|
||||
_filteredApps = scoredApps;
|
||||
}
|
||||
|
||||
RaiseItemsChanged();
|
||||
|
||||
timer.Stop();
|
||||
Logger.LogDebug($"Filter with '{newSearch}' in {timer.ElapsedMilliseconds}ms");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFallbacks(string newSearch, IReadOnlyList<TopLevelViewModel> commands)
|
||||
private void UpdateFallbacks(string newSearch, IReadOnlyList<TopLevelViewModel> commands, CancellationToken token)
|
||||
{
|
||||
// fire and forget
|
||||
_ = Task.Run(() =>
|
||||
_ = Task.Run(
|
||||
() =>
|
||||
{
|
||||
var needsToUpdate = false;
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var changedVisibility = command.SafeUpdateFallbackTextSynchronous(newSearch);
|
||||
needsToUpdate = needsToUpdate || changedVisibility;
|
||||
}
|
||||
|
||||
if (needsToUpdate)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
});
|
||||
},
|
||||
token);
|
||||
}
|
||||
|
||||
private bool ActuallyLoading()
|
||||
@@ -322,19 +459,19 @@ public partial class MainListPage : DynamicListPage,
|
||||
// * otherwise full weight match
|
||||
var nameMatch = isWhiteSpace ?
|
||||
(title.Contains(query) ? 1 : 0) :
|
||||
StringMatcher.FuzzySearch(query, title).Score;
|
||||
FuzzyStringMatcher.ScoreFuzzy(query, title);
|
||||
|
||||
// Subtitle:
|
||||
// * whitespace query: 1/2 point
|
||||
// * otherwise ~half weight match. Minus a bit, because subtitles tend to be longer
|
||||
var descriptionMatch = isWhiteSpace ?
|
||||
(topLevelOrAppItem.Subtitle.Contains(query) ? .5 : 0) :
|
||||
(StringMatcher.FuzzySearch(query, topLevelOrAppItem.Subtitle).Score - 4) / 2.0;
|
||||
(FuzzyStringMatcher.ScoreFuzzy(query, topLevelOrAppItem.Subtitle) - 4) / 2.0;
|
||||
|
||||
// Extension title: despite not being visible, give the extension name itself some weight
|
||||
// * whitespace query: 0 points
|
||||
// * otherwise more weight than a subtitle, but not much
|
||||
var extensionTitleMatch = isWhiteSpace ? 0 : StringMatcher.FuzzySearch(query, extensionDisplayName).Score / 1.5;
|
||||
var extensionTitleMatch = isWhiteSpace ? 0 : FuzzyStringMatcher.ScoreFuzzy(query, extensionDisplayName) / 1.5;
|
||||
|
||||
var scores = new[]
|
||||
{
|
||||
@@ -397,4 +534,22 @@ public partial class MainListPage : DynamicListPage,
|
||||
private void SettingsChangedHandler(SettingsModel sender, object? args) => HotReloadSettings(sender);
|
||||
|
||||
private void HotReloadSettings(SettingsModel settings) => ShowDetails = settings.ShowAppDetails;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
|
||||
_tlcManager.PropertyChanged -= TlcManager_PropertyChanged;
|
||||
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
|
||||
|
||||
var settings = _serviceProvider.GetService<SettingsModel>();
|
||||
if (settings is not null)
|
||||
{
|
||||
settings.SettingsChanged -= SettingsChangedHandler;
|
||||
}
|
||||
|
||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public partial class ContentTreeViewModel(ITreeContent _tree, WeakReference<IPag
|
||||
|
||||
// This is the content that's actually bound in XAML. We needed a
|
||||
// collection, even if the collection is just a single item.
|
||||
public ObservableCollection<ContentViewModel> Root => [RootContent];
|
||||
public ObservableCollection<ContentViewModel> Root => RootContent is not null ? [RootContent] : [];
|
||||
|
||||
public override void InitializeProperties()
|
||||
{
|
||||
@@ -122,7 +122,7 @@ public partial class ContentTreeViewModel(ITreeContent _tree, WeakReference<IPag
|
||||
if (viewModel is not null)
|
||||
{
|
||||
viewModel.InitializeProperties();
|
||||
newContent.Add(viewModel);
|
||||
newContent.Add((ContentViewModel)viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,16 +94,4 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Just mark it as AOT compatible. Do not publish with AOT now. We need fully test before we really publish it as AOT enabled-->
|
||||
<!--<PropertyGroup>
|
||||
<SelfContained>true</SelfContained>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
--><!-- <DisableRuntimeMarshalling>true</DisableRuntimeMarshalling> --><!--
|
||||
<PublishAot>true</PublishAot>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
</PropertyGroup>-->
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
<ResourceDictionary Source="Styles/Button.xaml" />
|
||||
<ResourceDictionary Source="Styles/Colors.xaml" />
|
||||
<ResourceDictionary Source="Styles/TextBlock.xaml" />
|
||||
<ResourceDictionary Source="Styles/TextBox.xaml" />
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<Rectangle
|
||||
Height="1"
|
||||
Margin="-16,-12,-12,-12"
|
||||
Fill="{ThemeResource MenuFlyoutSeparatorThemeBrush}" />
|
||||
Fill="{ThemeResource MenuFlyoutSeparatorBackground}" />
|
||||
</DataTemplate>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -68,11 +68,14 @@
|
||||
<ComboBox
|
||||
Name="FiltersComboBox"
|
||||
x:Uid="FiltersComboBox"
|
||||
MinWidth="200"
|
||||
VerticalAlignment="Center"
|
||||
ItemTemplateSelector="{StaticResource FilterTemplateSelector}"
|
||||
ItemsSource="{x:Bind ViewModel.Filters, Mode=OneWay}"
|
||||
PlaceholderText="Filters"
|
||||
PreviewKeyDown="FiltersComboBox_PreviewKeyDown"
|
||||
SelectedValue="{x:Bind ViewModel.CurrentFilterId, Mode=OneWay}"
|
||||
SelectedValuePath="Id"
|
||||
SelectionChanged="FiltersComboBox_SelectionChanged"
|
||||
Style="{StaticResource ComboBoxStyle}"
|
||||
Visibility="{x:Bind ViewModel.ShouldShowFilters, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Controls;
|
||||
|
||||
public sealed partial class IconMarginConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
// Only include a margin if there is text to separate from the icon.
|
||||
var text = value as string;
|
||||
return string.IsNullOrEmpty(text) ? new Thickness(0) : new Thickness(0, 0, 4, 0);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
|
||||
}
|
||||
@@ -32,10 +32,22 @@ public sealed partial class SearchBar : UserControl,
|
||||
private readonly DispatcherQueueTimer _debounceTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
|
||||
private bool _isBackspaceHeld;
|
||||
|
||||
// Inline text suggestions
|
||||
// In 0.4-0.5 we would replace the text of the search box with the TextToSuggest
|
||||
// This was really cool for navigating paths in run and pretty much nowhere else.
|
||||
// We'll have to try another approach, but for now, the code is still testable.
|
||||
// You can test this by setting the CMDPAL_ENABLE_SUGGESTION_SELECTION env var to 1
|
||||
private bool _inSuggestion;
|
||||
|
||||
private bool InSuggestion => _inSuggestion && IsTextToSuggestEnabled;
|
||||
|
||||
private string? _lastText;
|
||||
|
||||
private string? _deletedSuggestion;
|
||||
|
||||
// 0.6+ suggestions
|
||||
private string? _textToSuggest;
|
||||
|
||||
public PageViewModel? CurrentPageViewModel
|
||||
{
|
||||
get => (PageViewModel?)GetValue(CurrentPageViewModelProperty);
|
||||
@@ -131,6 +143,11 @@ public sealed partial class SearchBar : UserControl,
|
||||
WeakReferenceMessenger.Default.Send<OpenContextMenuMessage>(new OpenContextMenuMessage(null, null, null, ContextMenuFilterLocation.Bottom));
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (ctrlPressed && e.Key == VirtualKey.I)
|
||||
{
|
||||
// Today you learned that Ctrl+I in a TextBox will insert a tab
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Escape)
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilterBox.Text))
|
||||
@@ -189,7 +206,23 @@ public sealed partial class SearchBar : UserControl,
|
||||
}
|
||||
else if (e.Key == VirtualKey.Right)
|
||||
{
|
||||
if (_inSuggestion)
|
||||
// Check if the "replace search text with suggestion" feature from 0.4-0.5 is enabled.
|
||||
// If it isn't, then only use the suggestion when the caret is at the end of the input.
|
||||
if (!IsTextToSuggestEnabled)
|
||||
{
|
||||
if (_textToSuggest != null &&
|
||||
FilterBox.SelectionStart == FilterBox.Text.Length)
|
||||
{
|
||||
FilterBox.Text = _textToSuggest;
|
||||
FilterBox.Select(_textToSuggest.Length, 0);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Here, we're using the "replace search text with suggestion" feature.
|
||||
if (InSuggestion)
|
||||
{
|
||||
_inSuggestion = false;
|
||||
_lastText = null;
|
||||
@@ -203,7 +236,7 @@ public sealed partial class SearchBar : UserControl,
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
if (_inSuggestion)
|
||||
if (InSuggestion)
|
||||
{
|
||||
if (
|
||||
e.Key == VirtualKey.Back ||
|
||||
@@ -289,7 +322,7 @@ public sealed partial class SearchBar : UserControl,
|
||||
return;
|
||||
}
|
||||
|
||||
if (_inSuggestion)
|
||||
if (InSuggestion)
|
||||
{
|
||||
// Logger.LogInfo($"-- skipping, in suggestion --");
|
||||
return;
|
||||
@@ -311,7 +344,7 @@ public sealed partial class SearchBar : UserControl,
|
||||
|
||||
private void DoFilterBoxUpdate()
|
||||
{
|
||||
if (_inSuggestion)
|
||||
if (InSuggestion)
|
||||
{
|
||||
// Logger.LogInfo($"--- skipping ---");
|
||||
return;
|
||||
@@ -363,6 +396,12 @@ public sealed partial class SearchBar : UserControl,
|
||||
|
||||
public void Receive(UpdateSuggestionMessage message)
|
||||
{
|
||||
if (!IsTextToSuggestEnabled)
|
||||
{
|
||||
_textToSuggest = message.TextToSuggest;
|
||||
return;
|
||||
}
|
||||
|
||||
var suggestion = message.TextToSuggest;
|
||||
|
||||
_queue.TryEnqueue(new(() =>
|
||||
@@ -452,4 +491,15 @@ public sealed partial class SearchBar : UserControl,
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static bool IsTextToSuggestEnabled => _textToSuggestEnabled.Value;
|
||||
|
||||
private static Lazy<bool> _textToSuggestEnabled = new(() => QueryTextToSuggestEnabled());
|
||||
|
||||
private static bool QueryTextToSuggestEnabled()
|
||||
{
|
||||
var env = System.Environment.GetEnvironmentVariable("CMDPAL_ENABLE_SUGGESTION_SELECTION");
|
||||
return !string.IsNullOrEmpty(env) &&
|
||||
(env == "1" || env.Equals("true", System.StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.CmdPal.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:labToolkit="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
x:Name="ShortcutContentControl"
|
||||
mc:Ignorable="d">
|
||||
<Grid MinWidth="498" MinHeight="220">
|
||||
@@ -66,7 +66,7 @@
|
||||
IsTabStop="{Binding ElementName=ShortcutContentControl, Path=IsWarningAltGr, Mode=OneWay}"
|
||||
Severity="Warning" />
|
||||
</Grid>
|
||||
<labToolkit:MarkdownTextBlock
|
||||
<tkcontrols:MarkdownTextBlock
|
||||
x:Uid="InvalidShortcutWarningLabel"
|
||||
Background="Transparent"
|
||||
FontSize="12"
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.CmdPal.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:labToolkit="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
@@ -36,7 +36,7 @@
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<labToolkit:MarkdownTextBlock
|
||||
<tkcontrols:MarkdownTextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
<Thickness x:Key="TagPadding">4,2,4,2</Thickness>
|
||||
<Thickness x:Key="TagBorderThickness">1</Thickness>
|
||||
|
||||
<local:IconMarginConverter x:Key="IconMarginConverter" />
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultTagStyle}" TargetType="local:Tag" />
|
||||
|
||||
<Style x:Key="DefaultTagStyle" TargetType="local:Tag">
|
||||
@@ -71,7 +73,7 @@
|
||||
x:Name="PART_Icon"
|
||||
Grid.Column="0"
|
||||
Height="12"
|
||||
Margin="0,0,4,0"
|
||||
Margin="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IconMarginConverter}}"
|
||||
SourceKey="{TemplateBinding Icon}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
|
||||
@@ -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.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CmdPal.Core.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
@@ -14,6 +15,7 @@ internal sealed partial class FilterTemplateSelector : DataTemplateSelector
|
||||
|
||||
public DataTemplate? Separator { get; set; }
|
||||
|
||||
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "Microsoft.UI.Xaml.Controls.ComboBoxItem", "Microsoft.WinUI")]
|
||||
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject dependencyObject)
|
||||
{
|
||||
DataTemplate? dataTemplate = Default;
|
||||
|
||||
@@ -11,22 +11,20 @@
|
||||
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:coreViewModels="using:Microsoft.CmdPal.Core.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:labToolkit="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
|
||||
xmlns:local="using:Microsoft.CmdPal.UI"
|
||||
xmlns:markdownTextBlockRns="using:CommunityToolkit.WinUI.Controls.MarkdownTextBlockRns"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
|
||||
Background="Transparent"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<markdownTextBlockRns:MarkdownThemes
|
||||
<tkcontrols:MarkdownThemes
|
||||
x:Key="DefaultMarkdownThemeConfig"
|
||||
H3FontSize="12"
|
||||
H3FontWeight="Normal" />
|
||||
<labToolkit:MarkdownConfig x:Key="DefaultMarkdownConfig" Themes="{StaticResource DefaultMarkdownThemeConfig}" />
|
||||
<tkcontrols:MarkdownConfig x:Key="DefaultMarkdownConfig" Themes="{StaticResource DefaultMarkdownThemeConfig}" />
|
||||
|
||||
<StackLayout
|
||||
x:Name="VerticalStackLayout"
|
||||
@@ -52,10 +50,11 @@
|
||||
|
||||
<DataTemplate x:Key="MarkdownContentTemplate" x:DataType="viewModels:ContentMarkdownViewModel">
|
||||
<Grid Margin="0,4,4,4" Padding="12,8,8,8">
|
||||
<labToolkit:MarkdownTextBlock
|
||||
Background="Transparent"
|
||||
<tkcontrols:MarkdownTextBlock
|
||||
Config="{StaticResource DefaultMarkdownConfig}"
|
||||
Text="{x:Bind Body, Mode=OneWay}" />
|
||||
Text="{x:Bind Body, Mode=OneWay}"
|
||||
UseEmphasisExtras="True"
|
||||
UsePipeTables="True" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -67,10 +66,7 @@
|
||||
|
||||
<DataTemplate x:Key="NestedMarkdownContentTemplate" x:DataType="viewModels:ContentMarkdownViewModel">
|
||||
<Grid>
|
||||
<labToolkit:MarkdownTextBlock
|
||||
Background="Transparent"
|
||||
Config="{StaticResource DefaultMarkdownConfig}"
|
||||
Text="{x:Bind Body, Mode=OneWay}" />
|
||||
<tkcontrols:MarkdownTextBlock Config="{StaticResource DefaultMarkdownConfig}" Text="{x:Bind Body, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
|
||||
@@ -55,7 +55,10 @@ public static class TypedEventHandlerExtensions
|
||||
.OfType<TypedEventHandler<S, R>>()
|
||||
.Select(invocationDelegate =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return Task.FromCanceled(cancellationToken);
|
||||
}
|
||||
|
||||
invocationDelegate(sender, eventArgs);
|
||||
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Helpers;
|
||||
|
||||
public static class WindowExtensions
|
||||
internal static class WindowExtensions
|
||||
{
|
||||
public static void SetIcon(this Window window)
|
||||
{
|
||||
@@ -18,18 +22,59 @@ public static class WindowExtensions
|
||||
appWindow.SetIcon(@"Assets\icon.ico");
|
||||
}
|
||||
|
||||
public static void SetVisibilityInSwitchers(this Window window, bool showInSwitchers)
|
||||
private static HWND GetWindowHwnd(this Window window)
|
||||
{
|
||||
try
|
||||
return window is null
|
||||
? throw new ArgumentNullException(nameof(window))
|
||||
: new HWND(WinRT.Interop.WindowNative.GetWindowHandle(window));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the specified extended window style on or off for the supplied <see cref="Window"/>.
|
||||
/// </summary>
|
||||
/// <param name="window">The <see cref="Window"/> whose extended window styles will be modified. Cannot be null.</param>
|
||||
/// <param name="style">The <see cref="WINDOW_EX_STYLE"/> flag(s) to set or clear.</param>
|
||||
/// <param name="isStyleSet">When true, the specified <paramref name="style"/> bit(s) will be set (added). When false, the bit(s) will be cleared (removed).</param>
|
||||
/// <returns>True if the call to SetWindowLong succeeded and the style was applied; otherwise false.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="window"/> is null.</exception>
|
||||
internal static bool ToggleExtendedWindowStyle(this Window window, WINDOW_EX_STYLE style, bool isStyleSet)
|
||||
{
|
||||
var hWnd = GetWindowHwnd(window);
|
||||
var currentStyle = PInvoke.GetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
|
||||
|
||||
if (isStyleSet)
|
||||
{
|
||||
// IsShownInSwitchers needs to change the value to apply the effect, but its state might be out-of-sync with
|
||||
// the actual state of the switchers, so we need to toggle it.
|
||||
window.AppWindow.IsShownInSwitchers = !showInSwitchers;
|
||||
window.AppWindow.IsShownInSwitchers = showInSwitchers;
|
||||
currentStyle |= (int)style;
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
else
|
||||
{
|
||||
// Setting IsShownInSwitchers failed. This can happen if the Explorer is not running.
|
||||
currentStyle &= ~(int)style;
|
||||
}
|
||||
|
||||
return PInvoke.SetWindowLong(hWnd, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, currentStyle) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the window corner preference
|
||||
/// </summary>
|
||||
/// <param name="window">The window</param>
|
||||
/// <param name="cornerPreference">The desired corner preference</param>
|
||||
/// <returns>True if the operation succeeded</returns>
|
||||
public static bool SetCornerPreference(this Window window, DWM_WINDOW_CORNER_PREFERENCE cornerPreference)
|
||||
{
|
||||
return window.GetWindowHwnd().SetDwmWindowAttribute(DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, cornerPreference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unified wrapper for DwmSetWindowAttribute calls with enum values
|
||||
/// </summary>
|
||||
private static bool SetDwmWindowAttribute<T>(this HWND hwnd, DWMWINDOWATTRIBUTE attribute, T value)
|
||||
where T : unmanaged, Enum
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var result = PInvoke.DwmSetWindowAttribute(hwnd, attribute, &value, (uint)sizeof(T));
|
||||
return result.Succeeded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,16 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
// Force window to be created, and then cloaked. This will offset initial animation when the window is shown.
|
||||
HideWindow();
|
||||
|
||||
ApplyWindowStyle();
|
||||
}
|
||||
|
||||
private void ApplyWindowStyle()
|
||||
{
|
||||
// Tool windows don't show up in ALT+TAB, and don't show up in the taskbar
|
||||
// Since tool windows have smaller corner radii, we need to force the normal ones
|
||||
this.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW, !Debugger.IsAttached);
|
||||
this.SetCornerPreference(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND);
|
||||
}
|
||||
|
||||
private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
|
||||
@@ -173,8 +183,6 @@ public sealed partial class MainWindow : WindowEx,
|
||||
App.Current.Services.GetService<TrayIconService>()!.SetupTrayIcon(settings.ShowSystemTrayIcon);
|
||||
|
||||
_ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen;
|
||||
|
||||
this.SetVisibilityInSwitchers(Debugger.IsAttached);
|
||||
}
|
||||
|
||||
// We want to use DesktopAcrylicKind.Thin and custom colors as this is the default material
|
||||
@@ -252,6 +260,13 @@ public sealed partial class MainWindow : WindowEx,
|
||||
var display = GetScreen(hwnd, target);
|
||||
PositionCentered(display);
|
||||
|
||||
// Check if the debugger is attached. If it is, we don't want to apply the tool window style,
|
||||
// because that would make it hard to debug the app
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
ApplyWindowStyle();
|
||||
}
|
||||
|
||||
// Just to be sure, SHOW our hwnd.
|
||||
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_SHOW);
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
|
||||
<AppxBundle>Never</AppxBundle>
|
||||
<AppxPackageTestDir>$(OutputPath)\AppPackages\Microsoft.CmdPal.UI_$(Version)_Test\</AppxPackageTestDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -72,7 +74,6 @@
|
||||
<None Remove="Pages\Settings\GeneralPage.xaml" />
|
||||
<None Remove="SettingsWindow.xaml" />
|
||||
<None Remove="ShellPage.xaml" />
|
||||
<None Remove="Styles\Button.xaml" />
|
||||
<None Remove="Styles\Colors.xaml" />
|
||||
<None Remove="Styles\Settings.xaml" />
|
||||
<None Remove="Styles\TextBox.xaml" />
|
||||
@@ -97,13 +98,6 @@
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
|
||||
<PackageReference Include="System.Net.Http" />
|
||||
|
||||
<PackageReference Include="System.Private.Uri" />
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
|
||||
<PackageReference Include="System.Text.RegularExpressions" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
@@ -167,9 +161,6 @@
|
||||
<Page Update="Controls\SearchBar.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Update="Styles\Button.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Styles\TextBox.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
|
||||
@@ -44,6 +44,7 @@ MessageBox
|
||||
DwmGetWindowAttribute
|
||||
DwmSetWindowAttribute
|
||||
DWM_CLOAKED_APP
|
||||
DWM_WINDOW_CORNER_PREFERENCE
|
||||
|
||||
CoWaitForMultipleObjects
|
||||
INFINITE
|
||||
@@ -53,4 +54,8 @@ GetCurrentThreadId
|
||||
SetWindowsHookEx
|
||||
UnhookWindowsHookEx
|
||||
CallNextHookEx
|
||||
GetModuleHandle
|
||||
GetModuleHandle
|
||||
|
||||
GetWindowLong
|
||||
SetWindowLong
|
||||
WINDOW_EX_STYLE
|
||||
@@ -10,9 +10,8 @@
|
||||
xmlns:cpcontrols="using:Microsoft.CmdPal.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:help="using:Microsoft.CmdPal.UI.Helpers"
|
||||
xmlns:labToolkit="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
|
||||
xmlns:markdownTextBlockRns="using:CommunityToolkit.WinUI.Controls.MarkdownTextBlockRns"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
|
||||
@@ -147,11 +146,11 @@
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<markdownTextBlockRns:MarkdownThemes
|
||||
<tkcontrols:MarkdownThemes
|
||||
x:Key="DefaultMarkdownThemeConfig"
|
||||
H3FontSize="12"
|
||||
H3FontWeight="Normal" />
|
||||
<labToolkit:MarkdownConfig x:Key="DefaultMarkdownConfig" Themes="{StaticResource DefaultMarkdownThemeConfig}" />
|
||||
<tkcontrols:MarkdownConfig x:Key="DefaultMarkdownConfig" Themes="{StaticResource DefaultMarkdownThemeConfig}" />
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
@@ -327,11 +326,6 @@
|
||||
x:Name="FiltersDropDown"
|
||||
HorizontalAlignment="Right"
|
||||
CurrentPageViewModel="{x:Bind ViewModel.CurrentPage, Mode=OneWay}" />
|
||||
<Grid.Transitions>
|
||||
<TransitionCollection>
|
||||
<RepositionThemeTransition />
|
||||
</TransitionCollection>
|
||||
</Grid.Transitions>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -427,12 +421,14 @@
|
||||
TextWrapping="WrapWholeWords"
|
||||
Visibility="{x:Bind ViewModel.Details.Title, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}" />
|
||||
|
||||
<labToolkit:MarkdownTextBlock
|
||||
<tkcontrols:MarkdownTextBlock
|
||||
Grid.Row="2"
|
||||
Margin="0,4,0,24"
|
||||
Background="Transparent"
|
||||
Config="{StaticResource DefaultMarkdownConfig}"
|
||||
Text="{x:Bind ViewModel.Details.Body, Mode=OneWay}" />
|
||||
Text="{x:Bind ViewModel.Details.Body, Mode=OneWay}"
|
||||
UseEmphasisExtras="True"
|
||||
UsePipeTables="True" />
|
||||
|
||||
<ItemsRepeater
|
||||
Grid.Row="3"
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SystemColorHighlightTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SystemColorWindowColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SystemColorHighlightColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SystemColorGrayTextColor" />
|
||||
|
||||
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="SystemColorButtonTextColorBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
<Style x:Key="SubtleButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
|
||||
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AnimatedIcon.State="Normal"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<ContentPresenter.BackgroundTransition>
|
||||
<BrushTransition Duration="0:0:0.083" />
|
||||
</ContentPresenter.BackgroundTransition>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<VisualState.Setters>
|
||||
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
|
||||
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -12,6 +12,7 @@ using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.Graphics.Gdi;
|
||||
using Windows.Win32.UI.HiDpi;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
@@ -33,13 +34,18 @@ public sealed partial class ToastWindow : WindowEx,
|
||||
{
|
||||
this.InitializeComponent();
|
||||
AppWindow.Hide();
|
||||
this.SetVisibilityInSwitchers(false);
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
AppWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
|
||||
this.SetIcon();
|
||||
AppWindow.Title = RS_.GetString("ToastWindowTitle");
|
||||
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
||||
|
||||
// Tool windows don't show up in ALT+TAB, and don't show up in the taskbar
|
||||
// Since tool windows have smaller corner radii, we need to force the normal ones
|
||||
// to visually match system toasts.
|
||||
this.ToggleExtendedWindowStyle(WINDOW_EX_STYLE.WS_EX_TOOLWINDOW, true);
|
||||
this.SetCornerPreference(DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND);
|
||||
|
||||
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
||||
PInvoke.EnableWindow(_hwnd, false);
|
||||
|
||||
|
||||
@@ -3,9 +3,15 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup>
|
||||
<PathToRoot>..\..\..\..\</PathToRoot>
|
||||
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250513003</WasdkNuget>
|
||||
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
@@ -200,6 +206,12 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
@@ -210,6 +222,18 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', 'Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
|
||||
@@ -4,4 +4,14 @@
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -2,11 +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.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -14,7 +10,9 @@ namespace Microsoft.CmdPal.Ext.UnitTestBase;
|
||||
|
||||
public class CommandPaletteUnitTestBase
|
||||
{
|
||||
private bool MatchesFilter(string filter, IListItem item) => StringMatcher.FuzzySearch(filter, item.Title).Success || StringMatcher.FuzzySearch(filter, item.Subtitle).Success;
|
||||
private bool MatchesFilter(string filter, IListItem item) =>
|
||||
FuzzyStringMatcher.ScoreFuzzy(filter, item.Title) > 0 ||
|
||||
FuzzyStringMatcher.ScoreFuzzy(filter, item.Subtitle) > 0;
|
||||
|
||||
public IListItem[] Query(string query, IListItem[] candidates)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user