1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,6 @@
|
||||
name: "🕷️ Bug report"
|
||||
description: Report errors or unexpected behavior
|
||||
type: Bug
|
||||
labels:
|
||||
- Issue-Bug
|
||||
- Needs-Triage
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,5 +1,6 @@
|
||||
name: "⭐ Feature or enhancement request"
|
||||
description: Propose something new.
|
||||
type: Feature
|
||||
labels:
|
||||
- Needs-Triage
|
||||
body:
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/translation_issue.yml
vendored
@@ -1,5 +1,6 @@
|
||||
name: "🌐 Localization/Translation issue"
|
||||
description: Report incorrect translations.
|
||||
type: Bug
|
||||
labels:
|
||||
- Issue-Bug
|
||||
- Area-Localization
|
||||
|
||||
2
.github/actions/spell-check/allow/names.txt
vendored
@@ -28,6 +28,8 @@ videoconference
|
||||
|
||||
# USERS
|
||||
|
||||
# 8LWXpg is user name but user folder causes a flag
|
||||
LWXpg
|
||||
Adoumie
|
||||
Advaith
|
||||
alekhyareddy
|
||||
|
||||
4
.github/actions/spell-check/expect.txt
vendored
@@ -2,8 +2,6 @@
|
||||
## "PackagemanagerWrapper.cs" should be "PackageManagerWrapper.cs"
|
||||
## NOTICE.MD > MOZILLA PUBLIC LICENSE v1.1
|
||||
|
||||
# user name but user folder causes a flag
|
||||
8LWXpg
|
||||
aaaa
|
||||
abcdefghjkmnpqrstuvxyz
|
||||
abgr
|
||||
@@ -32,7 +30,6 @@ AFFINETRANSFORM
|
||||
AFX
|
||||
AGGREGATABLE
|
||||
AHybrid
|
||||
AKV
|
||||
akv
|
||||
ALarger
|
||||
ALLAPPS
|
||||
@@ -1912,6 +1909,7 @@ XFile
|
||||
XIncrement
|
||||
XLoc
|
||||
XNamespace
|
||||
Xoshiro
|
||||
XPels
|
||||
XPixel
|
||||
xplorer
|
||||
|
||||
@@ -166,6 +166,7 @@
|
||||
"PowerToys.FindMyMouse.dll",
|
||||
"PowerToys.MouseHighlighter.dll",
|
||||
"PowerToys.MouseJump.dll",
|
||||
"PowerToys.MouseJump.Common.dll",
|
||||
"PowerToys.MousePointerCrosshairs.dll",
|
||||
"PowerToys.MouseJumpUI.dll",
|
||||
"PowerToys.MouseJumpUI.exe",
|
||||
|
||||
@@ -117,7 +117,10 @@ else
|
||||
{
|
||||
Write-Error 'XAML Styling is incorrect, please run `.\.pipelines\applyXamlStyling.ps1 -Main` locally.'
|
||||
}
|
||||
|
||||
if ($lastExitCode -lt 0)
|
||||
{
|
||||
Write-Error "Error running dotnet tool run, with the exit code $lastExitCode. Please verify logs and running environment."
|
||||
}
|
||||
# Return XAML Styler Status
|
||||
exit $lastExitCode
|
||||
}
|
||||
|
||||
@@ -4,22 +4,22 @@ trigger:
|
||||
include:
|
||||
- main
|
||||
- stable
|
||||
paths:
|
||||
exclude:
|
||||
- doc/*
|
||||
- temp/*
|
||||
- tools/*
|
||||
- '**.md'
|
||||
# paths:
|
||||
# exclude:
|
||||
# - doc/*
|
||||
# - temp/*
|
||||
# - tools/*
|
||||
# - '**.md'
|
||||
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- stable
|
||||
paths:
|
||||
exclude:
|
||||
- '**.md'
|
||||
- doc
|
||||
# paths:
|
||||
# exclude:
|
||||
# - '**.md'
|
||||
# - doc
|
||||
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
|
||||
|
||||
@@ -127,12 +127,10 @@ jobs:
|
||||
Write-Host "##vso[task.setvariable variable=MSBuildCacheParameters]$MSBuildCacheParameters"
|
||||
displayName: Prepare MSBuildCache variables
|
||||
|
||||
- ${{ if eq(parameters.codeSign, true) }}:
|
||||
# Only required if we're using ESRP
|
||||
- template: steps-ensure-dotnet-version.yml
|
||||
parameters:
|
||||
sdk: true
|
||||
version: '6.0'
|
||||
- template: steps-ensure-dotnet-version.yml
|
||||
parameters:
|
||||
sdk: true
|
||||
version: '6.0'
|
||||
|
||||
- template: steps-ensure-dotnet-version.yml
|
||||
parameters:
|
||||
|
||||
@@ -15,7 +15,7 @@ Param(
|
||||
$referencedFileVersionsPerDll = @{}
|
||||
$totalFailures = 0
|
||||
|
||||
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones* | ForEach-Object {
|
||||
Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones*,MouseJump.Common.UnitTests* | ForEach-Object {
|
||||
# Temporarily exclude FancyZones UI tests because of Appium.WebDriver dependencies
|
||||
$depsJsonFullFileName = $_.FullName
|
||||
$depsJsonFileName = $_.Name
|
||||
|
||||
@@ -8,6 +8,12 @@ Write-Host "Verifying Nuget packages for $solution"
|
||||
|
||||
dotnet tool restore
|
||||
dotnet consolidate -s $solution
|
||||
if ($lastExitCode -ne 0)
|
||||
{
|
||||
$result = $lastExitCode
|
||||
Write-Error "Error running dotnet consolidate, with the exit code $lastExitCode. Please verify logs and running environment."
|
||||
exit $result
|
||||
}
|
||||
|
||||
if (-not $?)
|
||||
{
|
||||
|
||||
@@ -38,16 +38,16 @@
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2739.15" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="8.0.7" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="8.0.10" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
|
||||
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
|
||||
<!--
|
||||
TODO: in Common.Dotnet.CsWinRT.props, on upgrade, verify RemoveCsWinRTPackageAnalyzer is no longer needed.
|
||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.1" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.240829007" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" />
|
||||
<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" />
|
||||
@@ -66,30 +66,27 @@
|
||||
<PackageVersion Include="System.CodeDom" Version="8.0.0" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="8.0.1" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="8.0.1" />
|
||||
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
|
||||
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
|
||||
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="8.0.0" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.6" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="8.0.1" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="8.0.7" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="17.2.3" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="17.2.3" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="8.0.0" />
|
||||
<!-- Package System.Security.Cryptography.ProtectedData added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="8.0.1" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="8.0.1" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
<PackageVersion Include="UnitsNet" Version="5.56.0" />
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
|
||||
<PackageVersion Include="WinUIEx" Version="2.2.0" />
|
||||
<PackageVersion Include="WPF-UI" Version="3.0.0" />
|
||||
<PackageVersion Include="WPF-UI" Version="3.0.5" />
|
||||
|
||||
<!-- TODO! Mike see which of these we actually need, and -->
|
||||
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
|
||||
|
||||
24
NOTICE.md
@@ -1327,11 +1327,11 @@ EXHIBIT A -Mozilla Public License.
|
||||
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
|
||||
- Microsoft.Web.WebView2 1.0.2739.15
|
||||
- Microsoft.Win32.SystemEvents 8.0.0
|
||||
- Microsoft.Windows.Compatibility 8.0.7
|
||||
- Microsoft.Windows.Compatibility 8.0.10
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.1.1
|
||||
- Microsoft.Windows.CsWinRT 2.1.5
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
- Microsoft.WindowsAppSDK 1.6.240829007
|
||||
- Microsoft.WindowsAppSDK 1.6.240923002
|
||||
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
|
||||
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
|
||||
- ModernWpfUI 0.9.4
|
||||
@@ -1347,23 +1347,21 @@ EXHIBIT A -Mozilla Public License.
|
||||
- System.CodeDom 8.0.0
|
||||
- System.CommandLine 2.0.0-beta4.22272.1
|
||||
- System.ComponentModel.Composition 8.0.0
|
||||
- System.Configuration.ConfigurationManager 8.0.0
|
||||
- System.Data.OleDb 8.0.0
|
||||
- System.Configuration.ConfigurationManager 8.0.1
|
||||
- System.Data.OleDb 8.0.1
|
||||
- System.Data.SqlClient 4.8.6
|
||||
- System.Diagnostics.EventLog 8.0.0
|
||||
- System.Diagnostics.PerformanceCounter 8.0.0
|
||||
- System.Drawing.Common 8.0.6
|
||||
- System.Diagnostics.EventLog 8.0.1
|
||||
- System.Drawing.Common 8.0.7
|
||||
- System.IO.Abstractions 17.2.3
|
||||
- System.IO.Abstractions.TestingHelpers 17.2.3
|
||||
- System.Management 8.0.0
|
||||
- System.Reactive 6.0.1
|
||||
- System.Runtime.Caching 8.0.0
|
||||
- System.Security.Cryptography.ProtectedData 8.0.0
|
||||
- System.ServiceProcess.ServiceController 8.0.0
|
||||
- System.Runtime.Caching 8.0.1
|
||||
- System.ServiceProcess.ServiceController 8.0.1
|
||||
- System.Text.Encoding.CodePages 8.0.0
|
||||
- System.Text.Json 8.0.4
|
||||
- System.Text.Json 8.0.5
|
||||
- UnicodeInformation 2.6.0
|
||||
- UnitsNet 5.56.0
|
||||
- UTF.Unknown 2.5.1
|
||||
- WinUIEx 2.2.0
|
||||
- WPF-UI 3.0.0
|
||||
- WPF-UI 3.0.5
|
||||
|
||||
@@ -504,9 +504,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBordersHelper",
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouseJump", "src\modules\MouseUtils\MouseJump\MouseJump.vcxproj", "{8A08D663-4995-40E3-B42C-3F910625F284}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI", "src\modules\MouseUtils\MouseJumpUI\MouseJumpUI.csproj", "{D962A009-834F-4EEC-AABB-430DF8F98E39}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJump.Common", "src\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj", "{923DF87C-CA99-4D1C-B1D2-959174E95BFA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI.UnitTests", "src\modules\MouseUtils\MouseJumpUI.UnitTests\MouseJumpUI.UnitTests.csproj", "{D9C5DE64-6849-4278-91AD-9660AECF2876}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJump.Common.UnitTests", "src\modules\MouseUtils\MouseJump.Common.UnitTests\MouseJump.Common.UnitTests.csproj", "{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI", "src\modules\MouseUtils\MouseJumpUI\MouseJumpUI.csproj", "{D962A009-834F-4EEC-AABB-430DF8F98E39}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AdvancedPaste", "AdvancedPaste", "{9873BA05-4C41-4819-9283-CF45D795431B}"
|
||||
EndProject
|
||||
@@ -622,6 +624,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLauncher", "src\m
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesWindowArranger", "src\modules\Workspaces\WorkspacesWindowArranger\WorkspacesWindowArranger.vcxproj", "{37D07516-4185-43A4-924F-3C7A5D95ECF6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2250,18 +2254,6 @@ Global
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x64.Build.0 = Release|x64
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x86.ActiveCfg = Release|x64
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x86.Build.0 = Release|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x64.Build.0 = Debug|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x86.Build.0 = Debug|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x64.ActiveCfg = Release|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x64.Build.0 = Release|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x86.ActiveCfg = Release|x64
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x86.Build.0 = Release|x64
|
||||
{FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2650,6 +2642,30 @@ Global
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2734,6 +2750,18 @@ Global
|
||||
{37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x64.Build.0 = Release|x64
|
||||
{37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x86.ActiveCfg = Release|x64
|
||||
{37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x86.Build.0 = Release|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Debug|x64.Build.0 = Debug|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Debug|x86.Build.0 = Debug|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.ActiveCfg = Release|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2913,7 +2941,6 @@ Global
|
||||
{A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
|
||||
{8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{D9C5DE64-6849-4278-91AD-9660AECF2876} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B}
|
||||
{9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482}
|
||||
@@ -2952,6 +2979,8 @@ Global
|
||||
{8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
|
||||
{CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E}
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
@@ -2962,6 +2991,7 @@ Global
|
||||
{367D7543-7DBA-4381-99F1-BF6142A996C4} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -56,3 +56,5 @@ Below are community created plugins that target a website or software. They are
|
||||
| [Scoop](https://github.com/Quriz/PowerToysRunScoop) | [Quriz](https://github.com/Quriz) | Search and install packages from Scoop |
|
||||
| [Spotify](https://github.com/waaverecords/PowerToys-Run-Spotify) | [waaverecords](https://github.com/waaverecords) | Search Spotify and control its player |
|
||||
| [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords |
|
||||
| [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes |
|
||||
| [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!-- Some items may be set in Directory.Build.props in root -->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<WindowsSdkPackageVersion>10.0.22621.38</WindowsSdkPackageVersion>
|
||||
<WindowsSdkPackageVersion>10.0.22621.48</WindowsSdkPackageVersion>
|
||||
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
|
||||
@@ -60,14 +60,29 @@ namespace ManagedCommon
|
||||
|
||||
public static void LogError(string message, Exception ex)
|
||||
{
|
||||
Log(
|
||||
message + Environment.NewLine +
|
||||
ex?.Message + Environment.NewLine +
|
||||
"Inner exception: " + Environment.NewLine +
|
||||
ex?.InnerException?.Message + Environment.NewLine +
|
||||
"Stack trace: " + Environment.NewLine +
|
||||
ex?.StackTrace,
|
||||
Error);
|
||||
if (ex == null)
|
||||
{
|
||||
LogError(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
var exMessage =
|
||||
message + Environment.NewLine +
|
||||
ex.GetType() + ": " + ex.Message + Environment.NewLine;
|
||||
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
exMessage +=
|
||||
"Inner exception: " + Environment.NewLine +
|
||||
ex.InnerException.GetType() + ": " + ex.InnerException.Message + Environment.NewLine;
|
||||
}
|
||||
|
||||
exMessage +=
|
||||
"Stack trace: " + Environment.NewLine +
|
||||
ex.StackTrace;
|
||||
|
||||
Log(exMessage, Error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
|
||||
@@ -63,6 +63,10 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
{
|
||||
return CommonSharedConstants::ADVANCED_PASTE_JSON_MESSAGE;
|
||||
}
|
||||
hstring Constants::AdvancedPasteAdditionalActionMessage()
|
||||
{
|
||||
return CommonSharedConstants::ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE;
|
||||
}
|
||||
hstring Constants::AdvancedPasteCustomActionMessage()
|
||||
{
|
||||
return CommonSharedConstants::ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE;
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
static hstring AdvancedPasteShowUIMessage();
|
||||
static hstring AdvancedPasteMarkdownMessage();
|
||||
static hstring AdvancedPasteJsonMessage();
|
||||
static hstring AdvancedPasteAdditionalActionMessage();
|
||||
static hstring AdvancedPasteCustomActionMessage();
|
||||
static hstring ShowPowerOCRSharedEvent();
|
||||
static hstring MouseJumpShowPreviewEvent();
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace PowerToys
|
||||
static String AdvancedPasteShowUIMessage();
|
||||
static String AdvancedPasteMarkdownMessage();
|
||||
static String AdvancedPasteJsonMessage();
|
||||
static String AdvancedPasteAdditionalActionMessage();
|
||||
static String AdvancedPasteCustomActionMessage();
|
||||
static String ShowPowerOCRSharedEvent();
|
||||
static String MouseJumpShowPreviewEvent();
|
||||
|
||||
@@ -32,6 +32,8 @@ namespace CommonSharedConstants
|
||||
|
||||
const wchar_t ADVANCED_PASTE_JSON_MESSAGE[] = L"PasteJson";
|
||||
|
||||
const wchar_t ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE[] = L"AdditionalAction";
|
||||
|
||||
const wchar_t ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE[] = L"CustomAction";
|
||||
|
||||
// Path to the event used to show Color Picker
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Services;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.ViewModels;
|
||||
using ManagedCommon;
|
||||
@@ -34,6 +38,13 @@ namespace AdvancedPaste
|
||||
{
|
||||
public IHost Host { get; private set; }
|
||||
|
||||
private static readonly Dictionary<string, PasteFormats> AdditionalActionIPCKeys =
|
||||
typeof(PasteFormats).GetFields()
|
||||
.Where(field => field.IsLiteral)
|
||||
.Select(field => (Format: (PasteFormats)field.GetRawConstantValue(), field.GetCustomAttribute<PasteFormatMetadataAttribute>().IPCKey))
|
||||
.Where(field => field.IPCKey != null)
|
||||
.ToDictionary(field => field.IPCKey, field => field.Format);
|
||||
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
private readonly OptionsViewModel viewModel;
|
||||
|
||||
@@ -60,8 +71,10 @@ namespace AdvancedPaste
|
||||
|
||||
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
|
||||
{
|
||||
services.AddSingleton<OptionsViewModel>();
|
||||
services.AddSingleton<IUserSettings, UserSettings>();
|
||||
services.AddSingleton<AICompletionsHelper>();
|
||||
services.AddSingleton<OptionsViewModel>();
|
||||
services.AddSingleton<IPasteFormatExecutor, PasteFormatExecutor>();
|
||||
}).Build();
|
||||
|
||||
viewModel = GetService<OptionsViewModel>();
|
||||
@@ -111,35 +124,35 @@ namespace AdvancedPaste
|
||||
|
||||
private void ProcessNamedPipe(string pipeName)
|
||||
{
|
||||
void OnMessage(string message) => _dispatcherQueue.TryEnqueue(() => OnNamedPipeMessage(message));
|
||||
void OnMessage(string message) => _dispatcherQueue.TryEnqueue(async () => await OnNamedPipeMessage(message));
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var connectTimeout = TimeSpan.FromSeconds(10);
|
||||
await NamedPipeProcessor.ProcessNamedPipeAsync(pipeName, connectTimeout, OnMessage, CancellationToken.None);
|
||||
});
|
||||
Task.Run(async () => await NamedPipeProcessor.ProcessNamedPipeAsync(pipeName, connectTimeout: TimeSpan.FromSeconds(10), OnMessage, CancellationToken.None));
|
||||
}
|
||||
|
||||
private void OnNamedPipeMessage(string message)
|
||||
private async Task OnNamedPipeMessage(string message)
|
||||
{
|
||||
var messageParts = message.Split();
|
||||
var messageType = messageParts.First();
|
||||
|
||||
if (messageType == PowerToys.Interop.Constants.AdvancedPasteShowUIMessage())
|
||||
{
|
||||
OnAdvancedPasteHotkey();
|
||||
await ShowWindow();
|
||||
}
|
||||
else if (messageType == PowerToys.Interop.Constants.AdvancedPasteMarkdownMessage())
|
||||
{
|
||||
OnAdvancedPasteMarkdownHotkey();
|
||||
await viewModel.ExecutePasteFormatAsync(PasteFormats.Markdown, PasteActionSource.GlobalKeyboardShortcut);
|
||||
}
|
||||
else if (messageType == PowerToys.Interop.Constants.AdvancedPasteJsonMessage())
|
||||
{
|
||||
OnAdvancedPasteJsonHotkey();
|
||||
await viewModel.ExecutePasteFormatAsync(PasteFormats.Json, PasteActionSource.GlobalKeyboardShortcut);
|
||||
}
|
||||
else if (messageType == PowerToys.Interop.Constants.AdvancedPasteAdditionalActionMessage())
|
||||
{
|
||||
await OnAdvancedPasteAdditionalActionHotkey(messageParts);
|
||||
}
|
||||
else if (messageType == PowerToys.Interop.Constants.AdvancedPasteCustomActionMessage())
|
||||
{
|
||||
OnAdvancedPasteCustomActionHotkey(messageParts);
|
||||
await OnAdvancedPasteCustomActionHotkey(messageParts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,24 +161,27 @@ namespace AdvancedPaste
|
||||
Logger.LogError("Unhandled exception", e.Exception);
|
||||
}
|
||||
|
||||
private void OnAdvancedPasteJsonHotkey()
|
||||
private async Task OnAdvancedPasteAdditionalActionHotkey(string[] messageParts)
|
||||
{
|
||||
viewModel.ReadClipboard();
|
||||
viewModel.ToJsonFunction(true);
|
||||
if (messageParts.Length != 2)
|
||||
{
|
||||
Logger.LogWarning("Unexpected additional action message");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!AdditionalActionIPCKeys.TryGetValue(messageParts[1], out PasteFormats pasteFormat))
|
||||
{
|
||||
Logger.LogWarning($"Unexpected additional action type {messageParts[1]}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ShowWindow();
|
||||
await viewModel.ExecutePasteFormatAsync(pasteFormat, PasteActionSource.GlobalKeyboardShortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAdvancedPasteMarkdownHotkey()
|
||||
{
|
||||
viewModel.ReadClipboard();
|
||||
viewModel.ToMarkdownFunction(true);
|
||||
}
|
||||
|
||||
private void OnAdvancedPasteHotkey()
|
||||
{
|
||||
ShowWindow();
|
||||
}
|
||||
|
||||
private void OnAdvancedPasteCustomActionHotkey(string[] messageParts)
|
||||
private async Task OnAdvancedPasteCustomActionHotkey(string[] messageParts)
|
||||
{
|
||||
if (messageParts.Length != 2)
|
||||
{
|
||||
@@ -179,16 +195,15 @@ namespace AdvancedPaste
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowWindow();
|
||||
viewModel.ReadClipboard();
|
||||
viewModel.ExecuteCustomActionWithPaste(customActionId);
|
||||
await ShowWindow();
|
||||
await viewModel.ExecuteCustomActionAsync(customActionId, PasteActionSource.GlobalKeyboardShortcut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWindow()
|
||||
private async Task ShowWindow()
|
||||
{
|
||||
viewModel.OnShow();
|
||||
await viewModel.OnShowAsync();
|
||||
|
||||
if (window is null)
|
||||
{
|
||||
|
||||
@@ -346,7 +346,7 @@
|
||||
x:Name="InputTxtBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
x:FieldModifier="public"
|
||||
IsEnabled="{x:Bind ViewModel.IsClipboardDataText, Mode=OneWay}"
|
||||
IsEnabled="{x:Bind ViewModel.ClipboardHasData, Mode=OneWay}"
|
||||
KeyDown="InputTxtBox_KeyDown"
|
||||
PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}"
|
||||
Style="{StaticResource CustomTextBoxStyle}"
|
||||
@@ -589,7 +589,7 @@
|
||||
Background="Transparent"
|
||||
Visibility="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind ViewModel.GeneralErrorText}" />
|
||||
<ToolTip Content="{x:Bind ViewModel.AIDisabledErrorText}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -638,7 +638,7 @@
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.ApiErrorText, Mode=OneWay}" />
|
||||
Text="{x:Bind ViewModel.PasteOperationErrorText, Mode=OneWay}" />
|
||||
<HyperlinkButton
|
||||
x:Uid="SettingsBtn"
|
||||
Grid.Column="1"
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
// 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.Net;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.ViewModels;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
@@ -20,12 +18,6 @@ namespace AdvancedPaste.Controls
|
||||
{
|
||||
public sealed partial class PromptBox : Microsoft.UI.Xaml.Controls.UserControl
|
||||
{
|
||||
// Minimum time to show spinner when generating custom format using forcePasteCustom
|
||||
private static readonly TimeSpan MinTaskTime = TimeSpan.FromSeconds(2);
|
||||
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
private readonly IUserSettings _userSettings;
|
||||
|
||||
public OptionsViewModel ViewModel { get; private set; }
|
||||
|
||||
public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register(
|
||||
@@ -54,12 +46,31 @@ namespace AdvancedPaste.Controls
|
||||
|
||||
public PromptBox()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
_userSettings = App.GetService<IUserSettings>();
|
||||
InitializeComponent();
|
||||
|
||||
ViewModel = App.GetService<OptionsViewModel>();
|
||||
ViewModel.CustomActionActivated += (_, e) => GenerateCustom(e.ForcePasteCustom);
|
||||
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
|
||||
ViewModel.CustomActionActivated += ViewModel_CustomActionActivated;
|
||||
}
|
||||
|
||||
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(ViewModel.Busy) || e.PropertyName == nameof(ViewModel.PasteOperationErrorText))
|
||||
{
|
||||
var state = ViewModel.Busy ? "LoadingState" : string.IsNullOrEmpty(ViewModel.PasteOperationErrorText) ? "DefaultState" : "ErrorState";
|
||||
VisualStateManager.GoToState(this, state, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_CustomActionActivated(object sender, CustomActionActivatedEventArgs e)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
if (!e.PasteResult)
|
||||
{
|
||||
PreviewGrid.Width = InputTxtBox.ActualWidth;
|
||||
PreviewFlyout.ShowAt(InputTxtBox);
|
||||
}
|
||||
}
|
||||
|
||||
private void Grid_Loaded(object sender, RoutedEventArgs e)
|
||||
@@ -68,48 +79,7 @@ namespace AdvancedPaste.Controls
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void GenerateCustom() => GenerateCustom(false);
|
||||
|
||||
private void GenerateCustom(bool forcePasteCustom)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
VisualStateManager.GoToState(this, "LoadingState", true);
|
||||
string inputInstructions = ViewModel.Query;
|
||||
ViewModel.SaveQuery(inputInstructions);
|
||||
|
||||
var customFormatTask = ViewModel.GenerateCustomFunction(inputInstructions);
|
||||
var delayTask = forcePasteCustom ? Task.Delay(MinTaskTime) : Task.CompletedTask;
|
||||
Task.WhenAll(customFormatTask, delayTask)
|
||||
.ContinueWith(
|
||||
_ =>
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
ViewModel.CustomFormatResult = customFormatTask.Result;
|
||||
|
||||
if (ViewModel.ApiRequestStatus == (int)HttpStatusCode.OK)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "DefaultState", true);
|
||||
if (_userSettings.ShowCustomPreview && !forcePasteCustom)
|
||||
{
|
||||
PreviewGrid.Width = InputTxtBox.ActualWidth;
|
||||
PreviewFlyout.ShowAt(InputTxtBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.PasteCustom();
|
||||
InputTxtBox.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "ErrorState", true);
|
||||
}
|
||||
});
|
||||
},
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
private async Task GenerateCustomAsync() => await ViewModel.GenerateCustomFunctionAsync(PasteActionSource.PromptBox);
|
||||
|
||||
[RelayCommand]
|
||||
private void Recall()
|
||||
@@ -127,29 +97,24 @@ namespace AdvancedPaste.Controls
|
||||
ClipboardHelper.SetClipboardTextContent(lastQuery.ClipboardData);
|
||||
}
|
||||
|
||||
private void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
|
||||
private async void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == Windows.System.VirtualKey.Enter && InputTxtBox.Text.Length > 0 && ViewModel.IsCustomAIEnabled)
|
||||
{
|
||||
GenerateCustom();
|
||||
await GenerateCustomAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void PreviewPasteBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PasteCustom();
|
||||
InputTxtBox.Text = string.Empty;
|
||||
}
|
||||
|
||||
private void ThumbUpDown_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button btn)
|
||||
if (sender is Button btn && bool.TryParse(btn.CommandParameter as string, out bool result))
|
||||
{
|
||||
bool result;
|
||||
if (bool.TryParse(btn.CommandParameter as string, out result))
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteCustomFormatOutputThumbUpDownEvent(result));
|
||||
}
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteCustomFormatOutputThumbUpDownEvent(result));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace AdvancedPaste.Converters;
|
||||
|
||||
public sealed partial class PasteFormatsToHeightConverter : IValueConverter
|
||||
{
|
||||
private const int ItemHeight = 40;
|
||||
|
||||
public int MaxItems { get; set; } = 5;
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language) =>
|
||||
new GridLength(GetHeight((value is ICollection collection) ? collection.Count : (value is int intValue) ? intValue : 0));
|
||||
|
||||
public int GetHeight(int itemCount) => Math.Min(MaxItems, itemCount) * ItemHeight;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
|
||||
}
|
||||
@@ -8,9 +8,9 @@
|
||||
xmlns:pages="using:AdvancedPaste.Pages"
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
Width="420"
|
||||
Height="308"
|
||||
Height="188"
|
||||
MinWidth="420"
|
||||
MinHeight="308"
|
||||
MinHeight="188"
|
||||
Closed="WindowEx_Closed"
|
||||
IsAlwaysOnTop="True"
|
||||
IsMaximizable="False"
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
|
||||
using System;
|
||||
|
||||
using System.Linq;
|
||||
using AdvancedPaste.Converters;
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Settings;
|
||||
using AdvancedPaste.ViewModels;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -24,25 +28,32 @@ namespace AdvancedPaste
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
InitializeComponent();
|
||||
|
||||
_userSettings = App.GetService<IUserSettings>();
|
||||
|
||||
var optionsViewModel = App.GetService<OptionsViewModel>();
|
||||
|
||||
var baseHeight = MinHeight;
|
||||
var coreActionCount = PasteFormat.MetadataDict.Values.Count(metadata => metadata.IsCoreAction);
|
||||
|
||||
void UpdateHeight()
|
||||
{
|
||||
var trimmedCustomActionCount = optionsViewModel.IsPasteWithAIEnabled ? Math.Min(_userSettings.CustomActions.Count, 5) : 0;
|
||||
Height = MinHeight = baseHeight + (trimmedCustomActionCount * 40);
|
||||
double GetHeight(int maxCustomActionCount) =>
|
||||
baseHeight +
|
||||
new PasteFormatsToHeightConverter().GetHeight(coreActionCount + _userSettings.AdditionalActions.Count) +
|
||||
new PasteFormatsToHeightConverter() { MaxItems = maxCustomActionCount }.GetHeight(optionsViewModel.IsAIServiceEnabled ? _userSettings.CustomActions.Count : 0);
|
||||
|
||||
MinHeight = GetHeight(1);
|
||||
Height = GetHeight(5);
|
||||
}
|
||||
|
||||
UpdateHeight();
|
||||
|
||||
_userSettings.CustomActions.CollectionChanged += (_, _) => UpdateHeight();
|
||||
_userSettings.Changed += (_, _) => UpdateHeight();
|
||||
optionsViewModel.PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(optionsViewModel.IsPasteWithAIEnabled))
|
||||
if (e.PropertyName == nameof(optionsViewModel.IsAIServiceEnabled))
|
||||
{
|
||||
UpdateHeight();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
<Page.Resources>
|
||||
<tkconverters:BoolToVisibilityConverter x:Name="BoolToVisibilityConverter" />
|
||||
<converters:CountToVisibilityConverter x:Name="countToVisibilityConverter" />
|
||||
<converters:PasteFormatsToHeightConverter x:Name="standardPasteFormatsToHeightConverter" />
|
||||
<converters:CountToDoubleConverter
|
||||
x:Name="customActionsCountToMinHeightConverter"
|
||||
x:Name="customActionsToMinHeightConverter"
|
||||
ValueIfNonZero="40"
|
||||
ValueIfZero="0" />
|
||||
<Style
|
||||
@@ -28,37 +29,56 @@
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
<Style x:Key="PasteFormatListViewItemStyle" TargetType="ListViewItem">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
<DataTemplate x:Key="PasteFormatTemplate" x:DataType="local:PasteFormat">
|
||||
<Grid>
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="{x:Bind ToolTip}" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="26" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<FontIcon
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind IconGlyph}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
x:Phase="1"
|
||||
Text="{x:Bind Name}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="0,0,8,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ShortcutText, Mode=OneWay}"
|
||||
Visibility="{x:Bind ShortcutText.Length, Mode=OneWay, Converter={StaticResource countToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
<Button
|
||||
Margin="0"
|
||||
Padding="5,0,5,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
AllowFocusOnInteraction="False"
|
||||
BorderThickness="0"
|
||||
Click="ListView_Button_Click"
|
||||
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}">
|
||||
<Grid Opacity="{x:Bind Opacity, Mode=OneWay}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="26" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock Text="{x:Bind ToolTip, Mode=OneWay}" />
|
||||
</ToolTipService.ToolTip>
|
||||
<FontIcon
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="16"
|
||||
Glyph="{x:Bind IconGlyph, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
x:Phase="1"
|
||||
Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="0,0,8,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ShortcutText, Mode=OneWay}"
|
||||
Visibility="{x:Bind ShortcutText.Length, Mode=OneWay, Converter={StaticResource countToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</Page.Resources>
|
||||
<Page.KeyboardAccelerators>
|
||||
@@ -166,9 +186,9 @@
|
||||
BorderThickness="0,1,0,0"
|
||||
RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{x:Bind ViewModel.StandardPasteFormats.Count, Mode=OneWay, Converter={StaticResource standardPasteFormatsToHeightConverter}}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" MinHeight="{x:Bind ViewModel.CustomActionPasteFormats.Count, Mode=OneWay, Converter={StaticResource customActionsCountToMinHeightConverter}}" />
|
||||
<RowDefinition Height="*" MinHeight="{x:Bind ViewModel.CustomActionPasteFormats.Count, Mode=OneWay, Converter={StaticResource customActionsToMinHeightConverter}}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
@@ -176,12 +196,13 @@
|
||||
x:Name="PasteOptionsListView"
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Bottom"
|
||||
IsEnabled="{x:Bind ViewModel.IsClipboardDataText, Mode=OneWay}"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_Click"
|
||||
IsItemClickEnabled="False"
|
||||
ItemContainerStyle="{StaticResource PasteFormatListViewItemStyle}"
|
||||
ItemContainerTransitions="{x:Null}"
|
||||
ItemTemplate="{StaticResource PasteFormatTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.StandardPasteFormats, Mode=OneWay}"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
||||
ScrollViewer.VerticalScrollMode="Auto"
|
||||
SelectionMode="None"
|
||||
TabIndex="1" />
|
||||
|
||||
@@ -196,9 +217,8 @@
|
||||
x:Name="CustomActionsListView"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Top"
|
||||
IsEnabled="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay}"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_Click"
|
||||
IsItemClickEnabled="False"
|
||||
ItemContainerStyle="{StaticResource PasteFormatListViewItemStyle}"
|
||||
ItemContainerTransitions="{x:Null}"
|
||||
ItemTemplate="{StaticResource PasteFormatTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.CustomActionPasteFormats, Mode=OneWay}"
|
||||
|
||||
@@ -130,15 +130,15 @@ namespace AdvancedPaste.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private void ListView_Click(object sender, ItemClickEventArgs e)
|
||||
private async void ListView_Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is PasteFormat format)
|
||||
if (sender is Button { DataContext: PasteFormat format })
|
||||
{
|
||||
ViewModel.ExecutePasteFormat(format);
|
||||
await ViewModel.ExecutePasteFormatAsync(format, PasteActionSource.ContextMenu);
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyboardAccelerator_Invoked(Microsoft.UI.Xaml.Input.KeyboardAccelerator sender, Microsoft.UI.Xaml.Input.KeyboardAcceleratorInvokedEventArgs args)
|
||||
private async void KeyboardAccelerator_Invoked(Microsoft.UI.Xaml.Input.KeyboardAccelerator sender, Microsoft.UI.Xaml.Input.KeyboardAcceleratorInvokedEventArgs args)
|
||||
{
|
||||
if (GetMainWindow()?.Visible is false)
|
||||
{
|
||||
@@ -171,7 +171,7 @@ namespace AdvancedPaste.Pages
|
||||
case VirtualKey.Number7:
|
||||
case VirtualKey.Number8:
|
||||
case VirtualKey.Number9:
|
||||
ViewModel.ExecutePasteFormat(sender.Key);
|
||||
await ViewModel.ExecutePasteFormatAsync(sender.Key);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -199,8 +199,7 @@ namespace AdvancedPaste.Pages
|
||||
}
|
||||
else if (item.Image is not null)
|
||||
{
|
||||
RandomAccessStreamReference image = null;
|
||||
image = await item.Item.Content.GetBitmapAsync();
|
||||
RandomAccessStreamReference image = await item.Item.Content.GetBitmapAsync();
|
||||
ClipboardHelper.SetClipboardImageContent(image);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Models;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Data.Html;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.System;
|
||||
|
||||
@@ -16,6 +20,34 @@ namespace AdvancedPaste.Helpers
|
||||
{
|
||||
internal static class ClipboardHelper
|
||||
{
|
||||
private static readonly HashSet<string> ImageFileTypes = new(StringComparer.InvariantCultureIgnoreCase) { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico", ".svg" };
|
||||
|
||||
private static readonly (string DataFormat, ClipboardFormat ClipboardFormat)[] DataFormats =
|
||||
[
|
||||
(StandardDataFormats.Text, ClipboardFormat.Text),
|
||||
(StandardDataFormats.Html, ClipboardFormat.Html),
|
||||
(StandardDataFormats.Bitmap, ClipboardFormat.Image),
|
||||
];
|
||||
|
||||
internal static async Task<ClipboardFormat> GetAvailableClipboardFormatsAsync(DataPackageView clipboardData)
|
||||
{
|
||||
var availableClipboardFormats = DataFormats.Aggregate(
|
||||
ClipboardFormat.None,
|
||||
(result, formatPair) => clipboardData.Contains(formatPair.DataFormat) ? (result | formatPair.ClipboardFormat) : result);
|
||||
|
||||
if (clipboardData.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
var storageItems = await clipboardData.GetStorageItemsAsync();
|
||||
|
||||
if (storageItems.Count == 1 && storageItems.Single() is StorageFile file && ImageFileTypes.Contains(file.FileType))
|
||||
{
|
||||
availableClipboardFormats |= ClipboardFormat.ImageFile;
|
||||
}
|
||||
}
|
||||
|
||||
return availableClipboardFormats;
|
||||
}
|
||||
|
||||
internal static void SetClipboardTextContent(string text)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
@@ -26,31 +58,45 @@ namespace AdvancedPaste.Helpers
|
||||
output.SetText(text);
|
||||
Clipboard.SetContentWithOptions(output, null);
|
||||
|
||||
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
|
||||
// Calling inside a loop makes it work.
|
||||
bool flushed = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Flush()
|
||||
{
|
||||
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
|
||||
// Calling inside a loop makes it work.
|
||||
const int maxAttempts = 5;
|
||||
for (int i = 1; i <= maxAttempts; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (flushed)
|
||||
Task.Run(Clipboard.Flush).Wait();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (i == maxAttempts)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Clipboard.Flush();
|
||||
}).Wait();
|
||||
|
||||
flushed = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Clipboard.Flush() failed", ex);
|
||||
Logger.LogError($"{nameof(Clipboard)}.{nameof(Flush)}() failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<bool> FlushAsync() => await Task.Run(Flush);
|
||||
|
||||
internal static async Task SetClipboardFileContentAsync(string fileName)
|
||||
{
|
||||
var storageFile = await StorageFile.GetFileFromPathAsync(fileName);
|
||||
|
||||
DataPackage output = new();
|
||||
output.SetStorageItems([storageFile]);
|
||||
Clipboard.SetContent(output);
|
||||
|
||||
await FlushAsync();
|
||||
}
|
||||
|
||||
internal static void SetClipboardImageContent(RandomAccessStreamReference image)
|
||||
@@ -63,30 +109,7 @@ namespace AdvancedPaste.Helpers
|
||||
output.SetBitmap(image);
|
||||
Clipboard.SetContentWithOptions(output, null);
|
||||
|
||||
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
|
||||
// Calling inside a loop makes it work.
|
||||
bool flushed = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (flushed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
Clipboard.Flush();
|
||||
}).Wait();
|
||||
|
||||
flushed = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Clipboard.Flush() failed", ex);
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,5 +159,58 @@ namespace AdvancedPaste.Helpers
|
||||
|
||||
Logger.LogInfo("Paste sent");
|
||||
}
|
||||
|
||||
internal static async Task<string> GetClipboardTextOrHtmlTextAsync(DataPackageView clipboardData)
|
||||
{
|
||||
if (clipboardData.Contains(StandardDataFormats.Text))
|
||||
{
|
||||
return await clipboardData.GetTextAsync();
|
||||
}
|
||||
else if (clipboardData.Contains(StandardDataFormats.Html))
|
||||
{
|
||||
var html = await clipboardData.GetHtmlFormatAsync();
|
||||
return HtmlUtilities.ConvertToText(html);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task<string> GetClipboardHtmlContentAsync(DataPackageView clipboardData) =>
|
||||
clipboardData.Contains(StandardDataFormats.Html) ? await clipboardData.GetHtmlFormatAsync() : string.Empty;
|
||||
|
||||
internal static async Task<SoftwareBitmap> GetClipboardImageContentAsync(DataPackageView clipboardData)
|
||||
{
|
||||
using var stream = await GetClipboardImageStreamAsync(clipboardData);
|
||||
if (stream != null)
|
||||
{
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
return await decoder.GetSoftwareBitmapAsync();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream> GetClipboardImageStreamAsync(DataPackageView clipboardData)
|
||||
{
|
||||
if (clipboardData.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
var storageItems = await clipboardData.GetStorageItemsAsync();
|
||||
var file = storageItems.Count == 1 ? storageItems[0] as StorageFile : null;
|
||||
if (file != null)
|
||||
{
|
||||
return await file.OpenReadAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (clipboardData.Contains(StandardDataFormats.Bitmap))
|
||||
{
|
||||
var bitmap = await clipboardData.GetBitmapAsync();
|
||||
return await bitmap.OpenReadAsync();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using AdvancedPaste.Models;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace AdvancedPaste.Settings
|
||||
@@ -16,6 +18,10 @@ namespace AdvancedPaste.Settings
|
||||
|
||||
public bool CloseAfterLosingFocus { get; }
|
||||
|
||||
public ObservableCollection<AdvancedPasteCustomAction> CustomActions { get; }
|
||||
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions { get; }
|
||||
|
||||
public IReadOnlyList<PasteFormats> AdditionalActions { get; }
|
||||
|
||||
public event EventHandler Changed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Globalization;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Media.Ocr;
|
||||
using Windows.System.UserProfile;
|
||||
|
||||
namespace AdvancedPaste.Helpers;
|
||||
|
||||
public static class OcrHelpers
|
||||
{
|
||||
public static async Task<string> ExtractTextAsync(SoftwareBitmap bitmap)
|
||||
{
|
||||
var ocrLanguage = GetOCRLanguage() ?? throw new InvalidOperationException("Unable to determine OCR language");
|
||||
|
||||
var ocrEngine = OcrEngine.TryCreateFromLanguage(ocrLanguage) ?? throw new InvalidOperationException("Unable to create OCR engine");
|
||||
var ocrResult = await ocrEngine.RecognizeAsync(bitmap);
|
||||
|
||||
return ocrResult.Text;
|
||||
}
|
||||
|
||||
private static Language GetOCRLanguage()
|
||||
{
|
||||
var userLanguageTags = GlobalizationPreferences.Languages.ToList();
|
||||
|
||||
var languages = from language in OcrEngine.AvailableRecognizerLanguages
|
||||
let tag = language.LanguageTag
|
||||
where userLanguageTags.Contains(tag)
|
||||
orderby userLanguageTags.IndexOf(tag)
|
||||
select language;
|
||||
|
||||
return languages.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Models;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
@@ -20,6 +22,8 @@ namespace AdvancedPaste.Settings
|
||||
private readonly TaskScheduler _taskScheduler;
|
||||
private readonly IFileSystemWatcher _watcher;
|
||||
private readonly object _loadingSettingsLock = new();
|
||||
private readonly List<PasteFormats> _additionalActions;
|
||||
private readonly List<AdvancedPasteCustomAction> _customActions;
|
||||
|
||||
private const string AdvancedPasteModuleName = "AdvancedPaste";
|
||||
private const int MaxNumberOfRetry = 5;
|
||||
@@ -27,13 +31,17 @@ namespace AdvancedPaste.Settings
|
||||
private bool _disposedValue;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public event EventHandler Changed;
|
||||
|
||||
public bool ShowCustomPreview { get; private set; }
|
||||
|
||||
public bool SendPasteKeyCombination { get; private set; }
|
||||
|
||||
public bool CloseAfterLosingFocus { get; private set; }
|
||||
|
||||
public ObservableCollection<AdvancedPasteCustomAction> CustomActions { get; private set; }
|
||||
public IReadOnlyList<PasteFormats> AdditionalActions => _additionalActions;
|
||||
|
||||
public IReadOnlyList<AdvancedPasteCustomAction> CustomActions => _customActions;
|
||||
|
||||
public UserSettings()
|
||||
{
|
||||
@@ -42,8 +50,8 @@ namespace AdvancedPaste.Settings
|
||||
ShowCustomPreview = true;
|
||||
SendPasteKeyCombination = true;
|
||||
CloseAfterLosingFocus = false;
|
||||
CustomActions = [];
|
||||
|
||||
_additionalActions = [];
|
||||
_customActions = [];
|
||||
_taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
LoadSettingsFromJson();
|
||||
@@ -88,18 +96,29 @@ namespace AdvancedPaste.Settings
|
||||
{
|
||||
void UpdateSettings()
|
||||
{
|
||||
ShowCustomPreview = settings.Properties.ShowCustomPreview;
|
||||
SendPasteKeyCombination = settings.Properties.SendPasteKeyCombination;
|
||||
CloseAfterLosingFocus = settings.Properties.CloseAfterLosingFocus;
|
||||
var properties = settings.Properties;
|
||||
|
||||
CustomActions.Clear();
|
||||
foreach (var customAction in settings.Properties.CustomActions.Value)
|
||||
{
|
||||
if (customAction.IsShown && customAction.IsValid)
|
||||
{
|
||||
CustomActions.Add(customAction);
|
||||
}
|
||||
}
|
||||
ShowCustomPreview = properties.ShowCustomPreview;
|
||||
SendPasteKeyCombination = properties.SendPasteKeyCombination;
|
||||
CloseAfterLosingFocus = properties.CloseAfterLosingFocus;
|
||||
|
||||
var sourceAdditionalActions = properties.AdditionalActions;
|
||||
(PasteFormats Format, IAdvancedPasteAction[] Actions)[] additionalActionFormats =
|
||||
[
|
||||
(PasteFormats.ImageToText, [sourceAdditionalActions.ImageToText]),
|
||||
(PasteFormats.PasteAsTxtFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsTxtFile]),
|
||||
(PasteFormats.PasteAsPngFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsPngFile]),
|
||||
(PasteFormats.PasteAsHtmlFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsHtmlFile])
|
||||
];
|
||||
|
||||
_additionalActions.Clear();
|
||||
_additionalActions.AddRange(additionalActionFormats.Where(tuple => tuple.Actions.All(action => action.IsShown))
|
||||
.Select(tuple => tuple.Format));
|
||||
|
||||
_customActions.Clear();
|
||||
_customActions.AddRange(properties.CustomActions.Value.Where(customAction => customAction.IsShown && customAction.IsValid));
|
||||
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
Task.Factory
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
[Flags]
|
||||
public enum ClipboardFormat
|
||||
{
|
||||
None,
|
||||
Text = 1 << 0,
|
||||
Html = 1 << 1,
|
||||
Audio = 1 << 2,
|
||||
Image = 1 << 3,
|
||||
ImageFile = 1 << 4,
|
||||
}
|
||||
@@ -5,14 +5,13 @@
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace AdvancedPaste.Models
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
public class ClipboardItem
|
||||
{
|
||||
public class ClipboardItem
|
||||
{
|
||||
public string Content { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
public ClipboardHistoryItem Item { get; set; }
|
||||
public ClipboardHistoryItem Item { get; set; }
|
||||
|
||||
public BitmapImage Image { get; set; }
|
||||
}
|
||||
public BitmapImage Image { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ using System;
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
public sealed class CustomActionActivatedEventArgs(string text, bool forcePasteCustom) : EventArgs
|
||||
public sealed class CustomActionActivatedEventArgs(string text, bool pasteResult) : EventArgs
|
||||
{
|
||||
public string Text { get; private set; } = text;
|
||||
public string Text { get; private init; } = text;
|
||||
|
||||
public bool ForcePasteCustom { get; private set; } = forcePasteCustom;
|
||||
public bool PasteResult { get; private init; } = pasteResult;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
public sealed class PasteActionException(string message) : Exception(message)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
public enum PasteActionSource
|
||||
{
|
||||
ContextMenu,
|
||||
InAppKeyboardShortcut,
|
||||
GlobalKeyboardShortcut,
|
||||
PromptBox,
|
||||
}
|
||||
@@ -2,38 +2,62 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
public partial class PasteFormat : ObservableObject
|
||||
[DebuggerDisplay("{Name} IsEnabled={IsEnabled} ShortcutText={ShortcutText}")]
|
||||
public sealed class PasteFormat
|
||||
{
|
||||
[ObservableProperty]
|
||||
private string _shortcutText = string.Empty;
|
||||
public static readonly IReadOnlyDictionary<PasteFormats, PasteFormatMetadataAttribute> MetadataDict =
|
||||
typeof(PasteFormats).GetFields()
|
||||
.Where(field => field.IsLiteral)
|
||||
.ToDictionary(field => (PasteFormats)field.GetRawConstantValue(), field => field.GetCustomAttribute<PasteFormatMetadataAttribute>());
|
||||
|
||||
[ObservableProperty]
|
||||
private string _toolTip = string.Empty;
|
||||
|
||||
public PasteFormat()
|
||||
private PasteFormat(PasteFormats format, ClipboardFormat clipboardFormats, bool isAIServiceEnabled)
|
||||
{
|
||||
Format = format;
|
||||
IsEnabled = SupportsClipboardFormats(clipboardFormats) && (isAIServiceEnabled || !Metadata.RequiresAIService);
|
||||
}
|
||||
|
||||
public PasteFormat(AdvancedPasteCustomAction customAction, string shortcutText)
|
||||
public PasteFormat(PasteFormats format, ClipboardFormat clipboardFormats, bool isAIServiceEnabled, Func<string, string> resourceLoader)
|
||||
: this(format, clipboardFormats, isAIServiceEnabled)
|
||||
{
|
||||
Name = Metadata.ResourceId == null ? string.Empty : resourceLoader(Metadata.ResourceId);
|
||||
Prompt = string.Empty;
|
||||
}
|
||||
|
||||
public PasteFormat(AdvancedPasteCustomAction customAction, ClipboardFormat clipboardFormats, bool isAIServiceEnabled)
|
||||
: this(PasteFormats.Custom, clipboardFormats, isAIServiceEnabled)
|
||||
{
|
||||
IconGlyph = "\uE945";
|
||||
Name = customAction.Name;
|
||||
Prompt = customAction.Prompt;
|
||||
Format = PasteFormats.Custom;
|
||||
ShortcutText = shortcutText;
|
||||
ToolTip = customAction.Prompt;
|
||||
}
|
||||
|
||||
public string IconGlyph { get; init; }
|
||||
public PasteFormatMetadataAttribute Metadata => MetadataDict[Format];
|
||||
|
||||
public string Name { get; init; }
|
||||
public string IconGlyph => Metadata.IconGlyph;
|
||||
|
||||
public PasteFormats Format { get; init; }
|
||||
public string Name { get; private init; }
|
||||
|
||||
public string Prompt { get; init; } = string.Empty;
|
||||
public PasteFormats Format { get; private init; }
|
||||
|
||||
public string Prompt { get; private init; }
|
||||
|
||||
public bool IsEnabled { get; private init; }
|
||||
|
||||
public double Opacity => IsEnabled ? 1 : 0.5;
|
||||
|
||||
public string ToolTip => string.IsNullOrEmpty(Prompt) ? $"{Name} ({ShortcutText})" : Prompt;
|
||||
|
||||
public string Query => string.IsNullOrEmpty(Prompt) ? Name : Prompt;
|
||||
|
||||
public string ShortcutText { get; set; } = string.Empty;
|
||||
|
||||
public bool SupportsClipboardFormats(ClipboardFormat clipboardFormats) => (clipboardFormats & Metadata.SupportedClipboardFormats) != ClipboardFormat.None;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class PasteFormatMetadataAttribute : Attribute
|
||||
{
|
||||
public bool IsCoreAction { get; init; }
|
||||
|
||||
public string ResourceId { get; init; }
|
||||
|
||||
public string IconGlyph { get; init; }
|
||||
|
||||
public bool RequiresAIService { get; init; }
|
||||
|
||||
public ClipboardFormat SupportedClipboardFormats { get; init; }
|
||||
|
||||
public string IPCKey { get; init; }
|
||||
}
|
||||
@@ -2,13 +2,33 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace AdvancedPaste.Models
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace AdvancedPaste.Models;
|
||||
|
||||
public enum PasteFormats
|
||||
{
|
||||
public enum PasteFormats
|
||||
{
|
||||
PlainText,
|
||||
Markdown,
|
||||
Json,
|
||||
Custom,
|
||||
}
|
||||
[PasteFormatMetadata(IsCoreAction = true, ResourceId = "PasteAsPlainText", IconGlyph = "\uE8E9", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text)]
|
||||
PlainText,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = true, ResourceId = "PasteAsMarkdown", IconGlyph = "\ue8a5", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text)]
|
||||
Markdown,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = true, ResourceId = "PasteAsJson", IconGlyph = "\uE943", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text)]
|
||||
Json,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = false, ResourceId = "ImageToText", IconGlyph = "\uE91B", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Image | ClipboardFormat.ImageFile, IPCKey = AdvancedPasteAdditionalActions.PropertyNames.ImageToText)]
|
||||
ImageToText,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = false, ResourceId = "PasteAsTxtFile", IconGlyph = "\uE8D2", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text | ClipboardFormat.Html, IPCKey = AdvancedPastePasteAsFileAction.PropertyNames.PasteAsTxtFile)]
|
||||
PasteAsTxtFile,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = false, ResourceId = "PasteAsPngFile", IconGlyph = "\uE8B9", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Image | ClipboardFormat.ImageFile, IPCKey = AdvancedPastePasteAsFileAction.PropertyNames.PasteAsPngFile)]
|
||||
PasteAsPngFile,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = false, ResourceId = "PasteAsHtmlFile", IconGlyph = "\uF6FA", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Html, IPCKey = AdvancedPastePasteAsFileAction.PropertyNames.PasteAsHtmlFile)]
|
||||
PasteAsHtmlFile,
|
||||
|
||||
[PasteFormatMetadata(IsCoreAction = false, IconGlyph = "\uE945", RequiresAIService = true, SupportedClipboardFormats = ClipboardFormat.Text)]
|
||||
Custom,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Models;
|
||||
|
||||
namespace AdvancedPaste.Services;
|
||||
|
||||
public interface IPasteFormatExecutor
|
||||
{
|
||||
Task<string> ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source);
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
// 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.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace AdvancedPaste.Services;
|
||||
|
||||
public sealed class PasteFormatExecutor(AICompletionsHelper aiHelper) : IPasteFormatExecutor
|
||||
{
|
||||
private readonly AICompletionsHelper _aiHelper = aiHelper;
|
||||
|
||||
public async Task<string> ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source)
|
||||
{
|
||||
if (!pasteFormat.IsEnabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
WriteTelemetry(pasteFormat.Format, source);
|
||||
|
||||
return await ExecutePasteFormatCoreAsync(pasteFormat, Clipboard.GetContent());
|
||||
}
|
||||
|
||||
private async Task<string> ExecutePasteFormatCoreAsync(PasteFormat pasteFormat, DataPackageView clipboardData)
|
||||
{
|
||||
switch (pasteFormat.Format)
|
||||
{
|
||||
case PasteFormats.PlainText:
|
||||
ToPlainText(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.Markdown:
|
||||
ToMarkdown(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.Json:
|
||||
ToJson(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.ImageToText:
|
||||
await ImageToTextAsync(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.PasteAsTxtFile:
|
||||
await ToTxtFileAsync(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.PasteAsPngFile:
|
||||
await ToPngFileAsync(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.PasteAsHtmlFile:
|
||||
await ToHtmlFileAsync(clipboardData);
|
||||
return null;
|
||||
|
||||
case PasteFormats.Custom:
|
||||
return await ToCustomAsync(pasteFormat.Prompt, clipboardData);
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unknown paste format {pasteFormat.Format}", nameof(pasteFormat));
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteTelemetry(PasteFormats format, PasteActionSource source)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case PasteActionSource.ContextMenu:
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteFormatClickedEvent(format));
|
||||
break;
|
||||
|
||||
case PasteActionSource.InAppKeyboardShortcut:
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(format));
|
||||
break;
|
||||
|
||||
case PasteActionSource.GlobalKeyboardShortcut:
|
||||
case PasteActionSource.PromptBox:
|
||||
break; // no telemetry yet for these sources
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(format));
|
||||
}
|
||||
}
|
||||
|
||||
private void ToPlainText(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
SetClipboardTextContent(MarkdownHelper.PasteAsPlainTextFromClipboard(clipboardData));
|
||||
}
|
||||
|
||||
private void ToMarkdown(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
SetClipboardTextContent(MarkdownHelper.ToMarkdown(clipboardData));
|
||||
}
|
||||
|
||||
private void ToJson(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
SetClipboardTextContent(JsonHelper.ToJsonFromXmlOrCsv(clipboardData));
|
||||
}
|
||||
|
||||
private async Task ImageToTextAsync(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
var bitmap = await ClipboardHelper.GetClipboardImageContentAsync(clipboardData);
|
||||
var text = await OcrHelpers.ExtractTextAsync(bitmap);
|
||||
SetClipboardTextContent(text);
|
||||
}
|
||||
|
||||
private async Task ToPngFileAsync(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
var clipboardBitmap = await ClipboardHelper.GetClipboardImageContentAsync(clipboardData);
|
||||
|
||||
using var pngStream = new InMemoryRandomAccessStream();
|
||||
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, pngStream);
|
||||
encoder.SetSoftwareBitmap(clipboardBitmap);
|
||||
await encoder.FlushAsync();
|
||||
|
||||
await SetClipboardFileContentAsync(pngStream.AsStreamForRead(), "png");
|
||||
}
|
||||
|
||||
private async Task ToTxtFileAsync(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
var text = await ClipboardHelper.GetClipboardTextOrHtmlTextAsync(clipboardData);
|
||||
await SetClipboardFileContentAsync(text, "txt");
|
||||
}
|
||||
|
||||
private async Task ToHtmlFileAsync(DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
var cfHtml = await ClipboardHelper.GetClipboardHtmlContentAsync(clipboardData);
|
||||
var html = RemoveHtmlMetadata(cfHtml);
|
||||
|
||||
await SetClipboardFileContentAsync(html, "html");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes leading CF_HTML metadata from HTML clipboard data.
|
||||
/// See: https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
|
||||
/// </summary>
|
||||
private static string RemoveHtmlMetadata(string cfHtml)
|
||||
{
|
||||
int? GetIntTagValue(string tagName)
|
||||
{
|
||||
var tagNameWithColon = tagName + ":";
|
||||
int tagStartPos = cfHtml.IndexOf(tagNameWithColon, StringComparison.InvariantCulture);
|
||||
|
||||
const int tagValueLength = 10;
|
||||
return tagStartPos != -1 && int.TryParse(cfHtml.AsSpan(tagStartPos + tagNameWithColon.Length, tagValueLength), CultureInfo.InvariantCulture, out int result) ? result : null;
|
||||
}
|
||||
|
||||
var startFragmentIndex = GetIntTagValue("StartFragment");
|
||||
var endFragmentIndex = GetIntTagValue("EndFragment");
|
||||
|
||||
return (startFragmentIndex == null || endFragmentIndex == null) ? cfHtml : cfHtml[startFragmentIndex.Value..endFragmentIndex.Value];
|
||||
}
|
||||
|
||||
private static async Task SetClipboardFileContentAsync(string data, string fileExtension)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
throw new ArgumentException($"Empty value in {nameof(SetClipboardFileContentAsync)}", nameof(data));
|
||||
}
|
||||
|
||||
var path = GetPasteAsFileTempFilePath(fileExtension);
|
||||
|
||||
await File.WriteAllTextAsync(path, data);
|
||||
await ClipboardHelper.SetClipboardFileContentAsync(path);
|
||||
}
|
||||
|
||||
private static async Task SetClipboardFileContentAsync(Stream stream, string fileExtension)
|
||||
{
|
||||
var path = GetPasteAsFileTempFilePath(fileExtension);
|
||||
|
||||
using var fileStream = File.Create(path);
|
||||
await stream.CopyToAsync(fileStream);
|
||||
|
||||
await ClipboardHelper.SetClipboardFileContentAsync(path);
|
||||
}
|
||||
|
||||
private static string GetPasteAsFileTempFilePath(string fileExtension)
|
||||
{
|
||||
var prefix = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsFile_FilePrefix");
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture);
|
||||
|
||||
return Path.Combine(Path.GetTempPath(), $"{prefix}{timestamp}.{fileExtension}");
|
||||
}
|
||||
|
||||
private async Task<string> ToCustomAsync(string prompt, DataPackageView clipboardData)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(prompt))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (!clipboardData.Contains(StandardDataFormats.Text))
|
||||
{
|
||||
Logger.LogWarning("Clipboard does not contain text data");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var currentClipboardText = await clipboardData.GetTextAsync();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(currentClipboardText))
|
||||
{
|
||||
Logger.LogWarning("Clipboard has no usable text data");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var aiResponse = await Task.Run(() => _aiHelper.AIFormatString(prompt, currentClipboardText));
|
||||
|
||||
return aiResponse.ApiRequestStatus == (int)HttpStatusCode.OK
|
||||
? aiResponse.Response
|
||||
: throw new PasteActionException(TranslateErrorText(aiResponse.ApiRequestStatus));
|
||||
}
|
||||
|
||||
private void SetClipboardTextContent(string content)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
ClipboardHelper.SetClipboardTextContent(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static string TranslateErrorText(int apiRequestStatus) => (HttpStatusCode)apiRequestStatus switch
|
||||
{
|
||||
HttpStatusCode.TooManyRequests => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyTooManyRequests"),
|
||||
HttpStatusCode.Unauthorized => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyUnauthorized"),
|
||||
HttpStatusCode.OK => string.Empty,
|
||||
_ => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyError") + apiRequestStatus.ToString(CultureInfo.InvariantCulture),
|
||||
};
|
||||
}
|
||||
@@ -120,9 +120,12 @@
|
||||
<data name="AIMistakeNote.Text" xml:space="preserve">
|
||||
<value>AI can make mistakes.</value>
|
||||
</data>
|
||||
<data name="ClipboardDataTypeMismatchWarning" xml:space="preserve">
|
||||
<value>Clipboard data is not text</value>
|
||||
<data name="ClipboardEmptyWarning" xml:space="preserve">
|
||||
<value>Clipboard does not contain any usable formats</value>
|
||||
</data>
|
||||
<data name="ClipboardDataNotTextWarning" xml:space="preserve">
|
||||
<value>Clipboard data is not text</value>
|
||||
</data>
|
||||
<data name="OpenAINotConfigured" xml:space="preserve">
|
||||
<value>To custom with AI is not enabled</value>
|
||||
</data>
|
||||
@@ -135,6 +138,9 @@
|
||||
<data name="OpenAIApiKeyError" xml:space="preserve">
|
||||
<value>OpenAI request failed with status code: </value>
|
||||
</data>
|
||||
<data name="PasteError" xml:space="preserve">
|
||||
<value>An error occurred during the paste operation</value>
|
||||
</data>
|
||||
<data name="ClipboardHistoryButton.Text" xml:space="preserve">
|
||||
<value>Clipboard history</value>
|
||||
</data>
|
||||
@@ -151,7 +157,7 @@
|
||||
<value>Privacy</value>
|
||||
</data>
|
||||
<data name="LoadingText.Text" xml:space="preserve">
|
||||
<value>Connecting to AI services and generating output..</value>
|
||||
<value>Generating output...</value>
|
||||
</data>
|
||||
<data name="PasteAsJson" xml:space="preserve">
|
||||
<value>Paste as JSON</value>
|
||||
@@ -162,6 +168,18 @@
|
||||
<data name="PasteAsPlainText" xml:space="preserve">
|
||||
<value>Paste as plain text</value>
|
||||
</data>
|
||||
<data name="ImageToText" xml:space="preserve">
|
||||
<value>Image to text</value>
|
||||
</data>
|
||||
<data name="PasteAsTxtFile" xml:space="preserve">
|
||||
<value>Paste as .txt file</value>
|
||||
</data>
|
||||
<data name="PasteAsPngFile" xml:space="preserve">
|
||||
<value>Paste as .png file</value>
|
||||
</data>
|
||||
<data name="PasteAsHtmlFile" xml:space="preserve">
|
||||
<value>Paste as .html file</value>
|
||||
</data>
|
||||
<data name="PasteButtonAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Paste</value>
|
||||
</data>
|
||||
@@ -228,4 +246,7 @@
|
||||
<data name="CtrlKey" xml:space="preserve">
|
||||
<value>Ctrl</value>
|
||||
</data>
|
||||
<data name="PasteAsFile_FilePrefix" xml:space="preserve">
|
||||
<value>PowerToys_Paste_</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -3,21 +3,20 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedPaste.Helpers;
|
||||
using AdvancedPaste.Models;
|
||||
using AdvancedPaste.Services;
|
||||
using AdvancedPaste.Settings;
|
||||
using Common.UI;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.Win32;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
@@ -28,67 +27,66 @@ using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
|
||||
|
||||
namespace AdvancedPaste.ViewModels
|
||||
{
|
||||
public partial class OptionsViewModel : ObservableObject, IDisposable
|
||||
public sealed partial class OptionsViewModel : ObservableObject, IDisposable
|
||||
{
|
||||
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
private readonly DispatcherTimer _clipboardTimer;
|
||||
private readonly IUserSettings _userSettings;
|
||||
private readonly AICompletionsHelper aiHelper;
|
||||
private readonly App app = App.Current as App;
|
||||
private readonly PasteFormat[] _allStandardPasteFormats;
|
||||
private readonly IPasteFormatExecutor _pasteFormatExecutor;
|
||||
private readonly AICompletionsHelper _aiHelper;
|
||||
|
||||
public DataPackageView ClipboardData { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))]
|
||||
[NotifyPropertyChangedFor(nameof(GeneralErrorText))]
|
||||
[NotifyPropertyChangedFor(nameof(IsCustomAIEnabled))]
|
||||
private bool _isClipboardDataText;
|
||||
[NotifyPropertyChangedFor(nameof(ClipboardHasData))]
|
||||
[NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))]
|
||||
[NotifyPropertyChangedFor(nameof(AIDisabledErrorText))]
|
||||
private ClipboardFormat _availableClipboardFormats;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _clipboardHistoryEnabled;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))]
|
||||
[NotifyPropertyChangedFor(nameof(GeneralErrorText))]
|
||||
[NotifyPropertyChangedFor(nameof(IsPasteWithAIEnabled))]
|
||||
[NotifyPropertyChangedFor(nameof(AIDisabledErrorText))]
|
||||
[NotifyPropertyChangedFor(nameof(IsAIServiceEnabled))]
|
||||
[NotifyPropertyChangedFor(nameof(IsCustomAIEnabled))]
|
||||
private bool _isAllowedByGPO;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(ApiErrorText))]
|
||||
private int _apiRequestStatus;
|
||||
private string _pasteOperationErrorText;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _query = string.Empty;
|
||||
|
||||
private bool _pasteFormatsDirty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _busy;
|
||||
|
||||
public ObservableCollection<PasteFormat> StandardPasteFormats { get; } = [];
|
||||
|
||||
public ObservableCollection<PasteFormat> CustomActionPasteFormats { get; } = [];
|
||||
|
||||
public bool IsPasteWithAIEnabled => IsAllowedByGPO && aiHelper.IsAIEnabled;
|
||||
public bool IsAIServiceEnabled => IsAllowedByGPO && _aiHelper.IsAIEnabled;
|
||||
|
||||
public bool IsCustomAIEnabled => IsPasteWithAIEnabled && IsClipboardDataText;
|
||||
public bool IsCustomAIEnabled => IsAIServiceEnabled && ClipboardHasText;
|
||||
|
||||
public bool ClipboardHasData => AvailableClipboardFormats != ClipboardFormat.None;
|
||||
|
||||
private bool ClipboardHasText => AvailableClipboardFormats.HasFlag(ClipboardFormat.Text);
|
||||
|
||||
private bool Visible => GetMainWindow()?.Visible is true;
|
||||
|
||||
public event EventHandler<CustomActionActivatedEventArgs> CustomActionActivated;
|
||||
|
||||
public OptionsViewModel(IUserSettings userSettings)
|
||||
public OptionsViewModel(AICompletionsHelper aiHelper, IUserSettings userSettings, IPasteFormatExecutor pasteFormatExecutor)
|
||||
{
|
||||
aiHelper = new AICompletionsHelper();
|
||||
_aiHelper = aiHelper;
|
||||
_userSettings = userSettings;
|
||||
_pasteFormatExecutor = pasteFormatExecutor;
|
||||
|
||||
ApiRequestStatus = (int)HttpStatusCode.OK;
|
||||
|
||||
_allStandardPasteFormats =
|
||||
[
|
||||
new PasteFormat { IconGlyph = "\uE8E9", Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsPlainText"), Format = PasteFormats.PlainText },
|
||||
new PasteFormat { IconGlyph = "\ue8a5", Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsMarkdown"), Format = PasteFormats.Markdown },
|
||||
new PasteFormat { IconGlyph = "\uE943", Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsJson"), Format = PasteFormats.Json },
|
||||
];
|
||||
|
||||
GeneratedResponses = new ObservableCollection<string>();
|
||||
GeneratedResponses = [];
|
||||
GeneratedResponses.CollectionChanged += (s, e) =>
|
||||
{
|
||||
OnPropertyChanged(nameof(HasMultipleResponses));
|
||||
@@ -96,28 +94,31 @@ namespace AdvancedPaste.ViewModels
|
||||
};
|
||||
|
||||
ClipboardHistoryEnabled = IsClipboardHistoryEnabled();
|
||||
ReadClipboard();
|
||||
UpdateOpenAIKey();
|
||||
_clipboardTimer = new() { Interval = TimeSpan.FromSeconds(1) };
|
||||
_clipboardTimer.Tick += ClipboardTimer_Tick;
|
||||
_clipboardTimer.Start();
|
||||
|
||||
RefreshPasteFormats();
|
||||
_userSettings.CustomActions.CollectionChanged += (_, _) => EnqueueRefreshPasteFormats();
|
||||
_userSettings.Changed += (_, _) => EnqueueRefreshPasteFormats();
|
||||
PropertyChanged += (_, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(Query) || e.PropertyName == nameof(IsPasteWithAIEnabled))
|
||||
string[] dirtyingProperties = [nameof(Query), nameof(IsAIServiceEnabled), nameof(IsCustomAIEnabled), nameof(AvailableClipboardFormats)];
|
||||
|
||||
if (dirtyingProperties.Contains(e.PropertyName))
|
||||
{
|
||||
EnqueueRefreshPasteFormats();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void ClipboardTimer_Tick(object sender, object e)
|
||||
private static MainWindow GetMainWindow() => (App.Current as App)?.GetMainWindow();
|
||||
|
||||
private async void ClipboardTimer_Tick(object sender, object e)
|
||||
{
|
||||
if (app.GetMainWindow()?.Visible is true)
|
||||
if (Visible)
|
||||
{
|
||||
ReadClipboard();
|
||||
await ReadClipboardAsync();
|
||||
UpdateAllowedByGPO();
|
||||
}
|
||||
}
|
||||
@@ -137,10 +138,12 @@ namespace AdvancedPaste.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private PasteFormat CreatePasteFormat(PasteFormats format) => new(format, AvailableClipboardFormats, IsAIServiceEnabled, ResourceLoaderInstance.ResourceLoader.GetString);
|
||||
|
||||
private PasteFormat CreatePasteFormat(AdvancedPasteCustomAction customAction) => new(customAction, AvailableClipboardFormats, IsAIServiceEnabled);
|
||||
|
||||
private void RefreshPasteFormats()
|
||||
{
|
||||
bool Filter(string text) => text.Contains(Query, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
var ctrlString = ResourceLoaderInstance.ResourceLoader.GetString("CtrlKey");
|
||||
int shortcutNum = 0;
|
||||
|
||||
@@ -150,28 +153,33 @@ namespace AdvancedPaste.ViewModels
|
||||
return shortcutNum <= 9 ? $"{ctrlString}+{shortcutNum}" : string.Empty;
|
||||
}
|
||||
|
||||
StandardPasteFormats.Clear();
|
||||
foreach (var format in _allStandardPasteFormats)
|
||||
IEnumerable<PasteFormat> FilterAndSort(IEnumerable<PasteFormat> pasteFormats) =>
|
||||
from pasteFormat in pasteFormats
|
||||
let comparison = StringComparison.CurrentCultureIgnoreCase
|
||||
where pasteFormat.Name.Contains(Query, comparison) || pasteFormat.Prompt.Contains(Query, comparison)
|
||||
orderby pasteFormat.IsEnabled descending
|
||||
select pasteFormat;
|
||||
|
||||
void UpdateFormats(ObservableCollection<PasteFormat> collection, IEnumerable<PasteFormat> pasteFormats)
|
||||
{
|
||||
if (Filter(format.Name))
|
||||
collection.Clear();
|
||||
|
||||
foreach (var format in FilterAndSort(pasteFormats))
|
||||
{
|
||||
format.ShortcutText = GetNextShortcutText();
|
||||
format.ToolTip = $"{format.Name} ({format.ShortcutText})";
|
||||
StandardPasteFormats.Add(format);
|
||||
if (format.IsEnabled)
|
||||
{
|
||||
format.ShortcutText = GetNextShortcutText();
|
||||
}
|
||||
|
||||
collection.Add(format);
|
||||
}
|
||||
}
|
||||
|
||||
CustomActionPasteFormats.Clear();
|
||||
if (IsPasteWithAIEnabled)
|
||||
{
|
||||
foreach (var customAction in _userSettings.CustomActions)
|
||||
{
|
||||
if (Filter(customAction.Name) || Filter(customAction.Prompt))
|
||||
{
|
||||
CustomActionPasteFormats.Add(new PasteFormat(customAction, GetNextShortcutText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateFormats(StandardPasteFormats, Enum.GetValues<PasteFormats>()
|
||||
.Where(format => PasteFormat.MetadataDict[format].IsCoreAction || _userSettings.AdditionalActions.Contains(format))
|
||||
.Select(CreatePasteFormat));
|
||||
|
||||
UpdateFormats(CustomActionPasteFormats, IsAIServiceEnabled ? _userSettings.CustomActions.Select(CreatePasteFormat) : []);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -180,26 +188,34 @@ namespace AdvancedPaste.ViewModels
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void ReadClipboard()
|
||||
public async Task ReadClipboardAsync()
|
||||
{
|
||||
if (Busy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClipboardData = Clipboard.GetContent();
|
||||
IsClipboardDataText = ClipboardData.Contains(StandardDataFormats.Text);
|
||||
AvailableClipboardFormats = await ClipboardHelper.GetAvailableClipboardFormatsAsync(ClipboardData);
|
||||
}
|
||||
|
||||
public void OnShow()
|
||||
public async Task OnShowAsync()
|
||||
{
|
||||
ReadClipboard();
|
||||
PasteOperationErrorText = string.Empty;
|
||||
Query = string.Empty;
|
||||
|
||||
await ReadClipboardAsync();
|
||||
|
||||
if (UpdateOpenAIKey())
|
||||
{
|
||||
app.GetMainWindow()?.StartLoading();
|
||||
GetMainWindow()?.StartLoading();
|
||||
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
app.GetMainWindow()?.FinishLoading(aiHelper.IsAIEnabled);
|
||||
GetMainWindow()?.FinishLoading(_aiHelper.IsAIEnabled);
|
||||
OnPropertyChanged(nameof(InputTxtBoxPlaceholderText));
|
||||
OnPropertyChanged(nameof(GeneralErrorText));
|
||||
OnPropertyChanged(nameof(IsPasteWithAIEnabled));
|
||||
OnPropertyChanged(nameof(AIDisabledErrorText));
|
||||
OnPropertyChanged(nameof(IsAIServiceEnabled));
|
||||
OnPropertyChanged(nameof(IsCustomAIEnabled));
|
||||
});
|
||||
}
|
||||
@@ -209,7 +225,7 @@ namespace AdvancedPaste.ViewModels
|
||||
}
|
||||
|
||||
// List to store generated responses
|
||||
public ObservableCollection<string> GeneratedResponses { get; set; } = new ObservableCollection<string>();
|
||||
public ObservableCollection<string> GeneratedResponses { get; set; } = [];
|
||||
|
||||
// Index to keep track of the current response
|
||||
private int _currentResponseIndex;
|
||||
@@ -228,30 +244,20 @@ namespace AdvancedPaste.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasMultipleResponses
|
||||
{
|
||||
get => GeneratedResponses.Count > 1;
|
||||
}
|
||||
public bool HasMultipleResponses => GeneratedResponses.Count > 1;
|
||||
|
||||
public string CurrentIndexDisplay => $"{CurrentResponseIndex + 1}/{GeneratedResponses.Count}";
|
||||
|
||||
public string InputTxtBoxPlaceholderText
|
||||
=> ResourceLoaderInstance.ResourceLoader.GetString(ClipboardHasData ? "CustomFormatTextBox/PlaceholderText" : "ClipboardEmptyWarning");
|
||||
|
||||
public string AIDisabledErrorText
|
||||
{
|
||||
get
|
||||
{
|
||||
app.GetMainWindow().ClearInputText();
|
||||
|
||||
return IsClipboardDataText ? ResourceLoaderInstance.ResourceLoader.GetString("CustomFormatTextBox/PlaceholderText") : GeneralErrorText;
|
||||
}
|
||||
}
|
||||
|
||||
public string GeneralErrorText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsClipboardDataText)
|
||||
if (!ClipboardHasText)
|
||||
{
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("ClipboardDataTypeMismatchWarning");
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("ClipboardDataNotTextWarning");
|
||||
}
|
||||
|
||||
if (!IsAllowedByGPO)
|
||||
@@ -259,7 +265,7 @@ namespace AdvancedPaste.ViewModels
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAIGpoDisabled");
|
||||
}
|
||||
|
||||
if (!aiHelper.IsAIEnabled)
|
||||
if (!_aiHelper.IsAIEnabled)
|
||||
{
|
||||
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAINotConfigured");
|
||||
}
|
||||
@@ -270,17 +276,6 @@ namespace AdvancedPaste.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public string ApiErrorText
|
||||
{
|
||||
get => (HttpStatusCode)ApiRequestStatus switch
|
||||
{
|
||||
HttpStatusCode.TooManyRequests => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyTooManyRequests"),
|
||||
HttpStatusCode.Unauthorized => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyUnauthorized"),
|
||||
HttpStatusCode.OK => string.Empty,
|
||||
_ => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyError") + ApiRequestStatus.ToString(CultureInfo.InvariantCulture),
|
||||
};
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private string _customFormatResult;
|
||||
|
||||
@@ -289,9 +284,17 @@ namespace AdvancedPaste.ViewModels
|
||||
{
|
||||
var text = GeneratedResponses.ElementAtOrDefault(CurrentResponseIndex);
|
||||
|
||||
if (text != null)
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
PasteCustomFunction(text);
|
||||
ClipboardHelper.SetClipboardTextContent(text);
|
||||
HideWindow();
|
||||
|
||||
if (_userSettings.SendPasteKeyCombination)
|
||||
{
|
||||
ClipboardHelper.SendPasteKeyCombination();
|
||||
}
|
||||
|
||||
Query = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,190 +323,120 @@ namespace AdvancedPaste.ViewModels
|
||||
public void OpenSettings()
|
||||
{
|
||||
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.AdvancedPaste, true);
|
||||
(App.Current as App).GetMainWindow().Close();
|
||||
GetMainWindow()?.Close();
|
||||
}
|
||||
|
||||
private void SetClipboardContentAndHideWindow(string content)
|
||||
internal async Task ExecutePasteFormatAsync(PasteFormats format, PasteActionSource source)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
ClipboardHelper.SetClipboardTextContent(content);
|
||||
}
|
||||
|
||||
if (app.GetMainWindow() != null)
|
||||
{
|
||||
Windows.Win32.Foundation.HWND hwnd = (Windows.Win32.Foundation.HWND)app.GetMainWindow().GetWindowHandle();
|
||||
Windows.Win32.PInvoke.ShowWindow(hwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
|
||||
}
|
||||
await ReadClipboardAsync();
|
||||
await ExecutePasteFormatAsync(CreatePasteFormat(format), source);
|
||||
}
|
||||
|
||||
internal void ToPlainTextFunction()
|
||||
internal async Task ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
string outputString = MarkdownHelper.PasteAsPlainTextFromClipboard(ClipboardData);
|
||||
|
||||
SetClipboardContentAndHideWindow(outputString);
|
||||
|
||||
if (_userSettings.SendPasteKeyCombination)
|
||||
{
|
||||
ClipboardHelper.SendPasteKeyCombination();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal void ToMarkdownFunction(bool pasteAlways = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
string outputString = MarkdownHelper.ToMarkdown(ClipboardData);
|
||||
|
||||
SetClipboardContentAndHideWindow(outputString);
|
||||
|
||||
if (pasteAlways || _userSettings.SendPasteKeyCombination)
|
||||
{
|
||||
ClipboardHelper.SendPasteKeyCombination();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal void ToJsonFunction(bool pasteAlways = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
string jsonText = JsonHelper.ToJsonFromXmlOrCsv(ClipboardData);
|
||||
|
||||
SetClipboardContentAndHideWindow(jsonText);
|
||||
|
||||
if (pasteAlways || _userSettings.SendPasteKeyCombination)
|
||||
{
|
||||
ClipboardHelper.SendPasteKeyCombination();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExecutePasteFormat(VirtualKey key)
|
||||
{
|
||||
var index = key - VirtualKey.Number1;
|
||||
var pasteFormat = StandardPasteFormats.ElementAtOrDefault(index) ?? CustomActionPasteFormats.ElementAtOrDefault(index - StandardPasteFormats.Count);
|
||||
|
||||
if (pasteFormat != null)
|
||||
{
|
||||
ExecutePasteFormat(pasteFormat);
|
||||
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(pasteFormat.Format));
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExecutePasteFormat(PasteFormat pasteFormat)
|
||||
{
|
||||
if (!IsClipboardDataText || (pasteFormat.Format == PasteFormats.Custom && !IsCustomAIEnabled))
|
||||
if (Busy)
|
||||
{
|
||||
Logger.LogWarning($"Execution of {pasteFormat.Format} from {source} suppressed as busy");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pasteFormat.Format)
|
||||
if (!pasteFormat.IsEnabled)
|
||||
{
|
||||
case PasteFormats.PlainText:
|
||||
ToPlainTextFunction();
|
||||
break;
|
||||
var resourceId = pasteFormat.SupportsClipboardFormats(AvailableClipboardFormats) ? "PasteError" : "ClipboardEmptyWarning";
|
||||
PasteOperationErrorText = ResourceLoaderInstance.ResourceLoader.GetString(resourceId);
|
||||
return;
|
||||
}
|
||||
|
||||
case PasteFormats.Markdown:
|
||||
ToMarkdownFunction();
|
||||
break;
|
||||
Busy = true;
|
||||
PasteOperationErrorText = string.Empty;
|
||||
Query = pasteFormat.Query;
|
||||
|
||||
case PasteFormats.Json:
|
||||
ToJsonFunction();
|
||||
break;
|
||||
if (pasteFormat.Format == PasteFormats.Custom)
|
||||
{
|
||||
SaveQuery(Query);
|
||||
}
|
||||
|
||||
case PasteFormats.Custom:
|
||||
Query = pasteFormat.Prompt;
|
||||
CustomActionActivated?.Invoke(this, new CustomActionActivatedEventArgs(pasteFormat.Prompt, false));
|
||||
break;
|
||||
try
|
||||
{
|
||||
// Minimum time to show busy spinner for AI actions when triggered by global keyboard shortcut.
|
||||
var aiActionMinTaskTime = TimeSpan.FromSeconds(2);
|
||||
var delayTask = (Visible && source == PasteActionSource.GlobalKeyboardShortcut) ? Task.Delay(aiActionMinTaskTime) : Task.CompletedTask;
|
||||
var aiOutput = await _pasteFormatExecutor.ExecutePasteFormatAsync(pasteFormat, source);
|
||||
|
||||
await delayTask;
|
||||
|
||||
if (pasteFormat.Format != PasteFormats.Custom)
|
||||
{
|
||||
HideWindow();
|
||||
|
||||
if (source == PasteActionSource.GlobalKeyboardShortcut || _userSettings.SendPasteKeyCombination)
|
||||
{
|
||||
ClipboardHelper.SendPasteKeyCombination();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var pasteResult = source == PasteActionSource.GlobalKeyboardShortcut || !_userSettings.ShowCustomPreview;
|
||||
|
||||
GeneratedResponses.Add(aiOutput);
|
||||
CurrentResponseIndex = GeneratedResponses.Count - 1;
|
||||
CustomActionActivated?.Invoke(this, new CustomActionActivatedEventArgs(pasteFormat.Prompt, pasteResult));
|
||||
|
||||
if (pasteResult)
|
||||
{
|
||||
PasteCustom();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error executing paste format", ex);
|
||||
PasteOperationErrorText = ex is PasteActionException ? ex.Message : ResourceLoaderInstance.ResourceLoader.GetString("PasteError");
|
||||
}
|
||||
|
||||
Busy = false;
|
||||
}
|
||||
|
||||
internal async Task ExecutePasteFormatAsync(VirtualKey key)
|
||||
{
|
||||
var pasteFormat = StandardPasteFormats.Concat(CustomActionPasteFormats)
|
||||
.Where(pasteFormat => pasteFormat.IsEnabled)
|
||||
.ElementAtOrDefault(key - VirtualKey.Number1);
|
||||
|
||||
if (pasteFormat != null)
|
||||
{
|
||||
await ExecutePasteFormatAsync(pasteFormat, PasteActionSource.InAppKeyboardShortcut);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExecuteCustomActionWithPaste(int customActionId)
|
||||
internal async Task ExecuteCustomActionAsync(int customActionId, PasteActionSource source)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
await ReadClipboardAsync();
|
||||
|
||||
var customAction = _userSettings.CustomActions.FirstOrDefault(customAction => customAction.Id == customActionId);
|
||||
|
||||
if (customAction != null)
|
||||
{
|
||||
Query = customAction.Prompt;
|
||||
CustomActionActivated?.Invoke(this, new CustomActionActivatedEventArgs(customAction.Prompt, true));
|
||||
await ExecutePasteFormatAsync(CreatePasteFormat(customAction), source);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<string> GenerateCustomFunction(string inputInstructions)
|
||||
internal async Task GenerateCustomFunctionAsync(PasteActionSource triggerSource)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(inputInstructions) || !IsCustomAIEnabled)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (!IsClipboardDataText)
|
||||
{
|
||||
Logger.LogWarning("Clipboard does not contain text data");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string currentClipboardText = await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
string text = await ClipboardData.GetTextAsync() as string;
|
||||
return text;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Couldn't get text from the clipboard. Resume with empty text.
|
||||
return string.Empty;
|
||||
}
|
||||
});
|
||||
|
||||
if (string.IsNullOrWhiteSpace(currentClipboardText))
|
||||
{
|
||||
Logger.LogWarning("Clipboard has no usable text data");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var aiResponse = await Task.Run(() => aiHelper.AIFormatString(inputInstructions, currentClipboardText));
|
||||
|
||||
string aiOutput = aiResponse.Response;
|
||||
ApiRequestStatus = aiResponse.ApiRequestStatus;
|
||||
|
||||
GeneratedResponses.Add(aiOutput);
|
||||
CurrentResponseIndex = GeneratedResponses.Count - 1;
|
||||
return aiOutput;
|
||||
AdvancedPasteCustomAction customAction = new() { Name = "Default", Prompt = Query };
|
||||
await ExecutePasteFormatAsync(CreatePasteFormat(customAction), triggerSource);
|
||||
}
|
||||
|
||||
internal void PasteCustomFunction(string text)
|
||||
private void HideWindow()
|
||||
{
|
||||
Logger.LogTrace();
|
||||
var mainWindow = GetMainWindow();
|
||||
|
||||
SetClipboardContentAndHideWindow(text);
|
||||
|
||||
if (_userSettings.SendPasteKeyCombination)
|
||||
if (mainWindow != null)
|
||||
{
|
||||
ClipboardHelper.SendPasteKeyCombination();
|
||||
Windows.Win32.Foundation.HWND hwnd = (Windows.Win32.Foundation.HWND)mainWindow.GetWindowHandle();
|
||||
Windows.Win32.PInvoke.ShowWindow(hwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,11 +457,7 @@ namespace AdvancedPaste.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
string currentClipboardText = Task.Run(async () =>
|
||||
{
|
||||
string text = await clipboardData.GetTextAsync() as string;
|
||||
return text;
|
||||
}).Result;
|
||||
var currentClipboardText = Task.Run(async () => await clipboardData.GetTextAsync()).Result;
|
||||
|
||||
var queryData = new CustomQuery
|
||||
{
|
||||
@@ -536,13 +465,13 @@ namespace AdvancedPaste.ViewModels
|
||||
ClipboardData = currentClipboardText,
|
||||
};
|
||||
|
||||
SettingsUtils utils = new SettingsUtils();
|
||||
SettingsUtils utils = new();
|
||||
utils.SaveSettings(queryData.ToString(), Constants.AdvancedPasteModuleName, Constants.LastQueryJsonFileName);
|
||||
}
|
||||
|
||||
internal CustomQuery LoadPreviousQuery()
|
||||
{
|
||||
SettingsUtils utils = new SettingsUtils();
|
||||
SettingsUtils utils = new();
|
||||
var query = utils.GetSettings<CustomQuery>(Constants.AdvancedPasteModuleName, Constants.LastQueryJsonFileName);
|
||||
return query;
|
||||
}
|
||||
@@ -572,9 +501,9 @@ namespace AdvancedPaste.ViewModels
|
||||
|
||||
if (IsAllowedByGPO)
|
||||
{
|
||||
var oldKey = aiHelper.GetKey();
|
||||
var oldKey = _aiHelper.GetKey();
|
||||
var newKey = AICompletionsHelper.LoadOpenAIKey();
|
||||
aiHelper.SetOpenAIKey(newKey);
|
||||
_aiHelper.SetOpenAIKey(newKey);
|
||||
return newKey != oldKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace
|
||||
{
|
||||
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
|
||||
const wchar_t JSON_KEY_CUSTOM_ACTIONS[] = L"custom-actions";
|
||||
const wchar_t JSON_KEY_ADDITIONAL_ACTIONS[] = L"additional-actions";
|
||||
const wchar_t JSON_KEY_SHORTCUT[] = L"shortcut";
|
||||
const wchar_t JSON_KEY_IS_SHOWN[] = L"isShown";
|
||||
const wchar_t JSON_KEY_ID[] = L"id";
|
||||
@@ -73,7 +74,6 @@ private:
|
||||
|
||||
HANDLE m_hProcess;
|
||||
|
||||
std::thread create_pipe_thread;
|
||||
std::unique_ptr<CAtlFile> m_write_pipe;
|
||||
|
||||
// Time to wait for process to close after sending WM_CLOSE signal
|
||||
@@ -86,8 +86,18 @@ private:
|
||||
Hotkey m_paste_as_markdown_hotkey{};
|
||||
Hotkey m_paste_as_json_hotkey{};
|
||||
|
||||
std::vector<Hotkey> m_custom_action_hotkeys;
|
||||
std::vector<int> m_custom_action_ids;
|
||||
template<class Id>
|
||||
struct ActionData
|
||||
{
|
||||
Id id;
|
||||
Hotkey hotkey;
|
||||
};
|
||||
|
||||
using AdditionalAction = ActionData<std::wstring>;
|
||||
std::vector<AdditionalAction> m_additional_actions;
|
||||
|
||||
using CustomAction = ActionData<int>;
|
||||
std::vector<CustomAction> m_custom_actions;
|
||||
|
||||
bool m_preview_custom_format_output = true;
|
||||
|
||||
@@ -166,6 +176,34 @@ private:
|
||||
open_ai_key_exists();
|
||||
}
|
||||
|
||||
static std::wstring kebab_to_pascal_case(const std::wstring& kebab_str)
|
||||
{
|
||||
std::wstring result;
|
||||
bool capitalize_next = true;
|
||||
|
||||
for (const auto ch : kebab_str)
|
||||
{
|
||||
if (ch == L'-')
|
||||
{
|
||||
capitalize_next = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (capitalize_next)
|
||||
{
|
||||
result += std::towupper(ch);
|
||||
capitalize_next = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool migrate_data_and_remove_data_file(Hotkey& old_paste_as_plain_hotkey)
|
||||
{
|
||||
const wchar_t OLD_JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
|
||||
@@ -197,6 +235,39 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
void process_additional_action(const winrt::hstring& actionName, const winrt::Windows::Data::Json::IJsonValue& actionValue)
|
||||
{
|
||||
if (actionValue.ValueType() != winrt::Windows::Data::Json::JsonValueType::Object)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = actionValue.GetObjectW();
|
||||
|
||||
if (!action.GetNamedBoolean(JSON_KEY_IS_SHOWN, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.HasKey(JSON_KEY_SHORTCUT))
|
||||
{
|
||||
const AdditionalAction additionalAction
|
||||
{
|
||||
actionName.c_str(),
|
||||
parse_single_hotkey(action.GetNamedObject(JSON_KEY_SHORTCUT))
|
||||
};
|
||||
|
||||
m_additional_actions.push_back(additionalAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& [subActionName, subAction] : action)
|
||||
{
|
||||
process_additional_action(subActionName, subAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parse_hotkeys(PowerToysSettings::PowerToyValues& settings)
|
||||
{
|
||||
auto settingsObject = settings.get_raw_json();
|
||||
@@ -239,13 +310,23 @@ private:
|
||||
*hotkey = parse_single_hotkey(keyName, settingsObject);
|
||||
}
|
||||
|
||||
m_custom_action_hotkeys.clear();
|
||||
m_custom_action_ids.clear();
|
||||
m_additional_actions.clear();
|
||||
m_custom_actions.clear();
|
||||
|
||||
if (settingsObject.HasKey(JSON_KEY_PROPERTIES))
|
||||
{
|
||||
const auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES);
|
||||
|
||||
if (propertiesObject.HasKey(JSON_KEY_ADDITIONAL_ACTIONS))
|
||||
{
|
||||
const auto additionalActions = propertiesObject.GetNamedObject(JSON_KEY_ADDITIONAL_ACTIONS);
|
||||
|
||||
for (const auto& [actionName, additionalAction] : additionalActions)
|
||||
{
|
||||
process_additional_action(actionName, additionalAction);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesObject.HasKey(JSON_KEY_CUSTOM_ACTIONS))
|
||||
{
|
||||
const auto customActions = propertiesObject.GetNamedObject(JSON_KEY_CUSTOM_ACTIONS).GetNamedArray(JSON_KEY_VALUE);
|
||||
@@ -257,8 +338,13 @@ private:
|
||||
|
||||
if (object.GetNamedBoolean(JSON_KEY_IS_SHOWN, false))
|
||||
{
|
||||
m_custom_action_hotkeys.push_back(parse_single_hotkey(object.GetNamedObject(JSON_KEY_SHORTCUT)));
|
||||
m_custom_action_ids.push_back(static_cast<int>(object.GetNamedNumber(JSON_KEY_ID)));
|
||||
const CustomAction customActionData
|
||||
{
|
||||
static_cast<int>(object.GetNamedNumber(JSON_KEY_ID)),
|
||||
parse_single_hotkey(object.GetNamedObject(JSON_KEY_SHORTCUT))
|
||||
};
|
||||
|
||||
m_custom_actions.push_back(customActionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -331,7 +417,7 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
create_pipe_thread = std::thread([&] { start_named_pipe_server(pipe_name.value()); });
|
||||
std::thread create_pipe_thread ([&]{ start_named_pipe_server(pipe_name.value()); });
|
||||
launch_process(pipe_name.value());
|
||||
create_pipe_thread.join();
|
||||
}
|
||||
@@ -730,12 +816,19 @@ public:
|
||||
m_preview_custom_format_output = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHOW_CUSTOM_PREVIEW).GetNamedBoolean(JSON_KEY_VALUE);
|
||||
}
|
||||
|
||||
std::unordered_map<std::wstring, Hotkey> additionalActionMap;
|
||||
for (const auto& action : m_additional_actions)
|
||||
{
|
||||
additionalActionMap[kebab_to_pascal_case(action.id)] = action.hotkey;
|
||||
}
|
||||
|
||||
// order of args matter
|
||||
Trace::AdvancedPaste_SettingsTelemetry(m_paste_as_plain_hotkey,
|
||||
m_advanced_paste_ui_hotkey,
|
||||
m_paste_as_markdown_hotkey,
|
||||
m_paste_as_json_hotkey,
|
||||
m_preview_custom_format_output);
|
||||
m_preview_custom_format_output,
|
||||
additionalActionMap);
|
||||
|
||||
// If you don't need to do any custom processing of the settings, proceed
|
||||
// to persists the values calling:
|
||||
@@ -825,11 +918,24 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto custom_action_index = hotkeyId - NUM_DEFAULT_HOTKEYS;
|
||||
|
||||
if (custom_action_index < m_custom_action_ids.size())
|
||||
const auto additional_action_index = hotkeyId - NUM_DEFAULT_HOTKEYS;
|
||||
if (additional_action_index < m_additional_actions.size())
|
||||
{
|
||||
const auto id = m_custom_action_ids.at(custom_action_index);
|
||||
const auto& id = m_additional_actions.at(additional_action_index).id;
|
||||
|
||||
Logger::trace(L"Starting additional action id={}", id);
|
||||
|
||||
Trace::AdvancedPaste_Invoked(std::format(L"{}Direct", kebab_to_pascal_case(id)));
|
||||
|
||||
send_named_pipe_message(CommonSharedConstants::ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto custom_action_index = additional_action_index - m_additional_actions.size();
|
||||
if (custom_action_index < m_custom_actions.size())
|
||||
{
|
||||
const auto id = m_custom_actions.at(custom_action_index).id;
|
||||
|
||||
Logger::trace(L"Starting custom action id={}", id);
|
||||
|
||||
@@ -844,7 +950,7 @@ public:
|
||||
|
||||
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
|
||||
{
|
||||
const size_t num_hotkeys = NUM_DEFAULT_HOTKEYS + m_custom_action_hotkeys.size();
|
||||
const size_t num_hotkeys = NUM_DEFAULT_HOTKEYS + m_additional_actions.size() + m_custom_actions.size();
|
||||
|
||||
if (hotkeys && buffer_size >= num_hotkeys)
|
||||
{
|
||||
@@ -852,9 +958,11 @@ public:
|
||||
m_advanced_paste_ui_hotkey,
|
||||
m_paste_as_markdown_hotkey,
|
||||
m_paste_as_json_hotkey };
|
||||
|
||||
std::copy(default_hotkeys.begin(), default_hotkeys.end(), hotkeys);
|
||||
std::copy(m_custom_action_hotkeys.begin(), m_custom_action_hotkeys.end(), hotkeys + NUM_DEFAULT_HOTKEYS);
|
||||
|
||||
const auto get_action_hotkey = [](const auto& action) { return action.hotkey; };
|
||||
std::transform(m_additional_actions.begin(), m_additional_actions.end(), hotkeys + NUM_DEFAULT_HOTKEYS, get_action_hotkey);
|
||||
std::transform(m_custom_actions.begin(), m_custom_actions.end(), hotkeys + NUM_DEFAULT_HOTKEYS + m_additional_actions.size(), get_action_hotkey);
|
||||
}
|
||||
|
||||
return num_hotkeys;
|
||||
|
||||
@@ -58,45 +58,44 @@ void Trace::AdvancedPaste_SettingsTelemetry(const PowertoyModuleIface::Hotkey& p
|
||||
const PowertoyModuleIface::Hotkey& advancedPasteUIHotkey,
|
||||
const PowertoyModuleIface::Hotkey& pasteMarkdownHotkey,
|
||||
const PowertoyModuleIface::Hotkey& pasteJsonHotkey,
|
||||
const bool preview_custom_format_output) noexcept
|
||||
const bool preview_custom_format_output,
|
||||
const std::unordered_map<std::wstring, PowertoyModuleIface::Hotkey>& additionalActionsHotkeys) noexcept
|
||||
{
|
||||
std::wstring pastePlainHotkeyStr =
|
||||
std::wstring(pastePlainHotkey.win ? L"Win + " : L"") +
|
||||
std::wstring(pastePlainHotkey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(pastePlainHotkey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(pastePlainHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(pastePlainHotkey.key);
|
||||
const auto getHotKeyStr = [](const PowertoyModuleIface::Hotkey& hotKey)
|
||||
{
|
||||
return std::wstring(hotKey.win ? L"Win + " : L"") +
|
||||
std::wstring(hotKey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(hotKey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(hotKey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(hotKey.key);
|
||||
};
|
||||
|
||||
std::wstring advancedPasteUIHotkeyStr =
|
||||
std::wstring(advancedPasteUIHotkey.win ? L"Win + " : L"") +
|
||||
std::wstring(advancedPasteUIHotkey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(advancedPasteUIHotkey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(advancedPasteUIHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(advancedPasteUIHotkey.key);
|
||||
std::vector<std::wstring> hotkeyStrs;
|
||||
const auto getHotkeyCStr = [&](const PowertoyModuleIface::Hotkey& hotkey)
|
||||
{
|
||||
hotkeyStrs.push_back(getHotKeyStr(hotkey)); // Probably unnecessary, but offers protection against the macro TraceLoggingWideString expanding to something that would invalidate the pointer
|
||||
return hotkeyStrs.back().c_str();
|
||||
};
|
||||
|
||||
std::wstring pasteMarkdownHotkeyStr =
|
||||
std::wstring(pasteMarkdownHotkey.win ? L"Win + " : L"") +
|
||||
std::wstring(pasteMarkdownHotkey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(pasteMarkdownHotkey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(pasteMarkdownHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(pasteMarkdownHotkey.key);
|
||||
|
||||
std::wstring pasteJsonHotkeyStr =
|
||||
std::wstring(pasteJsonHotkey.win ? L"Win + " : L"") +
|
||||
std::wstring(pasteJsonHotkey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(pasteJsonHotkey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(pasteJsonHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(pasteJsonHotkey.key);
|
||||
const auto getAdditionalActionHotkeyCStr = [&](const std::wstring& name)
|
||||
{
|
||||
const auto it = additionalActionsHotkeys.find(name);
|
||||
return it != additionalActionsHotkeys.end() ? getHotkeyCStr(it->second) : L"";
|
||||
};
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"AdvancedPaste_Settings",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(pastePlainHotkeyStr.c_str(), "PastePlainHotkey"),
|
||||
TraceLoggingWideString(advancedPasteUIHotkeyStr.c_str(), "AdvancedPasteUIHotkey"),
|
||||
TraceLoggingWideString(pasteMarkdownHotkeyStr.c_str(), "PasteMarkdownHotkey"),
|
||||
TraceLoggingWideString(pasteJsonHotkeyStr.c_str(), "PasteJsonHotkey"),
|
||||
TraceLoggingBoolean(preview_custom_format_output, "ShowCustomPreview")
|
||||
TraceLoggingWideString(getHotkeyCStr(pastePlainHotkey), "PastePlainHotkey"),
|
||||
TraceLoggingWideString(getHotkeyCStr(advancedPasteUIHotkey), "AdvancedPasteUIHotkey"),
|
||||
TraceLoggingWideString(getHotkeyCStr(pasteMarkdownHotkey), "PasteMarkdownHotkey"),
|
||||
TraceLoggingWideString(getHotkeyCStr(pasteJsonHotkey), "PasteJsonHotkey"),
|
||||
TraceLoggingBoolean(preview_custom_format_output, "ShowCustomPreview"),
|
||||
TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"ImageToText"), "ImageToTextHotkey"),
|
||||
TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"PasteAsTxtFile"), "PasteAsTxtFileHotkey"),
|
||||
TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"PasteAsPngFile"), "PasteAsPngFileHotkey"),
|
||||
TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"PasteAsHtmlFile"), "PasteAsHtmlFileHotkey")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
#include <unordered_map>
|
||||
|
||||
class Trace
|
||||
{
|
||||
@@ -21,5 +22,6 @@ public:
|
||||
const PowertoyModuleIface::Hotkey& advancedPasteUIHotkey,
|
||||
const PowertoyModuleIface::Hotkey& pasteMarkdownHotkey,
|
||||
const PowertoyModuleIface::Hotkey& pasteJsonHotkey,
|
||||
const bool preview_custom_format_output) noexcept;
|
||||
const bool preview_custom_format_output,
|
||||
const std::unordered_map<std::wstring, PowertoyModuleIface::Hotkey>& additionalActionsHotkeys) noexcept;
|
||||
};
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
<!-- This package is a dependency of Microsoft.Extensions.Logging.EventLog, but we need to set it here so we can exclude the assets, so it doesn't conflict with the 8.0.1 dll coming from .NET SDK. -->
|
||||
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.CodeDom">
|
||||
<!-- This package is a dependency of System.Management, but we need to set it here so we can exclude the assets, so it doesn't conflict with the 8.0.1 dll coming from .NET SDK. -->
|
||||
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -137,8 +137,8 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -149,8 +149,8 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240829007\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -4,5 +4,5 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.240829007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.240923002" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -2,20 +2,15 @@
|
||||
// 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.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Helpers;
|
||||
using MouseJumpUI.Common.Imaging;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
using MouseJumpUI.Helpers;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Imaging;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Helpers;
|
||||
namespace MouseJump.Common.UnitTests.Helpers;
|
||||
|
||||
[TestClass]
|
||||
public static class DrawingHelperTests
|
||||
@@ -60,8 +55,8 @@ public static class DrawingHelperTests
|
||||
new(0, 500, 500, 500),
|
||||
},
|
||||
activatedLocation: new(x: 50, y: 50),
|
||||
desktopImageFilename: "Common/Helpers/_test-4grid-desktop.png",
|
||||
expectedImageFilename: "Common/Helpers/_test-4grid-expected.png"),
|
||||
desktopImageFilename: "_test-4grid-desktop.png",
|
||||
expectedImageFilename: "_test-4grid-expected.png"),
|
||||
};
|
||||
/* win 11 */
|
||||
yield return new object[]
|
||||
@@ -74,8 +69,8 @@ public static class DrawingHelperTests
|
||||
new(0, 0, 5120, 1440),
|
||||
},
|
||||
activatedLocation: new(x: 50, y: 50),
|
||||
desktopImageFilename: "Common/Helpers/_test-win11-desktop.png",
|
||||
expectedImageFilename: "Common/Helpers/_test-win11-expected.png"),
|
||||
desktopImageFilename: "_test-win11-desktop.png",
|
||||
expectedImageFilename: "_test-win11-expected.png"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,13 +99,19 @@ public static class DrawingHelperTests
|
||||
|
||||
private static Bitmap LoadImageResource(string filename)
|
||||
{
|
||||
// assume embedded resources are in the same source folder as this
|
||||
// class, and the namespace hierarchy matches the folder structure.
|
||||
// that way we can build resource names from the current namespace
|
||||
var resourcePrefix = typeof(DrawingHelperTests).Namespace;
|
||||
var resourceName = $"{resourcePrefix}.{filename}";
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
|
||||
var resourceName = $"Microsoft.{assemblyName.Name}.{filename.Replace("/", ".")}";
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
if (!resourceNames.Contains(resourceName))
|
||||
{
|
||||
throw new InvalidOperationException($"Embedded resource '{resourceName}' does not exist.");
|
||||
var message = $"Embedded resource '{resourceName}' does not exist. " +
|
||||
"Valid resource names are: \r\n" + string.Join("\r\n", resourceNames);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(resourceName)
|
||||
@@ -2,17 +2,15 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text.Json;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Helpers;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Layout;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Layout;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Helpers;
|
||||
namespace MouseJump.Common.UnitTests.Helpers;
|
||||
|
||||
[TestClass]
|
||||
public static class LayoutHelperTests
|
||||
@@ -2,13 +2,11 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Helpers;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Helpers;
|
||||
namespace MouseJump.Common.UnitTests.Helpers;
|
||||
|
||||
[TestClass]
|
||||
public static class MouseHelperTests
|
||||
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 215 KiB |
@@ -2,12 +2,10 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.UnitTests.Models.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public static class RectangleInfoTests
|
||||
@@ -2,12 +2,10 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.UnitTests.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.UnitTests.Models.Drawing;
|
||||
|
||||
[TestClass]
|
||||
public static class SizeInfoTests
|
||||
@@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
<ProjectGuid>{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}</ProjectGuid>
|
||||
<AssemblyName>PowerToys.MouseJump.Common.UnitTests</AssemblyName>
|
||||
<AssemblyTitle>PowerToys.MouseJump.Common.UnitTests</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys MouseJump.Common.UnitTests</AssemblyDescription>
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\MouseJump.Common.UnitTests\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Helpers\_test-4grid-desktop.png" />
|
||||
<None Remove="Helpers\_test-4grid-expected.png" />
|
||||
<None Remove="Helpers\_test-win11-desktop.png" />
|
||||
<None Remove="Helpers\_test-win11-expected.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Helpers\_test-4grid-desktop.png" />
|
||||
<EmbeddedResource Include="Helpers\_test-4grid-expected.png" />
|
||||
<EmbeddedResource Include="Helpers\_test-win11-desktop.png" />
|
||||
<EmbeddedResource Include="Helpers\_test-win11-expected.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MouseJump.Common\MouseJump.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -2,22 +2,18 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
|
||||
using MouseJumpUI.Common.Imaging;
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Layout;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
using MouseJump.Common.Imaging;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Layout;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.Common.Helpers;
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
internal static class DrawingHelper
|
||||
public static class DrawingHelper
|
||||
{
|
||||
public static Bitmap RenderPreview(
|
||||
PreviewLayout previewLayout,
|
||||
@@ -2,17 +2,13 @@
|
||||
// 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 MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Layout;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Layout;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
namespace MouseJumpUI.Common.Helpers;
|
||||
|
||||
internal static class LayoutHelper
|
||||
public static class LayoutHelper
|
||||
{
|
||||
public static PreviewLayout GetPreviewLayout(
|
||||
PreviewStyle previewStyle, List<RectangleInfo> screens, PointInfo activatedLocation)
|
||||
@@ -111,7 +107,7 @@ internal static class LayoutHelper
|
||||
/// <returns>A <see cref="BoxBounds"/> object that represents the bounds of the different areas of the box.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="contentBounds"/> or <paramref name="boxStyle"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when any of the styles in <paramref name="boxStyle"/> is null.</exception>
|
||||
internal static BoxBounds GetBoxBoundsFromContentBounds(
|
||||
public static BoxBounds GetBoxBoundsFromContentBounds(
|
||||
RectangleInfo contentBounds,
|
||||
BoxStyle boxStyle)
|
||||
{
|
||||
@@ -139,7 +135,7 @@ internal static class LayoutHelper
|
||||
/// <returns>A <see cref="BoxBounds"/> object that represents the bounds of the different areas of the box.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="outerBounds"/> or <paramref name="boxStyle"/> is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when any of the styles in <paramref name="boxStyle"/> is null.</exception>
|
||||
internal static BoxBounds GetBoxBoundsFromOuterBounds(
|
||||
public static BoxBounds GetBoxBoundsFromOuterBounds(
|
||||
RectangleInfo outerBounds,
|
||||
BoxStyle boxStyle)
|
||||
{
|
||||
@@ -5,15 +5,14 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.NativeMethods;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.NativeMethods;
|
||||
using static MouseJump.Common.NativeMethods.Core;
|
||||
using static MouseJump.Common.NativeMethods.User32;
|
||||
|
||||
using static MouseJumpUI.Common.NativeMethods.Core;
|
||||
using static MouseJumpUI.Common.NativeMethods.User32;
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
namespace MouseJumpUI.Common.Helpers;
|
||||
|
||||
internal static class MouseHelper
|
||||
public static class MouseHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates where to move the cursor to by projecting a point from
|
||||
@@ -24,7 +23,7 @@ internal static class MouseHelper
|
||||
/// or even negative if the primary monitor is not the at the top-left of the
|
||||
/// entire desktop rectangle, so results may contain negative coordinates.
|
||||
/// </remarks>
|
||||
internal static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds)
|
||||
public static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds)
|
||||
{
|
||||
return previewLocation
|
||||
.Scale(previewSize.ScaleToFitRatio(desktopBounds.Size))
|
||||
@@ -34,7 +33,7 @@ internal static class MouseHelper
|
||||
/// <summary>
|
||||
/// Get the current position of the cursor.
|
||||
/// </summary>
|
||||
internal static PointInfo GetCursorPosition()
|
||||
public static PointInfo GetCursorPosition()
|
||||
{
|
||||
var lpPoint = new LPPOINT(new POINT(0, 0));
|
||||
var result = User32.GetCursorPos(lpPoint);
|
||||
@@ -57,7 +56,7 @@ internal static class MouseHelper
|
||||
/// <remarks>
|
||||
/// See https://github.com/mikeclayton/FancyMouse/pull/3
|
||||
/// </remarks>
|
||||
internal static void SetCursorPosition(PointInfo location)
|
||||
public static void SetCursorPosition(PointInfo location)
|
||||
{
|
||||
// set the new cursor position *twice* - the cursor sometimes end up in
|
||||
// the wrong place if we try to cross the dead space between non-aligned
|
||||
@@ -2,26 +2,22 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.NativeMethods;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.NativeMethods;
|
||||
using static MouseJump.Common.NativeMethods.Core;
|
||||
using static MouseJump.Common.NativeMethods.User32;
|
||||
|
||||
using static MouseJumpUI.Common.NativeMethods.Core;
|
||||
using static MouseJumpUI.Common.NativeMethods.User32;
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
namespace MouseJumpUI.Common.Helpers;
|
||||
|
||||
internal static class ScreenHelper
|
||||
public static class ScreenHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Duplicates functionality available in System.Windows.Forms.SystemInformation
|
||||
/// to reduce the dependency on WinForms
|
||||
/// </summary>
|
||||
public static RectangleInfo GetVirtualScreen()
|
||||
private static RectangleInfo GetVirtualScreen()
|
||||
{
|
||||
return new(
|
||||
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN),
|
||||
@@ -30,7 +26,7 @@ internal static class ScreenHelper
|
||||
User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN));
|
||||
}
|
||||
|
||||
internal static IEnumerable<ScreenInfo> GetAllScreens()
|
||||
public static IEnumerable<ScreenInfo> GetAllScreens()
|
||||
{
|
||||
// enumerate the monitors attached to the system
|
||||
var hMonitors = new List<HMONITOR>();
|
||||
@@ -80,7 +76,7 @@ internal static class ScreenHelper
|
||||
}
|
||||
}
|
||||
|
||||
internal static ScreenInfo GetScreenFromPoint(
|
||||
public static ScreenInfo GetScreenFromPoint(
|
||||
List<ScreenInfo> screens,
|
||||
PointInfo pt)
|
||||
{
|
||||
@@ -2,15 +2,12 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
internal static class StyleHelper
|
||||
public static class StyleHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Default v2 preview style
|
||||
@@ -2,22 +2,19 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.NativeMethods;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.NativeMethods;
|
||||
using static MouseJump.Common.NativeMethods.Core;
|
||||
|
||||
using static MouseJumpUI.Common.NativeMethods.Core;
|
||||
|
||||
namespace MouseJumpUI.Common.Imaging;
|
||||
namespace MouseJump.Common.Imaging;
|
||||
|
||||
/// <summary>
|
||||
/// Implements an IImageRegionCopyService that uses the current desktop window as the copy source.
|
||||
/// This is used during the main application runtime to generate preview images of the desktop.
|
||||
/// </summary>
|
||||
internal sealed class DesktopImageRegionCopyService : IImageRegionCopyService
|
||||
public sealed class DesktopImageRegionCopyService : IImageRegionCopyService
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies the source region from the current desktop window
|
||||
@@ -2,13 +2,11 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Drawing;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.Imaging;
|
||||
|
||||
namespace MouseJumpUI.Common.Imaging;
|
||||
|
||||
internal interface IImageRegionCopyService
|
||||
public interface IImageRegionCopyService
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies the source region from the provider's source image (e.g. the interactive desktop,
|
||||
@@ -2,18 +2,15 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Common.Imaging;
|
||||
namespace MouseJump.Common.Imaging;
|
||||
|
||||
/// <summary>
|
||||
/// Implements an IImageRegionCopyService that uses the specified image as the copy source.
|
||||
/// This is used for testing the DrawingHelper rather than as part of the main application.
|
||||
/// </summary>
|
||||
internal sealed class StaticImageRegionCopyService : IImageRegionCopyService
|
||||
public sealed class StaticImageRegionCopyService : IImageRegionCopyService
|
||||
{
|
||||
public StaticImageRegionCopyService(Image sourceImage)
|
||||
{
|
||||
@@ -2,9 +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;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.Models.Drawing;
|
||||
|
||||
public sealed class BoxBounds
|
||||
{
|
||||
@@ -28,7 +26,7 @@ public sealed class BoxBounds
|
||||
|
||||
*/
|
||||
|
||||
internal BoxBounds(
|
||||
public BoxBounds(
|
||||
RectangleInfo outerBounds,
|
||||
RectangleInfo marginBounds,
|
||||
RectangleInfo borderBounds,
|
||||
@@ -2,10 +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.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Point object with some extra utility methods.
|
||||
@@ -2,15 +2,12 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle;
|
||||
|
||||
using BorderStyle = MouseJumpUI.Common.Models.Styles.BorderStyle;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods.
|
||||
@@ -2,19 +2,15 @@
|
||||
// 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 static MouseJumpUI.Common.NativeMethods.Core;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Windows.Forms.Screen object so we don't need to
|
||||
/// take a dependency on WinForms just for screen info.
|
||||
/// </summary>
|
||||
internal sealed class ScreenInfo
|
||||
public sealed class ScreenInfo
|
||||
{
|
||||
internal ScreenInfo(HMONITOR handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
|
||||
public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
|
||||
{
|
||||
this.Handle = handle;
|
||||
this.Primary = primary;
|
||||
@@ -2,14 +2,10 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle;
|
||||
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
|
||||
using BorderStyle = MouseJumpUI.Common.Models.Styles.BorderStyle;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Drawing;
|
||||
namespace MouseJump.Common.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Size object with some extra utility methods.
|
||||
@@ -2,15 +2,12 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
using MouseJumpUI.Common.Models.Styles;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Layout;
|
||||
namespace MouseJump.Common.Models.Layout;
|
||||
|
||||
public sealed class PreviewLayout
|
||||
{
|
||||
@@ -2,9 +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.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Models.Styles;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the background fill style for a drawing object.
|
||||
@@ -2,9 +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.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Models.Styles;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the border style for a drawing object.
|
||||
@@ -2,9 +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;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Models.Styles;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the styles to apply to a simple box-layout based drawing object.
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Models.Styles;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the margin style for a drawing object.
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Models.Styles;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the margin style for a drawing object.
|
||||
@@ -2,11 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
using MouseJumpUI.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Common.Models.Styles;
|
||||
namespace MouseJump.Common.Models.Styles;
|
||||
|
||||
public sealed class PreviewStyle
|
||||
{
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
<ProjectGuid>{923DF87C-CA99-4D1C-B1D2-959174E95BFA}</ProjectGuid>
|
||||
<AssemblyName>PowerToys.MouseJump.Common</AssemblyName>
|
||||
<AssemblyTitle>PowerToys.MouseJump.Common</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys MouseJump.Common</AssemblyDescription>
|
||||
<OutputType>Library</OutputType>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
|
||||
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -5,7 +5,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,9 +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;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,9 +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;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,9 +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;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,10 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
using System;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,10 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,10 +2,9 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
using System;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -5,7 +5,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -5,7 +5,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,9 +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;
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Core
|
||||
{
|
||||
@@ -2,7 +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.
|
||||
|
||||
namespace MouseJumpUI.Common.NativeMethods;
|
||||
namespace MouseJump.Common.NativeMethods;
|
||||
|
||||
internal static partial class Gdi32
|
||||
{
|
||||