Compare commits

..

1 Commits

Author SHA1 Message Date
Kayla Cinnamon
bec6693359 Update action-genai-issue-dedup version to v0 2025-09-02 20:02:36 +00:00
236 changed files with 3291 additions and 7319 deletions

View File

@@ -94,7 +94,6 @@ onefuzzingestionpreparationtool
OTP
Yubi
Yubico
Perplexity
svgl
# KEYS
@@ -320,4 +319,4 @@ MRUINFO
REGSTR
# Misc Win32 APIs and PInvokes
INVOKEIDLIST
INVOKEIDLIST

View File

@@ -29,8 +29,6 @@ shortcutguide
# 8LWXpg is user name but user folder causes a flag
LWXpg
# 0x6f677548 is user name but user folder causes a flag
x6f677548
Adoumie
Advaith
alekhyareddy

View File

@@ -27,7 +27,6 @@ admx
advancedpaste
advancedpasteui
advancedpasteuishortcut
advapi
advfirewall
AFeature
affordances
@@ -248,7 +247,6 @@ CONFIGW
CONFLICTINGMODIFIERKEY
CONFLICTINGMODIFIERSHORTCUT
CONOUT
coreclr
constexpr
contentdialog
contentfiles
@@ -270,8 +268,6 @@ cpcontrols
cph
cplusplus
CPower
cpptools
cppvsdbg
cppwinrt
createdump
CREATEPROCESS
@@ -283,7 +279,6 @@ CRH
critsec
cropandlock
Crossdevice
csdevkit
CSearch
CSettings
cso
@@ -311,7 +306,6 @@ CXVIRTUALSCREEN
CYSCREEN
CYSMICON
CYVIRTUALSCREEN
Czechia
cziplib
Dac
dacl
@@ -336,7 +330,6 @@ Deact
debugbreak
decryptor
Dedup
Deduplicator
Deeplink
DEFAULTBOOTSTRAPPERINSTALLFOLDER
DEFAULTCOLOR
@@ -442,7 +435,6 @@ EDITSHORTCUTS
EDITTEXT
EFile
ekus
emojis
ENABLEDELAYEDEXPANSION
ENABLEDPOPUP
ENABLETAB
@@ -807,7 +799,6 @@ KEYBOARDMANAGEREDITORLIBRARYWRAPPER
keyboardmanagerstate
keyboardmanagerui
keyboardtester
keycap
KEYEVENTF
KEYIMAGE
keynum
@@ -1456,6 +1447,7 @@ rstringalnum
rstringalpha
rstringdigit
rtb
RTB
RTLREADING
rtm
runas
@@ -1788,13 +1780,10 @@ UACUI
UAL
uap
UBR
UBreak
ubrk
UCallback
ucrt
ucrtd
uefi
UError
uesc
UFlags
UHash
@@ -1802,7 +1791,6 @@ UIA
UIEx
uild
uitests
UITo
ULONGLONG
ums
uncompilable
@@ -1865,7 +1853,6 @@ VFT
vget
vgetq
viewmodels
virama
VIRTKEY
VIRTUALDESK
VISEGRADRELAY
@@ -2017,7 +2004,6 @@ XButton
xclip
xcopy
XDeployment
xdf
XDocument
XElement
xfd

View File

@@ -1,43 +0,0 @@
# PowerToys Copilot guide (concise)
This is the top-level guide for AI changes. Keep edits small, follow existing patterns, and cite exact paths in PRs.
Repo map (1line per area)
- Core apps: `src/runner/**` (tray/loader), `src/settings-ui/**` (Settings app)
- Shared libs: `src/common/**`
- Modules: `src/modules/*` (one per utility; Command Palette in `src/modules/cmdpal/**`)
- Build tools/docs: `tools/**`, `doc/devdocs/**`
Build and test (defaults)
- Prerequisites: Visual Studio 2022 17.4+, minimal Windows 10 1803+.
- Build discipline:
- One terminal per operation (build → test). Dont switch/open new ones mid-flow.
- After making changes, `cd` to the project folder that changed (`.csproj`/`.vcxproj`).
- Use script(s) to build, synchronously block and wait in foreground for it to finish: `tools/build/build.ps1|.cmd` (current folder), `build-essentials.*` (once per brand new build for missing nuget packages)
- Treat build **exit code 0** as success; any non-zero exit code is a failure, have Copilot read the errors log in the build folder (e.g., `build.*.*.errors.log`) and surface problems.
- Dont start tests or launch Runner until the previous step succeeded.
- Tests (fast + targeted):
- Find the test project by product code prefix (e.g., FancyZones, AdvancedPaste). Look for a sibling folder or 12 levels up named like `<Product>*UnitTests` or `<Product>*UITests`.
- Build the test project, wait for **exit**, then run only those tests via VS Test Explorer or `vstest.console.exe` with filters. Avoid `dotnet test` in this repo.
- Add/adjust tests when changing behavior; if skipped, state why (e.g., comment-only, string rename).
Pull requests (expectations)
- Atomic: one logical change; no driveby refactors.
- Describe: problem / approach / risk / test evidence.
- List: touched paths if not obvious.
When to ask for clarification
- Ambiguous spec after scanning relevant docs (see below).
- Cross-module impact (shared enum/struct) not clear.
- Security / elevation / installer changes.
Logging (use existing stacks)
- C++: `src/common/logger/**` (`Logger::info|warn|error|debug`). Keep hot paths quiet (hooks, tight loops).
- C#: `ManagedCommon.Logger` (`LogInfo|LogWarning|LogError|LogDebug|LogTrace`). Some UIs use injected `ILogger` via `LoggerInstance.Logger`.
Docs to consult
- `tools/build/BUILD-GUIDELINES.md`
- `doc/devdocs/core/architecture.md`, `doc/devdocs/core/runner.md`, `doc/devdocs/core/settings/readme.md`, `doc/devdocs/modules/readme.md`
Done checklist (self review before finishing)
- Build clean? Tests updated/passed? No unintended formatting? Any new dependency? Documented skips?

View File

@@ -1,19 +0,0 @@
name: Automatic New Issue Deduplication
on:
issues:
types: [opened, reopened]
permissions:
models: read
issues: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true
jobs:
deduplicate:
runs-on: ubuntu-latest
steps:
- name: Run Deduplicate Action
uses: pelikhan/action-genai-issue-dedup@v0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
label_as_duplicate: true

View File

@@ -1,38 +1,20 @@
name: Manual Batch Issue Deduplication
on:
workflow_dispatch:
inputs:
issue_numbers:
description: "JSON array of issue numbers to deduplicate (e.g. [101,102,103])"
required: true
since:
description: "Only compare against issues created after this date (ISO 8601, e.g. 2019-05-05T00:00:00Z)"
required: false
default: "2019-05-05T00:00:00Z"
label_as_duplicate:
description: "Apply duplicate label if duplicates are found (true/false)"
required: false
default: "true"
permissions:
models: read
issues: write
workflow_dispatch: # Only runs when manually triggered
jobs:
deduplicate:
batch-deduplicate:
runs-on: ubuntu-latest
strategy:
matrix:
issue: ${{ fromJson(github.event.inputs.issue_numbers) }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Run GenAI Issue Deduplicator
- name: Batch Deduplicate Issues
uses: pelikhan/action-genai-issue-dedup@v0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
github_issue: ${{ matrix.issue }}
label_as_duplicate: ${{ github.event.inputs.label_as_duplicate }}
github-token: ${{ secrets.GITHUB_TOKEN }}
label-duplicate: "potential duplicate"
comment-duplicate: true
close-duplicate: false
batch-size: 100
since: '2019-05-05T00:00:00Z' # Process issues dating back to 2019
duplicate-comment-template: "This issue appears to be a duplicate of #{duplicate_issue_number}."
# Add other action-specific inputs if needed

View File

@@ -411,28 +411,9 @@ jobs:
!**\obj\**
- pwsh: |-
$Packages = Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix"
Write-Host "Found $($Packages.Count) CmdPal MSIX package(s):"
foreach ($pkg in $Packages) {
Write-Host " - $($pkg.FullName)"
}
if ($Packages.Count -gt 0) {
# Priority: Look for platform-specific MSIX (x64/arm64) first, then fall back to any
$PlatformPackage = $Packages | Where-Object { $_.Name -match "Microsoft\.CmdPal\.UI_.*_(x64|arm64)\.msix$" } | Select-Object -First 1
if ($PlatformPackage) {
$Package = $PlatformPackage
Write-Host "Using platform-specific package: $($Package.FullName)"
} else {
$Package = $Packages | Select-Object -First 1
Write-Host "Using first available package: $($Package.FullName)"
}
$PackageFilename = $Package.FullName
Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}"
} else {
Write-Warning "No CmdPal MSIX packages found!"
}
$Package = (Get-ChildItem -Recurse -Filter "Microsoft.CmdPal.UI_*.msix" | Select -First 1)
$PackageFilename = $Package.FullName
Write-Host "##vso[task.setvariable variable=CmdPalPackagePath]${PackageFilename}"
displayName: Locate the CmdPal MSIX
- ${{ if eq(parameters.codeSign, true) }}:

43
.vscode/launch.json vendored
View File

@@ -1,43 +0,0 @@
{
"version": "0.2.0",
"inputs": [
{
"id": "arch",
"type": "pickString",
"description": "Select target architecture",
"options": ["x64", "arm64"],
"default": "x64"
}
],
"configurations": [
{
"name": "Run native executable (no build)",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}\\${input:arch}\\Debug\\PowerToys.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"console": "integratedTerminal"
},
{
"name": "C/C++ Attach to PowerToys Process (native)",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}",
"symbolSearchPath": "${workspaceFolder}\\${input:arch}\\Debug;${workspaceFolder}\\Debug;${workspaceFolder}\\symbols"
},
{
"name": "Run managed code (managed, no build, ARCH configurable)",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}\\${input:arch}\\Debug\\WinUI3Apps\\PowerToys.Settings.exe",
"args": [],
"cwd": "${workspaceFolder}",
"env": {},
"console": "internalConsole",
"stopAtEntry": false
}
]
}

View File

@@ -45,7 +45,7 @@
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.8" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.8" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
@@ -57,8 +57,8 @@
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
-->
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4948" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
<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" />

View File

@@ -262,6 +262,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643
src\common\utils\EventLocker.h = src\common\utils\EventLocker.h
src\common\utils\EventWaiter.h = src\common\utils\EventWaiter.h
src\common\utils\excluded_apps.h = src\common\utils\excluded_apps.h
src\common\utils\shell_ext_registration.h = src\common\utils\shell_ext_registration.h
src\common\utils\exec.h = src\common\utils\exec.h
src\common\utils\game_mode.h = src\common\utils\game_mode.h
src\common\utils\gpo.h = src\common\utils\gpo.h
@@ -281,7 +282,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{B39DC643
src\common\utils\registry.h = src\common\utils\registry.h
src\common\utils\resources.h = src\common\utils\resources.h
src\common\utils\serialized.h = src\common\utils\serialized.h
src\common\utils\shell_ext_registration.h = src\common\utils\shell_ext_registration.h
src\common\utils\string_utils.h = src\common\utils\string_utils.h
src\common\utils\timeutil.h = src\common\utils\timeutil.h
src\common\utils\UnhandledExceptionHandler.h = src\common\utils\UnhandledExceptionHandler.h
@@ -793,10 +793,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Window
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.UnitTestBase", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.UnitTestsBase\Microsoft.CmdPal.Ext.UnitTestBase.csproj", "{00D8659C-2068-40B6-8B86-759CD6284BBB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{E11826E1-76DF-42AC-985C-164CC2EE57A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenRuler.UITests", "src\modules\MeasureTool\Tests\ScreenRuler.UITests\ScreenRuler.UITests.csproj", "{66C069F8-C548-4CA6-8CDE-239104D68E88}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Apps.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.Apps.UnitTests\Microsoft.CmdPal.Ext.Apps.UnitTests.csproj", "{E816D7B1-4688-4ECB-97CC-3D8E798F3830}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Bookmarks.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.Bookmarks.UnitTests\Microsoft.CmdPal.Ext.Bookmarks.UnitTests.csproj", "{E816D7B3-4688-4ECB-97CC-3D8E798F3832}"
@@ -2699,22 +2695,6 @@ Global
{61CBF221-9452-4934-B685-146285E080D7}.Release|ARM64.Build.0 = Release|ARM64
{61CBF221-9452-4934-B685-146285E080D7}.Release|x64.ActiveCfg = Release|x64
{61CBF221-9452-4934-B685-146285E080D7}.Release|x64.Build.0 = Release|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|ARM64.ActiveCfg = Debug|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|ARM64.Build.0 = Debug|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|x64.ActiveCfg = Debug|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|x64.Build.0 = Debug|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|ARM64.ActiveCfg = Release|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|ARM64.Build.0 = Release|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|x64.ActiveCfg = Release|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|x64.Build.0 = Release|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|ARM64.Build.0 = Debug|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|x64.ActiveCfg = Debug|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|x64.Build.0 = Debug|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|ARM64.ActiveCfg = Release|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|ARM64.Build.0 = Release|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|x64.ActiveCfg = Release|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|x64.Build.0 = Release|x64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|ARM64.Build.0 = Debug|ARM64
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1}.Debug|x64.ActiveCfg = Debug|x64
@@ -2731,6 +2711,30 @@ Global
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|ARM64.Build.0 = Release|ARM64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.ActiveCfg = Release|x64
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9}.Release|x64.Build.0 = Release|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|ARM64.ActiveCfg = Debug|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|ARM64.Build.0 = Debug|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|x64.ActiveCfg = Debug|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Debug|x64.Build.0 = Debug|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|ARM64.ActiveCfg = Release|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|ARM64.Build.0 = Release|ARM64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|x64.ActiveCfg = Release|x64
{38F187B2-6638-5A40-072F-DBE5E54070A0}.Release|x64.Build.0 = Release|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|ARM64.Build.0 = Debug|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|x64.ActiveCfg = Debug|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Debug|x64.Build.0 = Debug|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|ARM64.ActiveCfg = Release|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|ARM64.Build.0 = Release|ARM64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|x64.ActiveCfg = Release|x64
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE}.Release|x64.Build.0 = Release|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|ARM64.Build.0 = Debug|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.ActiveCfg = Debug|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Debug|x64.Build.0 = Debug|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.ActiveCfg = Release|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|ARM64.Build.0 = Release|ARM64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.ActiveCfg = Release|x64
{0217E86E-3476-9946-DE8E-9D200CEBD47A}.Release|x64.Build.0 = Release|x64
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|ARM64.Build.0 = Debug|ARM64
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6}.Debug|x64.ActiveCfg = Debug|x64
@@ -2883,14 +2887,6 @@ Global
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|ARM64.Build.0 = Release|ARM64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|x64.ActiveCfg = Release|x64
{00D8659C-2068-40B6-8B86-759CD6284BBB}.Release|x64.Build.0 = Release|x64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|ARM64.ActiveCfg = Debug|ARM64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|ARM64.Build.0 = Debug|ARM64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|x64.ActiveCfg = Debug|x64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Debug|x64.Build.0 = Debug|x64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Release|ARM64.ActiveCfg = Release|ARM64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Release|ARM64.Build.0 = Release|ARM64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Release|x64.ActiveCfg = Release|x64
{66C069F8-C548-4CA6-8CDE-239104D68E88}.Release|x64.Build.0 = Release|x64
{E816D7B1-4688-4ECB-97CC-3D8E798F3830}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E816D7B1-4688-4ECB-97CC-3D8E798F3830}.Debug|ARM64.Build.0 = Debug|ARM64
{E816D7B1-4688-4ECB-97CC-3D8E798F3830}.Debug|x64.ActiveCfg = Debug|x64
@@ -2932,6 +2928,7 @@ Global
{D1D6BC88-09AE-4FB4-AD24-5DED46A791DD} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9} = {264B412F-DB8B-4CF8-A74B-96998B183045}
{1AFB6476-670D-4E80-A464-657E01DFF482} = {557C4636-D7E1-4838-A504-7D19B725EE95}
{1A066C63-64B3-45F8-92FE-664E1CCE8077} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
@@ -3197,10 +3194,10 @@ Global
{9BC1C986-1E97-4D07-A7B1-CE226C239EFA} = {2F305555-C296-497E-AC20-5FA1B237996A}
{99CA1509-FB73-456E-AFAF-AB89C017BD72} = {6B01F1CF-F4DB-48B5-BFE7-0BF576C1D704}
{61CBF221-9452-4934-B685-146285E080D7} = {6B01F1CF-F4DB-48B5-BFE7-0BF576C1D704}
{38F187B2-6638-5A40-072F-DBE5E54070A0} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
{4E0AE3A4-2EE0-44D7-A2D0-8769977254A1} = {2C318EC3-BA86-4372-B1BC-DB0F33C208B2}
{43E779F3-D83C-48B1-BA8D-1912DBD76FC9} = {68328142-5B31-4715-BCBB-7B6345EE0971}
{38F187B2-6638-5A40-072F-DBE5E54070A0} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{DA0744BC-E822-680E-9CEB-D0FBA903A8EE} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
{2CF78CF7-8FEB-4BE1-9591-55FA25B48FC6} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{14AFD976-B4D2-49D0-9E6C-AA93CC061B8A} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{9D3F3793-EFE3-4525-8782-238015DABA62} = {66E1534A-1587-42B2-912F-45C994D32904}
@@ -3236,8 +3233,6 @@ Global
{E816D7AF-4688-4ECB-97CC-3D8E798F3828} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B0-4688-4ECB-97CC-3D8E798F3829} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{00D8659C-2068-40B6-8B86-759CD6284BBB} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E11826E1-76DF-42AC-985C-164CC2EE57A1} = {7AC943C9-52E8-44CF-9083-744D8049667B}
{66C069F8-C548-4CA6-8CDE-239104D68E88} = {E11826E1-76DF-42AC-985C-164CC2EE57A1}
{E816D7B1-4688-4ECB-97CC-3D8E798F3830} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B3-4688-4ECB-97CC-3D8E798F3832} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B2-4688-4ECB-97CC-3D8E798F3831} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}

View File

@@ -18,8 +18,8 @@ You can build the entire solution from the command line, which is sometimes fast
1. Open Developer Command Prompt for VS 2022
2. Navigate to the repository root directory
3. Run the following command(don't forget to set the correct platform):
```pwsh
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.sln /tl /p:NuGetInteractive="true"
```
msbuild -restore -p:RestorePackagesConfig=true -p:Platform=ARM64 -m PowerToys.sln
```
4. This process should complete in approximately 13-14 minutes for a full build

View File

@@ -1,128 +0,0 @@
## Developing PowerToys with Visual Studio Code
This guide shows how to build, debug, and contribute to PowerToys using VS Code instead of (or alongside) full Visual Studio. It focuses on common innerloop tasks for C++, .NET, and mixed scenarios present in the solution.
> PowerToys is a large mixed C++ / C# / WinAppSDK solution. VS Code works well for incremental development and quick module iterations, but occasionally you may still prefer full Visual Studio for designer tooling or specialized diagnostics.
---
VS Code extensions Needed:
| Area | Extension | Notes |
|------|-----------|-------|
| C++ | ms-vscode.cpptools | IntelliSense, debugging (cppvsdbg) |
| C# | ms-dotnettools.csdevkit (or C#) | Language service / test explorer |
---
## Building in VS Code
### Configure developer powershell for vs2022 for more convenient dev in vscode.
1. Configure profile in in settings, entry: "terminal.integrated.profiles.windows"
2. Add below config as entry:
```json
"Developer PowerShell for VS 2022": {
// Configure based on your preference
"path": "C:\\Program Files\\WindowsApps\\Microsoft.PowerShell_7.5.2.0_arm64__8wekyb3d8bbwe\\pwsh.exe",
"args": [
"-NoExit",
"-Command",
"& {",
"$orig = Get-Location;",
// Configure based on your environment
"& 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\Tools\\Launch-VsDevShell.ps1';",
"Set-Location $orig",
"}"
]
},
```
3. [Optional] Set Developer PowerShell for VS 2022 as your default profile, so that you can get a deep integration with vscode coding agent.
4. Now You can build with plain `msbuild` or configure tasks.json in below section
Or reach out to "tools\build\BUILD-GUIDELINES.md"
### Sample plain msbuild command
```powershell
# Restore:
msbuild powertoys.sln -t:restore -p:configuration=debug -p:platform=x64 -m
# Build powertoys sln
msbuild powertoys.sln -p:configuration=debug -p:platform=x64 -m
# dotnet project
msbuild src\settings-ui\Settings.UI\PowerToys.Settings.csproj -p:Platform=x64 -p:Configuration=Debug -m
# native project
msbuild "src\modules\MouseUtils\FindMyMouse\FindMyMouse.vcxproj" -p:Configuration=Debug -p:Platform=x64 -m
```
---
## Debugging
### Existing launch configuration
The repo provides `.vscode/launch.json` with:
- `Run PowerToys.exe (no build)`: Launches the already-built executable at `x64/Debug/PowerToys.exe` using `cppvsdbg`.
Build first, then press F5. To switch configuration (Release / ARM64) either edit the path or create additional launch entries.
### Attaching to a running instance
If PowerToys is already running, you can attach to that process:
2. VS Code command palette: “C/C++: (Windows) Attach to Process”.
3. Filter for `PowerToys.exe` / module-specific processes.
### Debugging managed components
Many modules have a managed component loaded into the PowerToys process. `cppvsdbg` can debug mixed mode, but if you need richer .NET inspection you can create a second configuration using `type: coreclr` and `processId` attachment after the native launch, or just attach separately:
Similar for attach to managed code.
> Note: In arm64 machine, can only debug arm64 code.
```jsonc
{
"version": "0.2.0",
"configurations": [
{
"name": "Run native executable (no build)",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}\\x64\\Debug\\PowerToys.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"console": "integratedTerminal"
},
{
"name": "C/C++ Attach to PowerToys Process (native)",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}",
"symbolSearchPath": "${workspaceFolder}\\x64\\Debug;${workspaceFolder}\\Debug;${workspaceFolder}\\symbols"
},
{
"name": "Run managed code (managed, no build)",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}\\arm64\\Debug\\WinUI3Apps\\PowerToys.Settings.exe",
"args": [],
"cwd": "${workspaceFolder}",
"env": {},
"console": "internalConsole",
"stopAtEntry": false
}
]
}
```
---
## 6. Common tasks & tips
| Task | Command / Action | Notes |
|------|------------------|-------|
| Clean | `git clean -xdf` (careful) or `msbuild /t:Clean PowerToys.sln` | Deep clean removes packages & build outputs |
| Rebuild single project | `msbuild path\to\proj.vcxproj /t:Rebuild -p:Platform=x64 -p:Configuration=Debug` | Faster than whole solution |
| Generate installer (rare in inner loop) | See `tools\build\build-installer.ps1` | Usually not needed for local debug |
| Resource conversion errors | Re-run restore + build | Triggers custom PowerShell targets |

View File

@@ -76,7 +76,6 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and an
1. Windows 10 April 2018 Update (version 1803) or newer
1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer
1. A local clone of the PowerToys repository
1. Enable long paths in Windows (see [Enable Long Paths](https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enabling-long-paths-in-windows-10-version-1607-and-later) for details)
### Install Visual Studio dependencies

View File

@@ -73,5 +73,4 @@ Below are community created plugins that target a website or software. They are
| [YubicoOauthOTP](https://github.com/dlnilsson/Community.PowerToys.Run.Plugin.YubicoOauthOTP) | [dlnilsson](https://github.com/dlnilsson) | Display generated codes from OATH accounts stored on the YubiKey in powerToys Run |
| [Firefox Bookmark](https://github.com/8LWXpg/PowerToysRun-FirefoxBookmark) | [8LWXpg](https://github.com/8LWXpg) | Open bookmarks in Firefox based browser |
| [Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run |
| [PerplexitySearchShortcut](https://github.com/0x6f677548/PowerToys-Run-PerplexitySearchShortcut) | [0x6f677548](https://github.com/0x6f677548) | Search Perplexity |
| [SpeedTest](https://github.com/ruslanlap/PowerToysRun-SpeedTest) | [ruslanlap](https://github.com/ruslanlap) | One-command internet speed tests with real-time results, modern UI, and shareable links. |

View File

@@ -9,6 +9,13 @@
<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
<?if $(sys.BUILDARCH) = x64 ?>
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
<?else ?>
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
<?endif ?>
</Directory>
</Directory>
</DirectoryRef>
@@ -26,14 +33,41 @@
</Component>
</DirectoryRef>
<?if $(sys.BUILDARCH) = x64 ?>
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
<Component Id="Module_CmdPal_Deps" Win64="yes" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?else ?>
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
<Component Id="Module_CmdPal_Deps" Win64="yes" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?endif ?>
<ComponentGroup Id="CmdPalComponentGroup">
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes"/>
</RegistryKey>
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall"/>
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall"/>
<?if $(sys.BUILDARCH) = x64 ?>
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall"/>
<?else ?>
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall"/>
<?endif ?>
</Component>
<ComponentRef Id="Module_CmdPal" />
<ComponentRef Id="Module_CmdPal_Deps" />
</ComponentGroup>
</Fragment>

View File

@@ -4,6 +4,13 @@
<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Directory Id="CmdPalInstallFolder" Name="CmdPal">
<Directory Id="CmdPalDepsInstallFolder" Name="Dependencies">
<?if $(sys.BUILDARCH) = x64 ?>
<Directory Id="CmdPalDepsX64InstallFolder" Name="x64" />
<?else?>
<Directory Id="CmdPalDepsArm64InstallFolder" Name="arm64" />
<?endif?>
</Directory>
</Directory>
</DirectoryRef>
<DirectoryRef Id="CmdPalInstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test">
@@ -18,14 +25,40 @@
<?endif?>
</Component>
</DirectoryRef>
<?if $(sys.BUILDARCH) = x64 ?>
<DirectoryRef Id="CmdPalDepsX64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64">
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.VCLibs.x64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?else?>
<DirectoryRef Id="CmdPalDepsArm64InstallFolder" FileSource="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64">
<Component Id="Module_CmdPal_Deps" Guid="C2790FC4-0665-4462-947A-D942A2AABFF0" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="Microsoft.VCLibs.ARM64.14.00.Desktop.appx" Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion)_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
</Component>
</DirectoryRef>
<?endif?>
<ComponentGroup Id="CmdPalComponentGroup">
<Component Id="RemoveCmdPalFolder" Guid="2DF90C08-CC75-4245-A14E-B82904636C53" Directory="INSTALLFOLDER">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveCmdPalFolder" Value="" KeyPath="yes" />
</RegistryKey>
<RemoveFolder Id="RemoveCmdPalInstallDirFolder" Directory="CmdPalInstallFolder" On="uninstall" />
<RemoveFolder Id="RemoveCmdPalDepsInstallDirFolder" Directory="CmdPalDepsInstallFolder" On="uninstall" />
<?if $(sys.BUILDARCH) = x64 ?>
<RemoveFolder Id="RemoveCmdPalDepsX64InstallDirFolder" Directory="CmdPalDepsX64InstallFolder" On="uninstall" />
<?else?>
<RemoveFolder Id="RemoveCmdPalDepsArm64InstallDirFolder" Directory="CmdPalDepsArm64InstallFolder" On="uninstall" />
<?endif?>
</Component>
<ComponentRef Id="Module_CmdPal" />
<ComponentRef Id="Module_CmdPal_Deps" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -33,7 +33,6 @@ namespace Microsoft.PowerToys.UITest
Workspaces,
PowerRename,
CommandPalette,
ScreenRuler,
}
/// <summary>
@@ -105,7 +104,6 @@ namespace Microsoft.PowerToys.UITest
[PowerToysModule.Workspaces] = new ModuleInfo("PowerToys.WorkspacesEditor.exe", "Workspaces Editor"),
[PowerToysModule.PowerRename] = new ModuleInfo("PowerToys.PowerRename.exe", "PowerRename", "WinUI3Apps"),
[PowerToysModule.CommandPalette] = new ModuleInfo("Microsoft.CmdPal.UI.exe", "PowerToys Command Palette", "WinUI3Apps\\CmdPal"),
[PowerToysModule.ScreenRuler] = new ModuleInfo("PowerToys.MeasureToolUI.exe", "PowerToys.ScreenRuler", "WinUI3Apps"),
};
}

View File

@@ -4,7 +4,6 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -95,7 +94,6 @@ namespace Microsoft.PowerToys.UITest
{
Task.Delay(1000).Wait();
AddScreenShotsToTestResultsDirectory();
AddLogFilesToTestResultsDirectory();
}
}
@@ -600,92 +598,6 @@ namespace Microsoft.PowerToys.UITest
}
}
/// <summary>
/// Copies PowerToys log files to test results directory when test fails.
/// Renames files to include the directory structure after \PowerToys.
/// </summary>
protected void AddLogFilesToTestResultsDirectory()
{
try
{
var localAppDataLow = Path.Combine(
Environment.GetEnvironmentVariable("USERPROFILE") ?? string.Empty,
"AppData",
"LocalLow",
"Microsoft",
"PowerToys");
if (Directory.Exists(localAppDataLow))
{
CopyLogFilesFromDirectory(localAppDataLow, string.Empty);
}
var localAppData = Path.Combine(
Environment.GetEnvironmentVariable("LOCALAPPDATA") ?? string.Empty,
"Microsoft",
"PowerToys");
if (Directory.Exists(localAppData))
{
CopyLogFilesFromDirectory(localAppData, string.Empty);
}
}
catch (Exception ex)
{
// Don't fail the test if log file copying fails
Console.WriteLine($"Failed to copy log files: {ex.Message}");
}
}
/// <summary>
/// Recursively copies log files from a directory and renames them with directory structure.
/// </summary>
/// <param name="sourceDir">Source directory to copy from</param>
/// <param name="relativePath">Relative path from PowerToys folder</param>
private void CopyLogFilesFromDirectory(string sourceDir, string relativePath)
{
if (!Directory.Exists(sourceDir))
{
return;
}
// Process log files in current directory
var logFiles = Directory.GetFiles(sourceDir, "*.log");
foreach (var logFile in logFiles)
{
try
{
var fileName = Path.GetFileName(logFile);
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
var extension = Path.GetExtension(fileName);
// Create new filename with directory structure
var directoryPart = string.IsNullOrEmpty(relativePath) ? string.Empty : relativePath.Replace("\\", "-") + "-";
var newFileName = $"{directoryPart}{fileNameWithoutExt}{extension}";
// Copy file to test results directory with new name
var testResultsDir = TestContext.TestResultsDirectory ?? Path.GetTempPath();
var destinationPath = Path.Combine(testResultsDir, newFileName);
File.Copy(logFile, destinationPath, true);
TestContext.AddResultFile(destinationPath);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to copy log file {logFile}: {ex.Message}");
}
}
// Recursively process subdirectories
var subdirectories = Directory.GetDirectories(sourceDir);
foreach (var subdir in subdirectories)
{
var dirName = Path.GetFileName(subdir);
var newRelativePath = string.IsNullOrEmpty(relativePath) ? dirName : Path.Combine(relativePath, dirName);
CopyLogFilesFromDirectory(subdir, newRelativePath);
}
}
/// <summary>
/// Restart scope exe.
/// </summary>

View File

@@ -1,16 +0,0 @@
---
applyTo: "**/*.cs,**/*.cpp,**/*.c,**/*.h,**/*.hpp"
---
# Common shared libraries guidance (concise)
Scope
- Logging, IPC, settings, DPI, telemetry, utilities consumed by multiple modules.
Guidelines
- Avoid breaking public headers/APIs; if changed, search & update all callers.
- Coordinate ABI-impacting struct/class layout changes; keep binary compatibility.
- Watch perf in hot paths (hooks, timers, serialization); avoid avoidable allocations.
- Ask before adding thirdparty deps or changing serialization formats.
Acceptance
- No unintended ABI breaks, no noisy logs, new non-obvious symbols briefly commented.

View File

@@ -31,11 +31,11 @@
<!-- The following sections assume that the machine we're building on is always x64. That means we won't be able to run/inspect arm64 executables, therefore we must always execute x64 generator. -->
<Target Name="PostBuildAction" AfterTargets="Build" Outputs="$(GeneratedDSCModule)" Condition="'$(Platform)'!='ARM64'">
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;..\..\..\x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule) $(GeneratedDSCManifest)" />
<Exec Command="&quot;$(OutDir)$(AssemblyName).exe&quot; &quot;$(SolutionDir)x64\$(Configuration)\WinUI3Apps\PowerToys.Settings.UI.Lib.dll&quot; $(GeneratedDSCModule) $(GeneratedDSCManifest)" />
</Target>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(Platform)'=='ARM64'">
<Exec Command="&quot;$(MSBuildToolsPath)\msbuild.exe&quot; PowerToys.sln -p:Configuration=&quot;$(Configuration)&quot; -p:Platform=&quot;x64&quot; -verbosity:m -t:DSC\PowerToys_Settings_DSC_Schema_Generator" WorkingDirectory="..\..\..\" />
<Exec Command="&quot;$(MSBuildToolsPath)\msbuild.exe&quot; PowerToys.sln -p:Configuration=&quot;$(Configuration)&quot; -p:Platform=&quot;x64&quot; -verbosity:m -t:DSC\PowerToys_Settings_DSC_Schema_Generator" WorkingDirectory="$(SolutionDir)" />
</Target>
</Project>

View File

@@ -1,12 +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.8.250907003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
@@ -147,13 +141,7 @@
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -165,19 +153,7 @@
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2903.40\build\native\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.8.250907003\build\native\Microsoft.WindowsAppSDK.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Base.1.8.250831001\build\native\Microsoft.WindowsAppSDK.Base.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Foundation.1.8.250906002\build\native\Microsoft.WindowsAppSDK.Foundation.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.WinUI.1.8.250906003\build\native\Microsoft.WindowsAppSDK.WinUI.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.Runtime.1.8.250907003\build\native\Microsoft.WindowsAppSDK.Runtime.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.DWrite.1.8.25090401\build\Microsoft.WindowsAppSDK.DWrite.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.InteractiveExperiences.1.8.250906004\build\native\Microsoft.WindowsAppSDK.InteractiveExperiences.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.7.250513003\build\native\Microsoft.WindowsAppSDK.targets'))" />
</Target>
</Project>

View File

@@ -31,11 +31,7 @@ struct CommonState
Measurement::Unit units = Measurement::Unit::Pixel;
#pragma warning(push)
#pragma warning(disable : 4324)
alignas(8) POINT cursorPosSystemSpace = {}; // updated atomically
#pragma warning(pop)
POINT cursorPosSystemSpace = {}; // updated atomically
std::atomic_bool closeOnOtherMonitors = false;
float GetPhysicalPx2MmRatio(HWND window) const

View File

@@ -4,14 +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.26100.4188" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
<package id="Microsoft.WindowsAppSDK" version="1.7.250513003" targetFramework="native" />
</packages>

View File

@@ -5,7 +5,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:winuiex="using:WinUIEx"
Title="PowerToys.ScreenRuler"
IsAlwaysOnTop="True"
IsMaximizable="False"
IsMinimizable="False"
@@ -251,7 +250,6 @@
<ToggleButton
Name="btnBounds"
x:Uid="BtnBounds"
AutomationProperties.AutomationId="Button_Bounds"
Click="BoundsTool_Click"
Content="&#xEF20;"
KeyboardAcceleratorPlacementMode="Auto"
@@ -269,7 +267,6 @@
<ToggleButton
Name="btnSpacing"
x:Uid="BtnSpacing"
AutomationProperties.AutomationId="Button_Spacing"
Click="MeasureTool_Click"
Style="{StaticResource ToggleButtonRadioButtonStyle}">
<ToolTipService.ToolTip>
@@ -287,7 +284,6 @@
<ToggleButton
Name="btnHorizontalSpacing"
x:Uid="BtnHorizontalSpacing"
AutomationProperties.AutomationId="Button_SpacingHorizontal"
Click="HorizontalMeasureTool_Click"
Style="{StaticResource ToggleButtonRadioButtonStyle}">
<ToolTipService.ToolTip>
@@ -308,7 +304,6 @@
<ToggleButton
Name="btnVerticalSpacing"
x:Uid="BtnVerticalSpacing"
AutomationProperties.AutomationId="Button_SpacingVertical"
Click="VerticalMeasureTool_Click"
Style="{StaticResource ToggleButtonRadioButtonStyle}">
<ToolTipService.ToolTip>
@@ -329,7 +324,6 @@
<AppBarSeparator />
<Button
x:Uid="BtnClosePanel"
AutomationProperties.AutomationId="Button_Close"
Click="ClosePanelTool_Click"
Content="&#xE8BB;"
Foreground="{StaticResource CloseButtonBackgroundPointerOver}">

View File

@@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity
Name="d4d0f157-5c12-4390-9689-152b0c86a582"
Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="d4d0f157-5c12-4390-9689-152b0c86a582" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>ScreenRuler.UITests</DisplayName>
<PublisherDisplayName>Microsoft</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="ScreenRuler.UITests"
Description="ScreenRuler.UITests"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

View File

@@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<RootNamespace>PowerToys.ScreenRuler.UITests</RootNamespace>
<AssemblyName>ScreenRuler.UITests</AssemblyName>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\ScreenRuler.UITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<ProjectReference Include="..\..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenRuler.UITests
{
[TestClass]
public class TestBounds : UITestBase
{
public TestBounds()
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
{
}
[TestMethod("ScreenRuler.BoundsTool")]
[TestCategory("Spacing")]
public void TestScreenRulerBoundsTool()
{
TestHelper.InitializeTest(this, "bounds test");
TestHelper.PerformBoundsToolTest(this);
TestHelper.CleanupTest(this);
}
}
}

View File

@@ -1,466 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenRuler.UITests
{
public static class TestHelper
{
private static readonly string[] ShortcutSeparators = { " + ", "+", " " };
// Button automation names from Resources.resw
public const string BoundsButtonId = "Button_Bounds";
public const string SpacingButtonName = "Button_Spacing";
public const string HorizontalSpacingButtonName = "Button_SpacingHorizontal";
public const string VerticalSpacingButtonName = "Button_SpacingVertical";
public const string CloseButtonId = "Button_Close";
/// <summary>
/// Performs common test initialization: navigate to settings, enable toggle, verify shortcut
/// </summary>
/// <param name="testBase">The test base instance</param>
/// <param name="testName">Name of the test for assertions</param>
/// <returns>The activation keys for the test</returns>
public static Key[] InitializeTest(UITestBase testBase, string testName)
{
LaunchFromSetting(testBase);
var toggleSwitch = SetScreenRulerToggle(testBase, enable: true);
Assert.IsTrue(
toggleSwitch.IsOn,
$"Screen Ruler toggle switch should be ON for {testName}");
var activationKeys = ReadActivationShortcut(testBase);
Assert.IsNotNull(activationKeys, "Should be able to read activation shortcut");
Assert.IsTrue(activationKeys.Length > 0, "Activation shortcut should contain at least one key");
return activationKeys;
}
/// <summary>
/// Performs common test cleanup: close ScreenRuler UI
/// </summary>
/// <param name="testBase">The test base instance</param>
public static void CleanupTest(UITestBase testBase)
{
CloseScreenRulerUI(testBase);
// Ensure we're attached to settings after cleanup
try
{
testBase.Session.Attach(PowerToysModule.PowerToysSettings);
}
catch
{
// Ignore attachment errors - this is just cleanup
}
}
/// <summary>
/// Navigate to the Screen Ruler (Measure Tool) settings page
/// </summary>
public static void LaunchFromSetting(UITestBase testBase)
{
var screenRulers = testBase.Session.FindAll<NavigationViewItem>(By.AccessibilityId("ScreenRulerNavItem"));
if (screenRulers.Count == 0)
{
testBase.Session.Find<NavigationViewItem>(By.AccessibilityId("SystemToolsNavItem"), 5000).Click(msPostAction: 500);
}
testBase.Session.Find<NavigationViewItem>(By.AccessibilityId("ScreenRulerNavItem"), 5000).Click(msPostAction: 500);
}
/// <summary>
/// Set the Screen Ruler toggle switch to the specified state
/// </summary>
public static ToggleSwitch SetScreenRulerToggle(UITestBase testBase, bool enable)
{
var toggleSwitch = testBase.Session.Find<ToggleSwitch>(By.AccessibilityId("Toggle_ScreenRuler"), 5000);
if (toggleSwitch.IsOn != enable)
{
toggleSwitch.Click(msPreAction: 1000, msPostAction: 2000);
}
if (toggleSwitch.IsOn != enable)
{
testBase.Session.SendKey(Key.Space, msPreAction: 0, msPostAction: 2000);
}
return toggleSwitch;
}
/// <summary>
/// Set the Screen Ruler toggle and verify its state
/// </summary>
/// <param name="testBase">The test base instance</param>
/// <param name="enable">True to enable, false to disable</param>
/// <param name="testName">Name of the test for assertion messages</param>
public static void SetAndVerifyScreenRulerToggle(UITestBase testBase, bool enable, string testName)
{
var toggleSwitch = SetScreenRulerToggle(testBase, enable);
Assert.AreEqual(
enable,
toggleSwitch.IsOn,
$"Screen Ruler toggle switch should be {(enable ? "ON" : "OFF")} for {testName}");
}
/// <summary>
/// Read the current activation shortcut from the ShortcutControl
/// </summary>
public static Key[] ReadActivationShortcut(UITestBase testBase)
{
var shortcutCard = testBase.Session.Find<Element>(By.AccessibilityId("Shortcut_ScreenRuler"), 5000);
var shortcutButton = shortcutCard.Find<Element>(By.AccessibilityId("EditButton"), 5000);
return ParseShortcutText(shortcutButton.HelpText);
}
/// <summary>
/// Parse shortcut text like "Win + Ctrl + Shift + M" into Key array
/// </summary>
private static Key[] ParseShortcutText(string shortcutText)
{
if (string.IsNullOrEmpty(shortcutText))
{
return new Key[] { Key.Win, Key.Ctrl, Key.Shift, Key.M };
}
var keys = new List<Key>();
var parts = shortcutText.Split(ShortcutSeparators, StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
var cleanPart = part.Trim().ToLowerInvariant();
var key = cleanPart switch
{
"win" or "windows" => Key.Win,
"ctrl" or "control" => Key.Ctrl,
"shift" => Key.Shift,
"alt" => Key.Alt,
_ when cleanPart.Length == 1 && char.IsLetter(cleanPart[0]) &&
cleanPart[0] >= 'a' && cleanPart[0] <= 'z' =>
(Key)Enum.Parse(typeof(Key), cleanPart.ToUpperInvariant()),
_ => (Key?)null,
};
if (key.HasValue)
{
keys.Add(key.Value);
}
}
return keys.Count > 0 ? keys.ToArray() : new Key[] { Key.Win, Key.Ctrl, Key.Shift, Key.M };
}
/// <summary>
/// Check if ScreenRulerUI window is open
/// </summary>
public static bool IsScreenRulerUIOpen(UITestBase testBase) => testBase.IsWindowOpen("PowerToys.ScreenRuler");
/// <summary>
/// Wait for ScreenRulerUI to reach the specified state within the timeout
/// </summary>
public static bool WaitForScreenRulerUIState(UITestBase testBase, bool shouldBeOpen, int timeoutMs = 5000, int pollingIntervalMs = 100)
{
var endTime = DateTime.Now.AddMilliseconds(timeoutMs);
while (DateTime.Now < endTime)
{
if (IsScreenRulerUIOpen(testBase) == shouldBeOpen)
{
return true;
}
Task.Delay(pollingIntervalMs).Wait();
}
return false;
}
/// <summary>
/// Wait for ScreenRulerUI to appear within the specified timeout
/// </summary>
public static bool WaitForScreenRulerUI(UITestBase testBase, int timeoutMs = 5000) =>
WaitForScreenRulerUIState(testBase, shouldBeOpen: true, timeoutMs);
/// <summary>
/// Wait for ScreenRulerUI to disappear within the specified timeout
/// </summary>
public static bool WaitForScreenRulerUIToDisappear(UITestBase testBase, int timeoutMs = 5000) =>
WaitForScreenRulerUIState(testBase, shouldBeOpen: false, timeoutMs);
/// <summary>
/// Close ScreenRulerUI if it's open
/// </summary>
public static void CloseScreenRulerUI(UITestBase testBase)
{
if (IsScreenRulerUIOpen(testBase))
{
try
{
// Attach to ScreenRuler window before trying to find and click close button
testBase.Session.Attach(PowerToysModule.ScreenRuler);
var closeButton = testBase.Session.Find<Element>(By.AccessibilityId(CloseButtonId), 15000, true);
closeButton?.Click();
}
catch
{
// If we can't find the close button, ignore - the window might have closed already
}
finally
{
// Attach back to settings after closing
try
{
testBase.Session.Attach(PowerToysModule.PowerToysSettings);
}
catch
{
// Ignore attachment errors
}
}
}
}
/// <summary>
/// Get a specific ScreenRulerUI button by its automation name
/// </summary>
public static Element? GetScreenRulerButton(UITestBase testBase, string buttonName, int timeoutMs = 1000)
{
return testBase.Session.Find<Element>(By.AccessibilityId(buttonName), timeoutMs, true);
/*
try
{
// Attach to ScreenRuler window before trying to find buttons
testBase.Session.Attach(PowerToysModule.ScreenRuler);
return testBase.Session.Find<Element>(By.AccessibilityId(buttonName), timeoutMs, true);
}
catch
{
return null;
}
finally
{
// Attach back to settings if needed for further operations
// This ensures we don't break the test flow
try
{
testBase.Session.Attach(PowerToysModule.PowerToysSettings);
}
catch
{
// Ignore attachment errors - the calling code will handle as needed
}
}
*/
}
/// <summary>
/// Clear the clipboard content using STA thread
/// </summary>
public static void ClearClipboard()
{
ExecuteInSTAThread(() => System.Windows.Forms.Clipboard.Clear());
}
/// <summary>
/// Get text content from clipboard using STA thread
/// </summary>
public static string GetClipboardText()
{
string result = string.Empty;
ExecuteInSTAThread(() =>
{
if (System.Windows.Forms.Clipboard.ContainsText())
{
result = System.Windows.Forms.Clipboard.GetText();
}
});
return result ?? string.Empty;
}
/// <summary>
/// Execute an action in an STA thread with error handling
/// </summary>
private static void ExecuteInSTAThread(Action action)
{
try
{
var staThread = new Thread(() =>
{
try
{
action();
}
catch
{
// Ignore clipboard errors
}
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join(TimeSpan.FromSeconds(5));
}
catch
{
// Ignore clipboard errors
}
}
/// <summary>
/// Validate clipboard content contains valid spacing measurement for the specified type
/// </summary>
public static bool ValidateSpacingClipboardContent(string clipboardText, string spacingType)
{
if (string.IsNullOrEmpty(clipboardText))
{
return false;
}
return spacingType switch
{
"Spacing" => Regex.IsMatch(clipboardText, @"\d+\s*[<5B>x×]\s*\d+"),
"Horizontal Spacing" or "Vertical Spacing" => Regex.IsMatch(clipboardText, @"^\d+$"),
_ => false,
};
}
/// <summary>
/// Perform a complete spacing tool test operation
/// </summary>
public static void PerformSpacingToolTest(UITestBase testBase, string buttonId, string testName)
{
ClearClipboard();
// Launch ScreenRuler UI
var activationKeys = ReadActivationShortcut(testBase);
testBase.SendKeys(activationKeys);
Assert.IsTrue(
WaitForScreenRulerUI(testBase, 2000),
$"ScreenRulerUI should appear after pressing activation shortcut for {testName}: {string.Join(" + ", activationKeys)}");
// Attach to ScreenRuler window and click spacing button
// testBase.Session.Attach(PowerToysModule.ScreenRuler);
var spacingButton = testBase.Session.Find<Element>(By.AccessibilityId(buttonId), 15000, true);
Assert.IsNotNull(spacingButton, $"{testName} button should be found");
spacingButton!.Click();
Task.Delay(500).Wait();
// Perform measurement action (stay attached to ScreenRuler for this)
PerformMeasurementAction(testBase);
// Validate results
ValidateClipboardResults(testName);
// Cleanup - this will handle session attachment properly
CloseScreenRulerUI(testBase);
Assert.IsTrue(
WaitForScreenRulerUIToDisappear(testBase, 2000),
$"{testName}: ScreenRulerUI should close after calling CloseScreenRulerUI");
}
/// <summary>
/// Perform a bounds tool test operation
/// </summary>
public static void PerformBoundsToolTest(UITestBase testBase)
{
ClearClipboard();
var activationKeys = ReadActivationShortcut(testBase);
testBase.SendKeys(activationKeys);
Assert.IsTrue(
WaitForScreenRulerUI(testBase, 2000),
$"ScreenRulerUI should appear after pressing activation shortcut: {string.Join(" + ", activationKeys)}");
// Attach to ScreenRuler window and click bounds button
// testBase.Session.Attach(PowerToysModule.ScreenRuler);
var boundsButton = testBase.Session.Find<Element>(By.AccessibilityId(BoundsButtonId), 15000, true);
Assert.IsNotNull(boundsButton, "Bounds button should be found");
boundsButton.Click();
Task.Delay(500).Wait();
// Perform drag operation to create 100x100 box (stay attached to ScreenRuler)
var currentPos = testBase.GetMousePosition();
int startX = currentPos.Item1;
int startY = currentPos.Item2 + 200;
testBase.MoveMouseTo(startX, startY);
Task.Delay(200).Wait();
// Drag operation
testBase.Session.PerformMouseAction(MouseActionType.LeftDown);
Task.Delay(100).Wait();
testBase.MoveMouseTo(startX + 99, startY + 99);
Task.Delay(200).Wait();
testBase.Session.PerformMouseAction(MouseActionType.LeftUp);
Task.Delay(500).Wait();
// Dismiss selection
testBase.Session.PerformMouseAction(MouseActionType.RightClick);
Task.Delay(500).Wait();
// Validate results
string clipboardText = GetClipboardText();
Assert.IsFalse(string.IsNullOrEmpty(clipboardText), "Clipboard should contain measurement data");
Assert.IsTrue(
clipboardText.Contains("100 × 100") || clipboardText.Contains("100 x 100"),
$"Clipboard should contain '100 x 100', but contained: '{clipboardText}'");
// Cleanup - this will handle session attachment properly
CloseScreenRulerUI(testBase);
Assert.IsTrue(
WaitForScreenRulerUIToDisappear(testBase, 2000),
"ScreenRulerUI should close after calling CloseScreenRulerUI");
}
/// <summary>
/// Perform a measurement action (move mouse and click)
/// </summary>
private static void PerformMeasurementAction(UITestBase testBase)
{
var currentPos = testBase.GetMousePosition();
int startX = currentPos.Item1;
int startY = currentPos.Item2 + 200;
testBase.MoveMouseTo(startX, startY);
Task.Delay(200).Wait();
testBase.Session.PerformMouseAction(MouseActionType.LeftClick);
Task.Delay(500).Wait();
testBase.Session.PerformMouseAction(MouseActionType.RightClick);
Task.Delay(500).Wait();
}
/// <summary>
/// Validate clipboard results for spacing tests
/// </summary>
private static void ValidateClipboardResults(string testName)
{
string clipboardText = GetClipboardText();
Assert.IsFalse(string.IsNullOrEmpty(clipboardText), $"{testName}: Clipboard should contain measurement data");
bool containsValidPattern = ValidateSpacingClipboardContent(clipboardText, testName);
Assert.IsTrue(
containsValidPattern,
$"{testName}: Clipboard should contain valid spacing measurement, but contained: '{clipboardText}'");
}
}
}

View File

@@ -1,72 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Threading.Tasks;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenRuler.UITests
{
[TestClass]
public class TestShortcutActivation : UITestBase
{
public TestShortcutActivation()
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
{
}
[TestMethod("ScreenRuler.ShortcutActivation")]
[TestCategory("Activation")]
public void TestScreenRulerShortcutActivation()
{
var activationKeys = TestHelper.InitializeTest(this, "activation test");
// Test 1: Press the activation shortcut and verify the toolbar appears
SendKeys(activationKeys);
bool screenRulerAppeared = TestHelper.WaitForScreenRulerUI(this, 1000);
Assert.IsTrue(
screenRulerAppeared,
$"ScreenRulerUI should appear after pressing activation shortcut: {string.Join(" + ", activationKeys)}");
// Test 2: Press the activation shortcut again and verify the toolbar disappears
SendKeys(activationKeys);
bool screenRulerDisappeared = TestHelper.WaitForScreenRulerUIToDisappear(this, 1000);
Assert.IsTrue(
screenRulerDisappeared,
$"ScreenRulerUI should disappear after pressing activation shortcut again: {string.Join(" + ", activationKeys)}");
// Test 3: Disable Screen Ruler and verify that the activation shortcut no longer activates the utility
// Ensure we're attached to settings UI before toggling
Session.Attach(PowerToysModule.PowerToysSettings);
TestHelper.SetAndVerifyScreenRulerToggle(this, enable: false, "disabled state test");
// Try to activate with shortcut while disabled
SendKeys(activationKeys);
Task.Delay(1000).Wait();
Assert.IsFalse(
TestHelper.IsScreenRulerUIOpen(this),
"ScreenRulerUI should not appear when Screen Ruler is disabled");
// Test 4: Enable Screen Ruler and press the activation shortcut and verify the toolbar appears
// Ensure we're attached to settings UI before toggling
Session.Attach(PowerToysModule.PowerToysSettings);
TestHelper.SetAndVerifyScreenRulerToggle(this, enable: true, "re-enabled state test");
SendKeys(activationKeys);
screenRulerAppeared = TestHelper.WaitForScreenRulerUI(this, 1000);
Assert.IsTrue(
screenRulerAppeared,
$"ScreenRulerUI should appear after re-enabling and pressing activation shortcut: {string.Join(" + ", activationKeys)}");
// Test 5: Verify the utility can be closed via the cleanup method
TestHelper.CloseScreenRulerUI(this);
bool screenRulerClosed = TestHelper.WaitForScreenRulerUIToDisappear(this, 1000);
Assert.IsTrue(
screenRulerClosed,
"ScreenRulerUI should close after calling CloseScreenRulerUI");
TestHelper.CleanupTest(this);
}
}
}

View File

@@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenRuler.UITests
{
[TestClass]
public class TestSpacing : UITestBase
{
public TestSpacing()
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
{
}
[TestMethod("ScreenRuler.SpacingTool")]
[TestCategory("Spacing")]
public void TestScreenRulerSpacingTool()
{
TestHelper.InitializeTest(this, "spacing test");
TestHelper.PerformSpacingToolTest(this, TestHelper.SpacingButtonName, "Spacing");
TestHelper.CleanupTest(this);
}
}
}

View File

@@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenRuler.UITests
{
[TestClass]
public class TestSpacingHorizontal : UITestBase
{
public TestSpacingHorizontal()
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
{
}
[TestMethod("ScreenRuler.HorizontalSpacingTool")]
[TestCategory("Spacing")]
public void TestScreenRulerHorizontalSpacingTool()
{
TestHelper.InitializeTest(this, "horizontal spacing test");
TestHelper.PerformSpacingToolTest(this, TestHelper.HorizontalSpacingButtonName, "Horizontal Spacing");
TestHelper.CleanupTest(this);
}
}
}

View File

@@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenRuler.UITests
{
[TestClass]
public class TestSpacingVertical : UITestBase
{
public TestSpacingVertical()
: base(PowerToysModule.PowerToysSettings, WindowSize.Large)
{
}
[TestMethod("ScreenRuler.VerticalSpacingTool")]
[TestCategory("Spacing")]
public void TestScreenRulerVerticalSpacingTool()
{
TestHelper.InitializeTest(this, "vertical spacing test");
TestHelper.PerformSpacingToolTest(this, TestHelper.VerticalSpacingButtonName, "Vertical Spacing");
TestHelper.CleanupTest(this);
}
}
}

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="ScreenRuler.UITests.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@@ -33,7 +33,6 @@ namespace
const wchar_t JSON_KEY_AUTO_ACTIVATE[] = L"auto_activate";
const wchar_t JSON_KEY_GLIDE_TRAVEL_SPEED[] = L"gliding_travel_speed";
const wchar_t JSON_KEY_GLIDE_DELAY_SPEED[] = L"gliding_delay_speed";
const wchar_t JSON_KEY_GLIDING_CURSOR_ENABLED[] = L"gliding_cursor_enabled";
}
extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -107,9 +106,6 @@ private:
// Gliding cursor state machine
std::atomic<int> m_glideState{ 0 }; // 0..4 like the AHK script
// Gliding cursor enabled state
bool m_glidingCursorEnabled = true;
// Timer configuration: 10ms tick, speeds are defined per 200ms base window
static constexpr int kTimerTickMs = 10;
static constexpr int kBaseSpeedTickMs = 200; // mapping period for configured pixel counts
@@ -391,11 +387,6 @@ private:
void HandleGlidingHotkey()
{
if (!m_glidingCursorEnabled)
{
return;
}
auto s = m_state;
if (!s)
{
@@ -743,17 +734,6 @@ private:
m_state->slowVSpeed = 5;
}
}
try
{
// Parse Gliding Cursor Enabled
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_GLIDING_CURSOR_ENABLED);
m_glidingCursorEnabled = jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE);
}
catch (...)
{
Logger::info("Failed to initialize gliding cursor enabled from settings. Using default true.");
m_glidingCursorEnabled = true;
}
}
else
{

View File

@@ -49,23 +49,23 @@ namespace MouseUtils.UITests
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseExcludedApps));
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
@@ -115,23 +115,23 @@ namespace MouseUtils.UITests
settings.BackgroundColor = "FF0000";
settings.SpotlightColor = "0000FF";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseExcludedApps));
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
@@ -170,27 +170,27 @@ namespace MouseUtils.UITests
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom);
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find<Group>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseExcludedApps));
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
@@ -212,14 +212,14 @@ namespace MouseUtils.UITests
VerifySpotlightAppears(ref settings);
// [Test Case] Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
Task.Delay(1000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightDisappears(ref settings);
// [Test Case] Press Left Ctrl twice and verify the overlay appears
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
Task.Delay(2000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
@@ -240,27 +240,27 @@ namespace MouseUtils.UITests
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom = this.Find<Custom>("Find My Mouse");
Assert.IsNotNull(foundCustom);
if (CheckAnimationEnable(ref foundCustom))
{
foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
foundCustom = this.Find<Custom>("Find My Mouse");
}
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom);
SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseExcludedApps));
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
@@ -282,14 +282,14 @@ namespace MouseUtils.UITests
VerifySpotlightAppears(ref settings);
// [Test Case] Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
Task.Delay(1000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightDisappears(ref settings);
// [Test Case] Press Left Ctrl twice and verify the overlay appears
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
Task.Delay(2000).Wait();
ActivateSpotlight(ref settings);
VerifySpotlightAppears(ref settings);
@@ -310,17 +310,17 @@ namespace MouseUtils.UITests
settings.AnimationDuration = "0";
settings.BackgroundColor = "000000";
settings.SpotlightColor = "FFFFFF";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom = this.Find<Custom>("Find My Mouse");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
// foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
// foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
SetFindMyMouseActivationMethod(ref foundCustom, "Press Left Control twice");
Assert.IsNotNull(foundCustom, "Find My Mouse group not found.");
// SetFindMyMouseAppearanceBehavior(ref foundCustom, ref settings);
var excludedApps = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseExcludedApps));
var excludedApps = foundCustom.Find<TextBlock>("Excluded apps");
if (excludedApps != null)
{
excludedApps.Click();
@@ -340,7 +340,7 @@ namespace MouseUtils.UITests
// VerifySpotlightSettings(ref settings);
// [Test Case] Disable FindMyMouse. Verify the overlay no longer appears when you press Left Ctrl twice
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
Task.Delay(2000).Wait();
Session.SendKey(Key.LCtrl, 0, 0);
Task.Delay(100).Wait();
@@ -382,6 +382,9 @@ namespace MouseUtils.UITests
var colorBackground = this.GetPixelColorString(location.Item1 + radius + 50, location.Item2 + radius + 50);
Assert.AreEqual("#" + settings.BackgroundColor, colorBackground);
var colorBackground2 = this.GetPixelColorString(location.Item1 + radius + 100, location.Item2 + radius + 100);
Assert.AreEqual("#" + settings.BackgroundColor, colorBackground2);
}
private void ActivateSpotlight(ref FindMyMouseSettings settings)
@@ -424,7 +427,7 @@ namespace MouseUtils.UITests
private void SetFindMyMouseActivationMethod(ref Custom? foundCustom, string method)
{
Assert.IsNotNull(foundCustom);
var groupActivation = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseActivationMethod));
var groupActivation = foundCustom.Find<TextBlock>("Activation method");
if (groupActivation != null)
{
groupActivation.Click();
@@ -453,17 +456,17 @@ namespace MouseUtils.UITests
private void SetFindMyMouseAppearanceBehavior(ref Custom foundCustom, ref FindMyMouseSettings settings)
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseAppearanceBehavior));
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// groupAppearanceBehavior.Click();
if (foundCustom.FindAll(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseOverlayOpacity)).Count == 0)
if (foundCustom.FindAll<Slider>("Overlay opacity (%)").Count == 0)
{
groupAppearanceBehavior.Click();
}
// Set the BackGround color
var backgroundColor = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseBackgroundColor));
var backgroundColor = foundCustom.Find<Group>("Background color");
Assert.IsNotNull(backgroundColor);
var button = backgroundColor.Find<Button>(By.XPath(".//Button"));
@@ -502,7 +505,7 @@ namespace MouseUtils.UITests
button.Click();
// Set the Spotlight color
var spotlightColor = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseSpotlightColor));
var spotlightColor = foundCustom.Find<Group>("Spotlight color");
Assert.IsNotNull(spotlightColor);
var spotlightColorButton = spotlightColor.Find<Button>(By.XPath(".//Button"));
@@ -542,7 +545,7 @@ namespace MouseUtils.UITests
spotlightColorButton.Click(false, 500, 1500);
// Set the overlay opacity to overlayOpacity%
var overlayOpacitySlider = foundCustom.Find<Slider>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseOverlayOpacity));
var overlayOpacitySlider = foundCustom.Find<Slider>("Overlay opacity (%)");
Assert.IsNotNull(overlayOpacitySlider);
Assert.IsNotNull(settings.OverlayOpacity);
int overlayOpacityValue = int.Parse(settings.OverlayOpacity, CultureInfo.InvariantCulture);
@@ -551,7 +554,7 @@ namespace MouseUtils.UITests
Task.Delay(1000).Wait();
// Set the Fade Initial zoom to 0
var spotlightInitialZoomSlider = foundCustom.Find<Slider>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseSpotlightZoom));
var spotlightInitialZoomSlider = foundCustom.Find<Slider>("Spotlight initial zoom");
Assert.IsNotNull(spotlightInitialZoomSlider);
Task.Delay(1000).Wait();
spotlightInitialZoomSlider.QuickSetValue(int.Parse(settings.InitialZoom, CultureInfo.InvariantCulture));
@@ -559,8 +562,7 @@ namespace MouseUtils.UITests
Task.Delay(1000).Wait();
//// Change the edit value
var spotlightRadius = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseSpotlightRadius));
var spotlightRadiusEdit = spotlightRadius.Find<TextBox>(By.AccessibilityId("InputBox"));
var spotlightRadiusEdit = foundCustom.Find<TextBox>("Spotlight radius (px) Minimum5");
Assert.IsNotNull(spotlightRadiusEdit);
Task.Delay(1000).Wait();
spotlightRadiusEdit.SetText(settings.Radius);
@@ -568,12 +570,11 @@ namespace MouseUtils.UITests
Task.Delay(1000).Wait();
// Set the duration to 0 ms
var spotlightAnimationDuration = foundCustom.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseAnimationDuration));
var spotlightAnimationDurationEdit = spotlightAnimationDuration.Find<TextBox>(By.AccessibilityId("InputBox"));
Assert.IsNotNull(spotlightAnimationDurationEdit);
var spotlightAnimationDuration = foundCustom.Find<TextBox>("Animation duration (ms) Minimum0");
Assert.IsNotNull(spotlightAnimationDuration);
Task.Delay(1000).Wait();
spotlightAnimationDurationEdit.SetText(settings.AnimationDuration);
Assert.AreEqual(settings.AnimationDuration, spotlightAnimationDurationEdit.Text);
spotlightAnimationDuration.SetText(settings.AnimationDuration);
Assert.AreEqual(settings.AnimationDuration, spotlightAnimationDuration.Text);
Task.Delay(1000).Wait();
// groupAppearanceBehavior.Click();
@@ -621,19 +622,19 @@ namespace MouseUtils.UITests
this.Session.SetMainWindowSize(WindowSize.Large);
// Goto Hosts File Editor setting page
if (this.FindAll(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Count == 0)
if (this.FindAll<NavigationViewItem>("Mouse utilities", 10000).Count == 0)
{
// Expand Advanced list-group if needed
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.InputOutputNavItem)).Click();
this.Find<NavigationViewItem>("Input / Output").Click();
}
if (reload)
{
this.Find<NavigationViewItem>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.KeyboardManagerNavItem)).Click();
this.Find<NavigationViewItem>("Keyboard Manager").Click();
}
Task.Delay(1000).Wait();
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Click();
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// 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.
@@ -24,10 +24,10 @@ namespace MouseUtils.UITests
public void TestEnableMouseHighlighter()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
@@ -42,11 +42,11 @@ namespace MouseUtils.UITests
settings.FadeDelay = "0";
settings.FadeDuration = "90";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighter));
var foundCustom = this.Find<Custom>("Mouse Highlighter");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(false);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
@@ -54,7 +54,7 @@ namespace MouseUtils.UITests
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
// Change the shortcut key for MouseHighlighter
// [TestCase]Change activation shortcut and test it
@@ -107,7 +107,7 @@ namespace MouseUtils.UITests
VerifyMouseHighlighterNotAppears(ref settings, "rightClick");
// [Test Case] Disable Mouse Highlighter and verify that the module is not activated when you press the activation shortcut.
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(false);
xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1 - 100, xy.Item2);
@@ -119,7 +119,7 @@ namespace MouseUtils.UITests
// [Test Case] With left mouse button pressed, drag the mouse and verify the highlight is dragged with the pointer.
// [Test Case] With right mouse button pressed, drag the mouse and verify the highlight is dragged with the pointer.
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1 - 100, xy.Item2);
@@ -143,10 +143,10 @@ namespace MouseUtils.UITests
public void TestMouseHighlighterDifferentSettings()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
@@ -161,11 +161,11 @@ namespace MouseUtils.UITests
settings.FadeDelay = "0";
settings.FadeDuration = "90";
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighter));
var foundCustom = this.Find<Custom>("Mouse Highlighter");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(false);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
@@ -173,7 +173,7 @@ namespace MouseUtils.UITests
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
Session.PerformMouseAction(MouseActionType.ScrollDown);
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Highlighter").Toggle(true);
// Change the shortcut key for MouseHighlighter
// [TestCase] Test the different settings and verify they apply - Change activation shortcut and test it
@@ -387,7 +387,7 @@ namespace MouseUtils.UITests
private void SetColor(ref Custom foundCustom, string colorName = "Primary button highlight color", string colorValue = "000000", string opacity = "0")
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<Group>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterAppearanceBehavior));
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
if (foundCustom.FindAll<TextBox>("Fade duration (ms) Minimum0").Count == 0)
@@ -439,7 +439,7 @@ namespace MouseUtils.UITests
private void SetMouseHighlighterAppearanceBehavior(ref Custom foundCustom, ref MouseHighlighterSettings settings)
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<Group>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseHighlighterAppearanceBehavior));
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// groupAppearanceBehavior.Click();
@@ -477,7 +477,7 @@ namespace MouseUtils.UITests
}
else
{
Assert.Fail("MouseHighlighter Appearance & behavior group not found.");
Assert.Fail("Appearance & behavior group not found.");
}
}
@@ -485,14 +485,14 @@ namespace MouseUtils.UITests
{
this.Session.SetMainWindowSize(WindowSize.Large);
// Goto Mouse utilities setting page
if (this.FindAll(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Count == 0)
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
// Expand Input / Output list-group if needed
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.InputOutputNavItem)).Click();
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").Click();
}
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Click();
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// 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.
@@ -29,11 +29,11 @@ namespace MouseUtils.UITests
public void TestEnableMouseJump2()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom0.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
@@ -45,10 +45,10 @@ namespace MouseUtils.UITests
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseJump));
var foundCustom = this.Find<Custom>("Mouse Jump");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseJumpToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Jump").Toggle(true);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
@@ -89,7 +89,7 @@ namespace MouseUtils.UITests
Task.Delay(1000).Wait();
// [TestCase] Enable Mouse Jump. Then - Disable Mouse Jump and verify that the module is not activated when you press the activation shortcut.
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseJumpToggle)).Toggle(false);
foundCustom.Find<ToggleSwitch>("Enable Mouse Jump").Toggle(false);
Session.MoveMouseTo(screenCenter.CenterX, screenCenter.CenterY - 300, 500, 1000);
Session.SendKeys(Key.Win, Key.Shift, Key.Z);
Task.Delay(500).Wait();
@@ -108,11 +108,11 @@ namespace MouseUtils.UITests
public void TestEnableMouseJump3()
{
LaunchFromSetting();
var foundCustom0 = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouse));
var foundCustom0 = this.Find<Custom>("Find My Mouse");
if (foundCustom0 != null)
{
foundCustom0.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(true);
foundCustom0.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.FindMyMouseToggle)).Toggle(false);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(true);
foundCustom0.Find<ToggleSwitch>("Enable Find My Mouse").Toggle(false);
}
else
{
@@ -124,10 +124,10 @@ namespace MouseUtils.UITests
Session.PerformMouseAction(MouseActionType.ScrollDown);
}
var foundCustom = this.Find<Custom>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseJump));
var foundCustom = this.Find<Custom>("Mouse Jump");
if (foundCustom != null)
{
foundCustom.Find<ToggleSwitch>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseJumpToggle)).Toggle(true);
foundCustom.Find<ToggleSwitch>("Enable Mouse Jump").Toggle(true);
var xy = Session.GetMousePosition();
Session.MoveMouseTo(xy.Item1, xy.Item2 - 100);
@@ -215,23 +215,23 @@ namespace MouseUtils.UITests
Session.SetMainWindowSize(WindowSize.Large);
Task.Delay(1000).Wait();
// Goto Mouse utilities setting page
if (this.FindAll(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Count == 0)
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
// Expand Input / Output list-group if needed
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.InputOutputNavItem)).Click();
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").ClickCenter();
Task.Delay(2000).Wait();
}
// Goto Mouse utilities setting page
if (this.FindAll(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Count == 0)
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
RestartScopeExe();
Session.SetMainWindowSize(WindowSize.Large);
Task.Delay(1000).Wait();
// Expand Input / Output list-group if needed
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.InputOutputNavItem)).Click();
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").ClickCenter();
Task.Delay(2000).Wait();
}
@@ -243,7 +243,7 @@ namespace MouseUtils.UITests
}
else
{
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Click();
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -249,7 +249,7 @@ namespace MouseUtils.UITests
private void SetColor(ref Custom foundCustom, string colorName, string colorValue = "000000")
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<Group>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MousePointerCrosshairsAppearanceBehavior));
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// Set primary button highlight color
@@ -277,7 +277,7 @@ namespace MouseUtils.UITests
private void SetMousePointerCrosshairsAppearanceBehavior(ref Custom foundCustom, ref MousePointerCrosshairsSettings settings)
{
Assert.IsNotNull(foundCustom);
var groupAppearanceBehavior = foundCustom.Find<Group>(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MousePointerCrosshairsAppearanceBehavior));
var groupAppearanceBehavior = foundCustom.Find<TextBlock>("Appearance & behavior");
if (groupAppearanceBehavior != null)
{
// groupAppearanceBehavior.Click();
@@ -337,7 +337,7 @@ namespace MouseUtils.UITests
}
else
{
Assert.Fail("MousePointerCrosshairs Appearance & behavior group not found.");
Assert.Fail("Appearance & behavior group not found.");
}
}
@@ -371,16 +371,8 @@ namespace MouseUtils.UITests
public Custom? FindMouseUtilElement(MouseUtilsSettings.MouseUtils element)
{
string accessibilityId = element switch
{
MouseUtilsSettings.MouseUtils.FindMyMouse => MouseUtilsSettings.AccessibilityIds.FindMyMouse,
MouseUtilsSettings.MouseUtils.MouseHighlighter => MouseUtilsSettings.AccessibilityIds.MouseHighlighter,
MouseUtilsSettings.MouseUtils.MousePointerCrosshairs => MouseUtilsSettings.AccessibilityIds.MousePointerCrosshairs,
MouseUtilsSettings.MouseUtils.MouseJump => MouseUtilsSettings.AccessibilityIds.MouseJump,
_ => throw new ArgumentException($"Unknown MouseUtils element: {element}"),
};
var foundCustom = this.Find<Custom>(By.AccessibilityId(accessibilityId));
var elementName = MouseUtilsSettings.GetMouseUtilUIName(element);
var foundCustom = this.Find<Custom>(elementName);
for (int i = 0; i < 20; i++)
{
if (foundCustom != null)
@@ -389,7 +381,7 @@ namespace MouseUtils.UITests
}
Session.PerformMouseAction(MouseActionType.ScrollDown);
foundCustom = this.Find<Custom>(By.AccessibilityId(accessibilityId));
foundCustom = this.Find<Custom>(elementName);
}
return foundCustom;
@@ -399,14 +391,14 @@ namespace MouseUtils.UITests
{
Session.SetMainWindowSize(WindowSize.Large);
// Goto Mouse utilities setting page
if (this.FindAll(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Count == 0)
// Goto Hosts File Editor setting page
if (this.FindAll<NavigationViewItem>("Mouse utilities").Count == 0)
{
// Expand Input / Output list-group if needed
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.InputOutputNavItem)).Click();
// Expand Advanced list-group if needed
this.Find<NavigationViewItem>("Input / Output").Click();
}
this.Find(By.AccessibilityId(MouseUtilsSettings.AccessibilityIds.MouseUtilitiesNavItem)).Click();
this.Find<NavigationViewItem>("Mouse utilities").Click();
}
}
}

View File

@@ -11,48 +11,6 @@ namespace MouseUtils.UITests
{
public class MouseUtilsSettings
{
// Accessibility ID constants
public static class AccessibilityIds
{
// Mouse Utils module IDs
public const string FindMyMouse = "MouseUtils_FindMyMouseTestId";
public const string MouseHighlighter = "MouseUtils_MouseHighlighterTestId";
public const string MousePointerCrosshairs = "MouseUtils_MousePointerCrosshairsTestId";
public const string MouseJump = "MouseUtils_MouseJumpTestId";
// ToggleSwitch IDs
public const string FindMyMouseToggle = "MouseUtils_FindMyMouseToggleId";
public const string MouseHighlighterToggle = "MouseUtils_MouseHighlighterToggleId";
public const string MousePointerCrosshairsToggle = "MouseUtils_MousePointerCrosshairsToggleId";
public const string MouseJumpToggle = "MouseUtils_MouseJumpToggleId";
// Find My Mouse UI Element IDs
public const string FindMyMouseActivationMethod = "MouseUtils_FindMyMouseActivationMethodId";
public const string FindMyMouseAppearanceBehavior = "MouseUtils_FindMyMouseAppearanceBehaviorId";
public const string FindMyMouseExcludedApps = "MouseUtils_FindMyMouseExcludedAppsId";
public const string FindMyMouseBackgroundColor = "MouseUtils_FindMyMouseBackgroundColorId";
public const string FindMyMouseSpotlightColor = "MouseUtils_FindMyMouseSpotlightColorId";
public const string FindMyMouseOverlayOpacity = "MouseUtils_FindMyMouseOverlayOpacityId";
public const string FindMyMouseSpotlightZoom = "MouseUtils_FindMyMouseSpotlightZoomId";
public const string FindMyMouseSpotlightRadius = "MouseUtils_FindMyMouseSpotlightRadiusId";
public const string FindMyMouseAnimationDuration = "MouseUtils_FindMyMouseAnimationDurationId";
// Mouse Highlighter UI Element IDs
public const string MouseHighlighterActivationShortcut = "MouseUtils_MouseHighlighterActivationShortcutId";
public const string MouseHighlighterAppearanceBehavior = "MouseUtils_MouseHighlighterAppearanceBehaviorId";
// Mouse Pointer Crosshairs UI Element IDs
public const string MousePointerCrosshairsAppearanceBehavior = "MouseUtils_MousePointerCrosshairsAppearanceBehaviorId";
// Mouse Jump UI Element IDs
public const string MouseJumpActivationShortcut = "MouseUtils_MouseJumpActivationShortcutId";
// Navigation IDs
public const string InputOutputNavItem = "InputOutputNavItem";
public const string MouseUtilitiesNavItem = "MouseUtilitiesNavItem";
public const string KeyboardManagerNavItem = "KeyboardManagerNavItem";
}
// Mouse Utils Modules
public enum MouseUtils
{

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Threading;
// <summary>
// Initialization and clean up.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
using Microsoft.Win32;
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
using MouseWithoutBorders.Form;
using Windows.UI.Input.Preview.Injection;
using Thread = MouseWithoutBorders.Core.Thread;
namespace MouseWithoutBorders
{
internal partial class Common
{
private static bool initDone;
internal static int REOPEN_WHEN_WSAECONNRESET = -10054;
internal static int REOPEN_WHEN_HOTKEY = -10055;
internal static int PleaseReopenSocket;
internal static bool ReopenSocketDueToReadError;
internal static DateTime LastResumeSuspendTime { get; set; } = DateTime.UtcNow;
internal static bool InitDone
{
get => Common.initDone;
set => Common.initDone = value;
}
internal static void UpdateMachineTimeAndID()
{
Common.MachineName = Common.MachineName.Trim();
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
}
private static void InitializeMachinePoolFromSettings()
{
try
{
MachineInf[] info = MachinePoolHelpers.LoadMachineInfoFromMachinePoolStringSetting(Setting.Values.MachinePoolString);
for (int i = 0; i < info.Length; i++)
{
info[i].Name = info[i].Name.Trim();
}
MachineStuff.MachinePool.Initialize(info);
MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true);
}
catch (Exception ex)
{
Logger.Log(ex);
MachineStuff.MachinePool.Clear();
}
}
internal static void SetupMachineNameAndID()
{
try
{
GetMachineName();
DesMachineID = MachineStuff.NewDesMachineID = MachineID;
// MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test
InitializeMachinePoolFromSettings();
Common.MachineName = Common.MachineName.Trim();
_ = MachineStuff.MachinePool.LearnMachine(Common.MachineName);
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
MachineStuff.UpdateMachinePoolStringSetting();
}
catch (Exception e)
{
Logger.Log(e);
}
}
internal static void Init()
{
_ = Helper.GetUserName();
Common.GeneratedKey = true;
try
{
Common.MyKey = Setting.Values.MyKey;
int tmp = Setting.Values.MyKeyDaysToExpire;
}
catch (FormatException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Logger.Log(e.Message);
}
catch (CryptographicException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Logger.Log(e.Message);
}
try
{
InputSimulation.Injector = InputInjector.TryCreate();
if (InputSimulation.Injector != null)
{
InputSimulation.MoveMouseRelative(0, 0);
NativeMethods.InjectMouseInputAvailable = true;
}
}
catch (EntryPointNotFoundException)
{
NativeMethods.InjectMouseInputAvailable = false;
Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false");
}
bool dummy = Setting.Values.DrawMouseEx;
Is64bitOS = IntPtr.Size == 8;
tcpPort = Setting.Values.TcpPort;
GetScreenConfig();
PackageSent = new PackageMonitor(0);
PackageReceived = new PackageMonitor(0);
SetupMachineNameAndID();
InitEncryption();
CreateHelperThreads();
SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
PleaseReopenSocket = 9;
/* TODO: Telemetry for the matrix? */
}
private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
Helper.WndProcCounter++;
if (e.Mode is PowerModes.Resume or PowerModes.Suspend)
{
Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information);
LastResumeSuspendTime = DateTime.UtcNow;
MachineStuff.SwitchToMultipleMode(false, true);
}
}
private static void CreateHelperThreads()
{
// NOTE(@yuyoyuppe): service crashes while trying to obtain this info, disabling.
/*
Thread watchDogThread = new(new ThreadStart(WatchDogThread), nameof(WatchDogThread));
watchDogThread.Priority = ThreadPriority.Highest;
watchDogThread.Start();
*/
helper = new Thread(new ThreadStart(Helper.HelperThread), "Helper Thread");
helper.SetApartmentState(ApartmentState.STA);
helper.Start();
}
private static void AskHelperThreadsToExit(int waitTime)
{
Helper.signalHelperToExit = true;
Helper.signalWatchDogToExit = true;
_ = EvSwitch.Set();
int c = 0;
if (helper != null && c < waitTime)
{
while (Helper.signalHelperToExit)
{
Thread.Sleep(1);
}
helper = null;
}
}
internal static void Cleanup()
{
try
{
SendByeBye();
// UnhookClipboard();
AskHelperThreadsToExit(500);
MainForm.NotifyIcon.Visible = false;
MainForm.NotifyIcon.Dispose();
CloseAllFormsAndHooks();
DoSomethingInUIThread(() =>
{
Sk?.Close(true);
});
}
catch (Exception e)
{
Logger.Log(e);
}
}
private static long lastReleaseAllKeysCall;
internal static void ReleaseAllKeys()
{
if (Math.Abs(GetTick() - lastReleaseAllKeysCall) < 2000)
{
return;
}
lastReleaseAllKeysCall = GetTick();
KEYBDDATA kd;
kd.dwFlags = (int)LLKHF.UP;
VK[] keys = new VK[]
{
VK.LSHIFT, VK.LCONTROL, VK.LMENU, VK.LWIN, VK.RSHIFT,
VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL,
};
Logger.LogDebug("***** ReleaseAllKeys has been called! *****:");
foreach (VK vk in keys)
{
if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0)
{
Logger.LogDebug(vk.ToString() + " is down, release it...");
Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl)
kd.wVk = (int)vk;
InputSimulation.SendKey(kd);
Hook?.ResetLastSwitchKeys();
}
}
}
private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture));
Helper.WndProcCounter++;
ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable);
}
private static void ScheduleReopenSocketsDueToNetworkChanges(bool closeSockets = true)
{
if (closeSockets)
{
// Slept/hibernated machine may still have the sockets' status as Connected:( (unchanged) so it would not re-connect after a timeout when waking up.
// Closing the sockets when it is going to sleep/hibernate will trigger the reconnection faster when it wakes up.
DoSomethingInUIThread(
() =>
{
SocketStuff s = Sk;
Sk = null;
s?.Close(false);
},
true);
}
if (!Common.IsMyDesktopActive())
{
PleaseReopenSocket = 0;
}
else if (PleaseReopenSocket != 10)
{
PleaseReopenSocket = 10;
}
}
}
}

View File

@@ -36,7 +36,7 @@ namespace MouseWithoutBorders
internal static string ActiveDesktop => Common.activeDesktop;
internal static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
private static void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
GetScreenConfig();
}
@@ -340,7 +340,7 @@ namespace MouseWithoutBorders
Setting.Values.LastX = JUST_GOT_BACK_FROM_SCREEN_SAVER;
if (cleanupIfExit)
{
InitAndCleanup.Cleanup();
Common.Cleanup();
}
Process.GetCurrentProcess().KillProcess();

View File

@@ -33,7 +33,6 @@ using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
using MouseWithoutBorders.Exceptions;
using Clipboard = MouseWithoutBorders.Core.Clipboard;
using Thread = MouseWithoutBorders.Core.Thread;
// Log is enough
@@ -91,8 +90,8 @@ namespace MouseWithoutBorders
private static FrmMatrix matrixForm;
private static FrmInputCallback inputCallbackForm;
private static FrmAbout aboutForm;
private static Thread helper;
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
internal static Thread helper;
internal static int screenWidth;
internal static int screenHeight;
#pragma warning restore SA1307
@@ -122,9 +121,7 @@ namespace MouseWithoutBorders
internal static int switchCount;
#pragma warning restore SA1307
private static long lastReconnectByHotKeyTime;
#pragma warning disable SA1307 // Accessible fields should begin with upper-case names
internal static int tcpPort;
#pragma warning restore SA1307
private static int tcpPort;
private static bool secondOpenSocketTry;
private static string binaryName;
@@ -213,7 +210,7 @@ namespace MouseWithoutBorders
internal static bool Is64bitOS
{
get; set;
get; private set;
// set { Common.is64bitOS = value; }
}
@@ -614,7 +611,7 @@ namespace MouseWithoutBorders
}
* */
internal static void SendByeBye()
private static void SendByeBye()
{
Logger.LogDebug($"{nameof(SendByeBye)}");
SendPackage(ID.ALL, PackageType.ByeBye);
@@ -728,7 +725,7 @@ namespace MouseWithoutBorders
internal static void SendImage(string machine, string file)
{
Clipboard.LastDragDropFile = file;
LastDragDropFile = file;
// Send ClipboardCapture
if (machine.Equals("All", StringComparison.OrdinalIgnoreCase))
@@ -747,7 +744,7 @@ namespace MouseWithoutBorders
internal static void SendImage(ID src, string file)
{
Clipboard.LastDragDropFile = file;
LastDragDropFile = file;
// Send ClipboardCapture
SendPackage(src, PackageType.ClipboardCapture);
@@ -1294,7 +1291,7 @@ namespace MouseWithoutBorders
});
}
internal static string GetMyStorageDir()
private static string GetMyStorageDir()
{
string st = string.Empty;

View File

@@ -28,7 +28,6 @@ using MouseWithoutBorders.Core;
using SystemClipboard = System.Windows.Forms.Clipboard;
#if !MM_HELPER
using Clipboard = MouseWithoutBorders.Core.Clipboard;
using Thread = MouseWithoutBorders.Core.Thread;
#endif
@@ -160,7 +159,7 @@ namespace MouseWithoutBorders
public void SendClipboardData(ByteArrayOrString data, bool isFilePath)
{
_ = Clipboard.CheckClipboardEx(data, isFilePath);
_ = Common.CheckClipboardEx(data, isFilePath);
}
}
#endif

View File

@@ -579,7 +579,7 @@ namespace MouseWithoutBorders.Class
{
Common.ShowToolTip("Reconnecting...", 2000);
Common.LastReconnectByHotKeyTime = Common.GetTick();
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY;
Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY;
return false;
}
@@ -632,7 +632,7 @@ namespace MouseWithoutBorders.Class
{
// Common.DoSomethingInUIThread(delegate()
{
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
}
// );

View File

@@ -407,7 +407,7 @@ namespace MouseWithoutBorders.Class
{
ResetModifiersState(Setting.Values.HotKeyLockMachine);
eatKey = true;
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
_ = NativeMethods.LockWorkStation();
}
}
@@ -439,7 +439,7 @@ namespace MouseWithoutBorders.Class
{
ctrlDown = altDown = false;
eatKey = true;
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
}
break;
@@ -449,7 +449,7 @@ namespace MouseWithoutBorders.Class
{
winDown = false;
eatKey = true;
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
uint rv = NativeMethods.LockWorkStation();
Logger.LogDebug("LockWorkStation returned " + rv.ToString(CultureInfo.CurrentCulture));
}

View File

@@ -235,7 +235,7 @@ namespace MouseWithoutBorders.Class
_ = Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
Application.SetCompatibleTextRenderingDefault(false);
InitAndCleanup.Init();
Common.Init();
Core.Helper.WndProcCounter++;
var formScreen = new FrmScreen();
@@ -314,7 +314,7 @@ namespace MouseWithoutBorders.Class
MachineStuff.UpdateMachinePoolStringSetting();
SocketStuff.InvalidKeyFound = false;
InitAndCleanup.ReopenSocketDueToReadError = true;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
MachineStuff.SendMachineMatrix();
@@ -340,7 +340,7 @@ namespace MouseWithoutBorders.Class
public void Reconnect()
{
SocketStuff.InvalidKeyFound = false;
InitAndCleanup.ReopenSocketDueToReadError = true;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
for (int i = 0; i < 10; i++)
@@ -397,7 +397,7 @@ namespace MouseWithoutBorders.Class
using var asyncFlowControl = ExecutionContext.SuppressFlow();
Common.InputCallbackThreadID = Thread.CurrentThread.ManagedThreadId;
while (!InitAndCleanup.InitDone)
while (!Common.InitDone)
{
Thread.Sleep(100);
}

View File

@@ -118,7 +118,7 @@ namespace MouseWithoutBorders.Class
if (shouldReopenSockets)
{
SocketStuff.InvalidKeyFound = false;
InitAndCleanup.ReopenSocketDueToReadError = true;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
}

View File

@@ -29,7 +29,6 @@ using MouseWithoutBorders.Core;
// </history>
using MouseWithoutBorders.Exceptions;
using Clipboard = MouseWithoutBorders.Core.Clipboard;
using Thread = MouseWithoutBorders.Core.Thread;
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.SocketStuff.#SendData(System.Byte[])", Justification = "Dotnet port with style preservation")]
@@ -282,7 +281,7 @@ namespace MouseWithoutBorders.Class
* */
Common.GetMachineName(); // IPs might have been changed
InitAndCleanup.UpdateMachineTimeAndID();
Common.UpdateMachineTimeAndID();
Logger.LogDebug("Creating sockets...");
@@ -309,7 +308,7 @@ namespace MouseWithoutBorders.Class
{
Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning);
Program.StartService();
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
}
break;
@@ -1249,7 +1248,7 @@ namespace MouseWithoutBorders.Class
// WSAECONNRESET
if (e is ExpectedSocketException se && se.ShouldReconnect)
{
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
Logger.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}");
}
}
@@ -1307,7 +1306,7 @@ namespace MouseWithoutBorders.Class
}
catch (ObjectDisposedException e)
{
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed);
currentSocket.Close();
Logger.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}");
@@ -1354,10 +1353,10 @@ namespace MouseWithoutBorders.Class
* In this case, we should give ONE try to reconnect.
*/
if (InitAndCleanup.ReopenSocketDueToReadError)
if (Common.ReopenSocketDueToReadError)
{
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_WSAECONNRESET;
InitAndCleanup.ReopenSocketDueToReadError = false;
Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET;
Common.ReopenSocketDueToReadError = false;
}
break;
@@ -1642,7 +1641,7 @@ namespace MouseWithoutBorders.Class
bool clientPushData = true;
ClipboardPostAction postAction = ClipboardPostAction.Other;
bool handShaken = Clipboard.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction);
bool handShaken = Common.ShakeHand(ref remoteEndPoint, s, out Stream enStream, out Stream deStream, ref clientPushData, ref postAction);
if (!handShaken)
{
@@ -1657,7 +1656,7 @@ namespace MouseWithoutBorders.Class
if (clientPushData)
{
Clipboard.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}");
Common.ReceiveAndProcessClipboardData(remoteEndPoint, s, enStream, deStream, $"{postAction}");
}
else
{
@@ -1681,23 +1680,23 @@ namespace MouseWithoutBorders.Class
const int CLOSE_TIMEOUT = 10;
byte[] header = new byte[1024];
string headerString = string.Empty;
if (Clipboard.LastDragDropFile != null)
if (Common.LastDragDropFile != null)
{
string fileName = null;
if (!Launch.ImpersonateLoggedOnUserAndDoSomething(() =>
{
if (!File.Exists(Clipboard.LastDragDropFile))
if (!File.Exists(Common.LastDragDropFile))
{
headerString = Directory.Exists(Clipboard.LastDragDropFile)
? $"{0}*{Clipboard.LastDragDropFile} - Folder is not supported, zip it first!"
: Clipboard.LastDragDropFile.Contains("- File too big")
? $"{0}*{Clipboard.LastDragDropFile}"
: $"{0}*{Clipboard.LastDragDropFile} not found!";
headerString = Directory.Exists(Common.LastDragDropFile)
? $"{0}*{Common.LastDragDropFile} - Folder is not supported, zip it first!"
: Common.LastDragDropFile.Contains("- File too big")
? $"{0}*{Common.LastDragDropFile}"
: $"{0}*{Common.LastDragDropFile} not found!";
}
else
{
fileName = Clipboard.LastDragDropFile;
fileName = Common.LastDragDropFile;
headerString = $"{new FileInfo(fileName).Length}*{fileName}";
}
}))
@@ -1740,11 +1739,11 @@ namespace MouseWithoutBorders.Class
Logger.Log(log);
}
}
else if (!Clipboard.IsClipboardDataImage && Clipboard.LastClipboardData != null)
else if (!Common.IsClipboardDataImage && Common.LastClipboardData != null)
{
try
{
byte[] data = Clipboard.LastClipboardData;
byte[] data = Common.LastClipboardData;
headerString = $"{data.Length}*{"text"}";
Common.GetBytesU(headerString).CopyTo(header, 0);
@@ -1774,9 +1773,9 @@ namespace MouseWithoutBorders.Class
Logger.Log(log);
}
}
else if (Clipboard.LastClipboardData != null && Clipboard.LastClipboardData.Length > 0)
else if (Common.LastClipboardData != null && Common.LastClipboardData.Length > 0)
{
byte[] data = Clipboard.LastClipboardData;
byte[] data = Common.LastClipboardData;
headerString = $"{data.Length}*{"image"}";
Common.GetBytesU(headerString).CopyTo(header, 0);
@@ -1985,8 +1984,8 @@ namespace MouseWithoutBorders.Class
{
tcp = null;
Setting.Values.MachineId = Common.Ran.Next();
InitAndCleanup.UpdateMachineTimeAndID();
InitAndCleanup.PleaseReopenSocket = InitAndCleanup.REOPEN_WHEN_HOTKEY;
Common.UpdateMachineTimeAndID();
Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY;
Logger.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information);
}

File diff suppressed because it is too large Load Diff

View File

@@ -83,7 +83,7 @@ internal static class DragDrop
if (wParam == Common.WM_RBUTTONUP && IsDropping)
{
IsDropping = false;
Clipboard.LastIDWithClipboardData = ID.NONE;
Common.LastIDWithClipboardData = ID.NONE;
}
}
@@ -193,7 +193,7 @@ internal static class DragDrop
{
if (!string.IsNullOrEmpty(dragFileName) && (File.Exists(dragFileName) || Directory.Exists(dragFileName)))
{
Clipboard.LastDragDropFile = dragFileName;
Common.LastDragDropFile = dragFileName;
/*
* possibleDropMachineID is used as desID sent in DragDropStep06();
* */
@@ -270,7 +270,7 @@ internal static class DragDrop
else
{
IsDragging = false;
Clipboard.LastIDWithClipboardData = ID.NONE;
Common.LastIDWithClipboardData = ID.NONE;
}
}
}
@@ -280,7 +280,7 @@ internal static class DragDrop
Logger.LogDebug("DragDropStep10: Hide the form and get data...");
IsDropping = false;
IsDragging = false;
Clipboard.LastIDWithClipboardData = ID.NONE;
Common.LastIDWithClipboardData = ID.NONE;
Common.DoSomethingInUIThread(() =>
{
@@ -288,7 +288,7 @@ internal static class DragDrop
});
PowerToysTelemetry.Log.WriteEvent(new MouseWithoutBorders.Telemetry.MouseWithoutBordersDragAndDropEvent());
Clipboard.GetRemoteClipboard("desktop");
Common.GetRemoteClipboard("desktop");
}
internal static void DragDropStep11()
@@ -298,8 +298,8 @@ internal static class DragDrop
IsDropping = false;
IsDragging = false;
DragMachine = (ID)1;
Clipboard.LastIDWithClipboardData = ID.NONE;
Clipboard.LastDragDropFile = null;
Common.LastIDWithClipboardData = ID.NONE;
Common.LastDragDropFile = null;
MouseDown = false;
}
@@ -307,7 +307,7 @@ internal static class DragDrop
{
Logger.LogDebug("DragDropStep12: ClipboardDragDropEnd received");
IsDropping = false;
Clipboard.LastIDWithClipboardData = ID.NONE;
Common.LastIDWithClipboardData = ID.NONE;
Common.DoSomethingInUIThread(() =>
{

View File

@@ -78,7 +78,7 @@ internal static class Event
// if they are, check that there is no application running in fullscreen mode before switching.
if (!p.IsEmpty && Common.IsEasyMouseSwitchAllowed())
{
Clipboard.HasSwitchedMachineSinceLastCopy = true;
Common.HasSwitchedMachineSinceLastCopy = true;
Logger.LogDebug(string.Format(
CultureInfo.CurrentCulture,
@@ -218,10 +218,10 @@ internal static class Event
if (MachineStuff.desMachineID == Common.MachineID)
{
if (Common.GetTick() - Clipboard.clipboardCopiedTime < Clipboard.BIG_CLIPBOARD_DATA_TIMEOUT)
if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT)
{
Clipboard.clipboardCopiedTime = 0;
Clipboard.GetRemoteClipboard("PrepareToSwitchToMachine");
Common.clipboardCopiedTime = 0;
Common.GetRemoteClipboard("PrepareToSwitchToMachine");
}
}
else

View File

@@ -119,7 +119,7 @@ internal static class Helper
if (MachineStuff.NewDesMachineID == Common.MachineID)
{
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
}
}
}
@@ -317,7 +317,7 @@ internal static class Helper
Common.GetInputDesktop(),
0);
Clipboard.HasSwitchedMachineSinceLastCopy = true;
Common.HasSwitchedMachineSinceLastCopy = true;
// Common.CreateLowIntegrityProcess("\"" + Path.GetDirectoryName(Application.ExecutablePath) + "\\MouseWithoutBordersHelper.exe\"", string.Empty, 0, false, 0);
var processes = Process.GetProcessesByName(HelperProcessName);

View File

@@ -1,278 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Threading;
using Microsoft.Win32;
using MouseWithoutBorders.Class;
using Windows.UI.Input.Preview.Injection;
// <summary>
// Initialization and clean up.
// </summary>
// <history>
// 2008 created by Truong Do (ductdo).
// 2009-... modified by Truong Do (TruongDo).
// 2023- Included in PowerToys.
// </history>
namespace MouseWithoutBorders.Core;
internal static class InitAndCleanup
{
private static bool initDone;
internal static int REOPEN_WHEN_WSAECONNRESET = -10054;
internal static int REOPEN_WHEN_HOTKEY = -10055;
internal static int PleaseReopenSocket;
internal static bool ReopenSocketDueToReadError;
private static DateTime LastResumeSuspendTime { get; set; } = DateTime.UtcNow;
internal static bool InitDone
{
get => InitAndCleanup.initDone;
set => InitAndCleanup.initDone = value;
}
internal static void UpdateMachineTimeAndID()
{
Common.MachineName = Common.MachineName.Trim();
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
}
private static void InitializeMachinePoolFromSettings()
{
try
{
MachineInf[] info = MachinePoolHelpers.LoadMachineInfoFromMachinePoolStringSetting(Setting.Values.MachinePoolString);
for (int i = 0; i < info.Length; i++)
{
info[i].Name = info[i].Name.Trim();
}
MachineStuff.MachinePool.Initialize(info);
MachineStuff.MachinePool.ResetIPAddressesForDeadMachines(true);
}
catch (Exception ex)
{
Logger.Log(ex);
MachineStuff.MachinePool.Clear();
}
}
private static void SetupMachineNameAndID()
{
try
{
Common.GetMachineName();
Common.DesMachineID = MachineStuff.NewDesMachineID = Common.MachineID;
// MessageBox.Show(machineID.ToString(CultureInfo.CurrentCulture)); // For test
InitializeMachinePoolFromSettings();
Common.MachineName = Common.MachineName.Trim();
_ = MachineStuff.MachinePool.LearnMachine(Common.MachineName);
_ = MachineStuff.MachinePool.TryUpdateMachineID(Common.MachineName, Common.MachineID, true);
MachineStuff.UpdateMachinePoolStringSetting();
}
catch (Exception e)
{
Logger.Log(e);
}
}
internal static void Init()
{
_ = Helper.GetUserName();
Common.GeneratedKey = true;
try
{
Common.MyKey = Setting.Values.MyKey;
int tmp = Setting.Values.MyKeyDaysToExpire;
}
catch (FormatException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Logger.Log(e.Message);
}
catch (CryptographicException e)
{
Common.KeyCorrupted = true;
Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey();
Logger.Log(e.Message);
}
try
{
InputSimulation.Injector = InputInjector.TryCreate();
if (InputSimulation.Injector != null)
{
InputSimulation.MoveMouseRelative(0, 0);
NativeMethods.InjectMouseInputAvailable = true;
}
}
catch (EntryPointNotFoundException)
{
NativeMethods.InjectMouseInputAvailable = false;
Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false");
}
bool dummy = Setting.Values.DrawMouseEx;
Common.Is64bitOS = IntPtr.Size == 8;
Common.tcpPort = Setting.Values.TcpPort;
Common.GetScreenConfig();
Common.PackageSent = new PackageMonitor(0);
Common.PackageReceived = new PackageMonitor(0);
SetupMachineNameAndID();
Common.InitEncryption();
CreateHelperThreads();
SystemEvents.DisplaySettingsChanged += new EventHandler(Common.SystemEvents_DisplaySettingsChanged);
NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
PleaseReopenSocket = 9;
/* TODO: Telemetry for the matrix? */
}
private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
Helper.WndProcCounter++;
if (e.Mode is PowerModes.Resume or PowerModes.Suspend)
{
Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information);
LastResumeSuspendTime = DateTime.UtcNow;
MachineStuff.SwitchToMultipleMode(false, true);
}
}
private static void CreateHelperThreads()
{
// NOTE(@yuyoyuppe): service crashes while trying to obtain this info, disabling.
/*
Thread watchDogThread = new(new ThreadStart(WatchDogThread), nameof(WatchDogThread));
watchDogThread.Priority = ThreadPriority.Highest;
watchDogThread.Start();
*/
Common.helper = new Thread(new ThreadStart(Helper.HelperThread), "Helper Thread");
Common.helper.SetApartmentState(ApartmentState.STA);
Common.helper.Start();
}
private static void AskHelperThreadsToExit(int waitTime)
{
Helper.signalHelperToExit = true;
Helper.signalWatchDogToExit = true;
_ = Common.EvSwitch.Set();
int c = 0;
if (Common.helper != null && c < waitTime)
{
while (Helper.signalHelperToExit)
{
Thread.Sleep(1);
}
Common.helper = null;
}
}
internal static void Cleanup()
{
try
{
Common.SendByeBye();
// UnhookClipboard();
AskHelperThreadsToExit(500);
Common.MainForm.NotifyIcon.Visible = false;
Common.MainForm.NotifyIcon.Dispose();
Common.CloseAllFormsAndHooks();
Common.DoSomethingInUIThread(() =>
{
Common.Sk?.Close(true);
});
}
catch (Exception e)
{
Logger.Log(e);
}
}
private static long lastReleaseAllKeysCall;
internal static void ReleaseAllKeys()
{
if (Math.Abs(Common.GetTick() - lastReleaseAllKeysCall) < 2000)
{
return;
}
lastReleaseAllKeysCall = Common.GetTick();
KEYBDDATA kd;
kd.dwFlags = (int)Common.LLKHF.UP;
VK[] keys = new VK[]
{
VK.LSHIFT, VK.LCONTROL, VK.LMENU, VK.LWIN, VK.RSHIFT,
VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL,
};
Logger.LogDebug("***** ReleaseAllKeys has been called! *****:");
foreach (VK vk in keys)
{
if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0)
{
Logger.LogDebug(vk.ToString() + " is down, release it...");
Common.Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl)
kd.wVk = (int)vk;
InputSimulation.SendKey(kd);
Common.Hook?.ResetLastSwitchKeys();
}
}
}
private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture));
Helper.WndProcCounter++;
ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable);
}
private static void ScheduleReopenSocketsDueToNetworkChanges(bool closeSockets = true)
{
if (closeSockets)
{
// Slept/hibernated machine may still have the sockets' status as Connected:( (unchanged) so it would not re-connect after a timeout when waking up.
// Closing the sockets when it is going to sleep/hibernate will trigger the reconnection faster when it wakes up.
Common.DoSomethingInUIThread(
() =>
{
SocketStuff s = Common.Sk;
Common.Sk = null;
s?.Close(false);
},
true);
}
if (!Common.IsMyDesktopActive())
{
PleaseReopenSocket = 0;
}
else if (PleaseReopenSocket != 10)
{
PleaseReopenSocket = 10;
}
}
}

View File

@@ -247,24 +247,22 @@ internal static class Logger
internal static void DumpStaticTypes(StringBuilder sb, int level)
{
var staticTypes = new List<Type>
{
typeof(Clipboard),
typeof(DragDrop),
typeof(Event),
typeof(InitAndCleanup),
typeof(Helper),
typeof(Launch),
typeof(Logger),
typeof(MachineStuff),
typeof(Receiver),
typeof(Service),
};
foreach (var staticType in staticTypes)
{
sb.AppendLine(CultureInfo.InvariantCulture, $"[{staticType.Name}]\r\n===============");
Logger.DumpType(sb, staticType, 0, level);
}
sb.AppendLine($"[{nameof(DragDrop)}]\r\n===============");
Logger.DumpType(sb, typeof(DragDrop), 0, level);
sb.AppendLine($"[{nameof(Event)}]\r\n===============");
Logger.DumpType(sb, typeof(Event), 0, level);
sb.AppendLine($"[{nameof(Helper)}]\r\n===============");
Logger.DumpType(sb, typeof(Helper), 0, level);
sb.AppendLine($"[{nameof(Launch)}]\r\n===============");
Logger.DumpType(sb, typeof(Launch), 0, level);
sb.AppendLine($"[{nameof(Logger)}]\r\n===============");
Logger.DumpType(sb, typeof(Logger), 0, level);
sb.AppendLine($"[{nameof(MachineStuff)}]\r\n===============");
Logger.DumpType(sb, typeof(MachineStuff), 0, level);
sb.AppendLine($"[{nameof(Receiver)}]\r\n===============");
Logger.DumpType(sb, typeof(Receiver), 0, level);
sb.AppendLine($"[{nameof(Service)}]\r\n===============");
Logger.DumpType(sb, typeof(Service), 0, level);
}
internal static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop)

View File

@@ -992,7 +992,7 @@ internal static class MachineStuff
Setting.Values.MatrixOneRow = !((package.Type & PackageType.MatrixTwoRowFlag) == PackageType.MatrixTwoRowFlag);
MachineMatrix = MachineMatrix; // Save
InitAndCleanup.ReopenSocketDueToReadError = true;
Common.ReopenSocketDueToReadError = true;
UpdateClientSockets("UpdateMachineMatrix");
@@ -1044,7 +1044,7 @@ internal static class MachineStuff
Common.MoveMouseToCenter();
}
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
Common.UpdateMultipleModeIconAndMenu();
}

View File

@@ -157,7 +157,7 @@ internal static class Receiver
if (!p.IsEmpty)
{
Clipboard.HasSwitchedMachineSinceLastCopy = true;
Common.HasSwitchedMachineSinceLastCopy = true;
Logger.LogDebug(string.Format(
CultureInfo.CurrentCulture,
@@ -274,7 +274,7 @@ internal static class Receiver
Common.PackageReceived.Clipboard++;
if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop)
{
Clipboard.clipboardCopiedTime = Common.GetTick();
Common.clipboardCopiedTime = Common.GetTick();
GetNameOfMachineWithClipboardData(package);
SignalBigClipboardData();
}
@@ -282,10 +282,10 @@ internal static class Receiver
break;
case PackageType.MachineSwitched:
if (Common.GetTick() - Clipboard.clipboardCopiedTime < Clipboard.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID))
if (Common.GetTick() - Common.clipboardCopiedTime < Common.BIG_CLIPBOARD_DATA_TIMEOUT && (package.Des == Common.MachineID))
{
Clipboard.clipboardCopiedTime = 0;
Clipboard.GetRemoteClipboard("PackageType.MachineSwitched");
Common.clipboardCopiedTime = 0;
Common.GetRemoteClipboard("PackageType.MachineSwitched");
}
break;
@@ -297,7 +297,7 @@ internal static class Receiver
if (package.Des == Common.MachineID || package.Des == ID.ALL)
{
GetNameOfMachineWithClipboardData(package);
Clipboard.GetRemoteClipboard("mspaint," + Clipboard.LastMachineWithClipboardData);
Common.GetRemoteClipboard("mspaint," + Common.LastMachineWithClipboardData);
}
}
@@ -326,10 +326,10 @@ internal static class Receiver
Thread.UpdateThreads(thread);
string remoteMachine = package.MachineName;
System.Net.Sockets.TcpClient client = Clipboard.ConnectToRemoteClipboardSocket(remoteMachine);
System.Net.Sockets.TcpClient client = Common.ConnectToRemoteClipboardSocket(remoteMachine);
bool clientPushData = true;
if (Clipboard.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction))
if (Common.ShakeHand(ref remoteMachine, client.Client, out Stream enStream, out Stream deStream, ref clientPushData, ref package.PostAction))
{
SocketStuff.SendClipboardData(client.Client, enStream);
}
@@ -360,7 +360,7 @@ internal static class Receiver
case PackageType.ClipboardText:
case PackageType.ClipboardImage:
Clipboard.clipboardCopiedTime = 0;
Common.clipboardCopiedTime = 0;
if (package.Type == PackageType.ClipboardImage)
{
Common.PackageReceived.ClipboardImage++;
@@ -372,7 +372,7 @@ internal static class Receiver
if (tcp != null)
{
Clipboard.ReceiveClipboardDataUsingTCP(
Common.ReceiveClipboardDataUsingTCP(
package,
package.Type == PackageType.ClipboardImage,
tcp);
@@ -381,10 +381,10 @@ internal static class Receiver
break;
case PackageType.HideMouse:
Clipboard.HasSwitchedMachineSinceLastCopy = true;
Common.HasSwitchedMachineSinceLastCopy = true;
Common.HideMouseCursor(true);
Helper.MainFormDotEx(false);
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
break;
default:
@@ -405,11 +405,11 @@ internal static class Receiver
internal static void GetNameOfMachineWithClipboardData(DATA package)
{
Clipboard.LastIDWithClipboardData = package.Src;
List<MachineInf> matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Clipboard.LastIDWithClipboardData);
Common.LastIDWithClipboardData = package.Src;
List<MachineInf> matchingMachines = MachineStuff.MachinePool.TryFindMachineByID(Common.LastIDWithClipboardData);
if (matchingMachines.Count >= 1)
{
Clipboard.LastMachineWithClipboardData = matchingMachines[0].Name.Trim();
Common.LastMachineWithClipboardData = matchingMachines[0].Name.Trim();
}
/*

View File

@@ -84,7 +84,7 @@ namespace MouseWithoutBorders
if ((connectedClientSocket = Common.GetConnectedClientSocket()) != null)
{
ShowStatus($"Connected from local IP Address: {connectedClientSocket.Address}.");
InitAndCleanup.UpdateMachineTimeAndID();
Common.UpdateMachineTimeAndID();
Common.MMSleep(1);
connected = true;

View File

@@ -22,8 +22,6 @@ using Microsoft.PowerToys.Telemetry;
// </history>
using MouseWithoutBorders.Class;
using MouseWithoutBorders.Core;
using Clipboard = MouseWithoutBorders.Core.Clipboard;
using Timer = System.Windows.Forms.Timer;
[module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#buttonOK_Click(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")]
@@ -112,7 +110,7 @@ namespace MouseWithoutBorders
{
SocketStuff.InvalidKeyFound = false;
showInvalidKeyMessage = false;
InitAndCleanup.ReopenSocketDueToReadError = true;
Common.ReopenSocketDueToReadError = true;
Common.ReopenSockets(true);
for (int i = 0; i < 10; i++)
@@ -782,7 +780,7 @@ namespace MouseWithoutBorders
ShowUpdateMessage();
Clipboard.HasSwitchedMachineSinceLastCopy = true;
Common.HasSwitchedMachineSinceLastCopy = true;
}
private void CheckBoxDisableCAD_CheckedChanged(object sender, EventArgs e)

View File

@@ -139,13 +139,13 @@ namespace MouseWithoutBorders
{
if (cleanup)
{
InitAndCleanup.Cleanup();
Common.Cleanup();
}
Helper.WndProcCounter++;
if (!Common.RunOnScrSaverDesktop)
{
InitAndCleanup.ReleaseAllKeys();
Common.ReleaseAllKeys();
}
Helper.RunDDHelper(true);
@@ -412,7 +412,7 @@ namespace MouseWithoutBorders
count = 0;
InitAndCleanup.InitDone = true;
Common.InitDone = true;
#if SHOW_ON_WINLOGON
if (Common.RunOnLogonDesktop)
{
@@ -423,39 +423,39 @@ namespace MouseWithoutBorders
if ((count % 2) == 0)
{
if (InitAndCleanup.PleaseReopenSocket == 10 || (InitAndCleanup.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0))
if (Common.PleaseReopenSocket == 10 || (Common.PleaseReopenSocket > 0 && count > 0 && count % 300 == 0))
{
if (!Common.AtLeastOneSocketEstablished() || InitAndCleanup.PleaseReopenSocket == 10)
if (!Common.AtLeastOneSocketEstablished() || Common.PleaseReopenSocket == 10)
{
Thread.Sleep(1000);
if (InitAndCleanup.PleaseReopenSocket > 0)
if (Common.PleaseReopenSocket > 0)
{
InitAndCleanup.PleaseReopenSocket--;
Common.PleaseReopenSocket--;
}
// Double check.
if (!Common.AtLeastOneSocketEstablished())
{
Common.GetMachineName();
Logger.LogDebug("Common.pleaseReopenSocket: " + InitAndCleanup.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture));
Logger.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture));
Common.ReopenSockets(false);
MachineStuff.NewDesMachineID = Common.DesMachineID = Common.MachineID;
}
}
else
{
InitAndCleanup.PleaseReopenSocket = 0;
Common.PleaseReopenSocket = 0;
}
}
if (InitAndCleanup.PleaseReopenSocket == InitAndCleanup.REOPEN_WHEN_HOTKEY)
if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_HOTKEY)
{
InitAndCleanup.PleaseReopenSocket = 0;
Common.PleaseReopenSocket = 0;
Common.ReopenSockets(true);
}
else if (InitAndCleanup.PleaseReopenSocket == InitAndCleanup.REOPEN_WHEN_WSAECONNRESET)
else if (Common.PleaseReopenSocket == Common.REOPEN_WHEN_WSAECONNRESET)
{
InitAndCleanup.PleaseReopenSocket = 0;
Common.PleaseReopenSocket = 0;
Thread.Sleep(1000);
MachineStuff.UpdateClientSockets("REOPEN_WHEN_WSAECONNRESET");
}

View File

@@ -4,6 +4,28 @@
[Other Logs]
===============
= MouseWithoutBorders.Common
Comma = System.Char[]
--System.Char[] = System.Char[]: N/A
Star = System.Char[]
--System.Char[] = System.Char[]: N/A
NullSeparator = System.Char[]
--System.Char[] = System.Char[]: N/A
lastClipboardEventTime = 0
clipboardCopiedTime = 0
<LastIDWithClipboardData>k__BackingField = NONE
<NextClipboardViewer>k__BackingField = 0
<IsClipboardDataImage>k__BackingField = False
lastClipboardObject =
<HasSwitchedMachineSinceLastCopy>k__BackingField = False
ClipboardThreadOldLock = Lock
--_owningThreadId = 0
--_state = 0
--_recursionCount = 0
--_spinCount = 22
--_waiterStartTimeMs = 0
--s_contentionCount = 0
--s_maxSpinCount = 22
--s_minSpinCountForAdaptiveSpin = -100
screenWidth = 0
screenHeight = 0
lastX = 0
@@ -77,6 +99,17 @@ LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte
--_budget = ????????????
--_growLockArray = True
--_comparerIsDefaultForClasses = False
initDone = False
REOPEN_WHEN_WSAECONNRESET = -10054
REOPEN_WHEN_HOTKEY = -10055
PleaseReopenSocket = 0
ReopenSocketDueToReadError = False
<LastResumeSuspendTime>k__BackingField = ????????????
--_dateData = ????????????
--MinValue = 01/01/0001 00:00:00
--MaxValue = 31/12/9999 23:59:59
--UnixEpoch = 01/01/1970 00:00:00
lastReleaseAllKeysCall = 0
PackageSent = MouseWithoutBorders.PackageMonitor
--Keyboard = 0
--Mouse = 0
@@ -120,6 +153,12 @@ p = {X=0,Y=0}
--y = 0
--Empty = {X=0,Y=0}
<IpcChannelCreated>k__BackingField = False
BIG_CLIPBOARD_DATA_TIMEOUT = 30000
MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
TEXT_HEADER_SIZE = 12
DATA_SIZE = 48
TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
TOGGLE_ICONS_SIZE = 4
ICON_ONE = 0
ICON_ALL = 1
@@ -156,36 +195,6 @@ WM_KEYDOWN = 256
WM_KEYUP = 257
WM_SYSKEYDOWN = 260
WM_SYSKEYUP = 261
[Clipboard]
===============
Comma = System.Char[]
--System.Char[] = System.Char[]: N/A
Star = System.Char[]
--System.Char[] = System.Char[]: N/A
NullSeparator = System.Char[]
--System.Char[] = System.Char[]: N/A
lastClipboardEventTime = 0
clipboardCopiedTime = 0
<LastIDWithClipboardData>k__BackingField = NONE
<NextClipboardViewer>k__BackingField = 0
<IsClipboardDataImage>k__BackingField = False
lastClipboardObject =
<HasSwitchedMachineSinceLastCopy>k__BackingField = False
ClipboardThreadOldLock = Lock
--_owningThreadId = 0
--_state = 0
--_recursionCount = 0
--_spinCount = 22
--_waiterStartTimeMs = 0
--s_contentionCount = 0
--s_maxSpinCount = 22
--s_minSpinCountForAdaptiveSpin = -100
BIG_CLIPBOARD_DATA_TIMEOUT = 30000
MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
TEXT_HEADER_SIZE = 12
DATA_SIZE = 48
TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
[DragDrop]
===============
isDragging = False
@@ -240,19 +249,6 @@ actualLastPos = {X=0,Y=0}
--Empty = {X=0,Y=0}
myLastX = 0
myLastY = 0
[InitAndCleanup]
===============
initDone = False
REOPEN_WHEN_WSAECONNRESET = -10054
REOPEN_WHEN_HOTKEY = -10055
PleaseReopenSocket = 0
ReopenSocketDueToReadError = False
<LastResumeSuspendTime>k__BackingField = ????????????
--_dateData = ????????????
--MinValue = 01/01/0001 00:00:00
--MaxValue = 31/12/9999 23:59:59
--UnixEpoch = 01/01/1970 00:00:00
lastReleaseAllKeysCall = 0
[Helper]
===============
signalHelperToExit = False

View File

@@ -121,8 +121,8 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
LTEXT "ZoomIt v9.01",IDC_VERSION,42,7,73,10
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,166,8
LTEXT "ZoomIt v9.0",IDC_VERSION,42,7,73,10
LTEXT "Copyright <EFBFBD> 2006-2024 Mark Russinovich",IDC_COPYRIGHT,42,17,166,8
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
"SysLink",WS_TABSTOP,42,26,150,9
ICON "APPICON",IDC_STATIC,12,9,20,20

View File

@@ -3525,10 +3525,6 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
}
if( destFile == nullptr ) {
if (stream) {
stream.Close();
stream = nullptr;
}
co_await file.DeleteAsync();
}
else {
@@ -3548,10 +3544,6 @@ winrt::fire_and_forget StartRecordingAsync( HWND hWnd, LPRECT rcCrop, HWND hWndR
}
else {
if (stream) {
stream.Close();
stream = nullptr;
}
co_await file.DeleteAsync();
g_RecordingSession = nullptr;
}
@@ -4024,10 +4016,7 @@ LRESULT APIENTRY MainWndProc(
// Now copy crop or copy+save
if( LOWORD( wParam ) == SNIP_SAVE_HOTKEY )
{
// Hide cursor for screen capture
ShowCursor(false);
SendMessage( hWnd, WM_COMMAND, IDC_SAVE_CROP, ( zoomed ? 0 : SHALLOW_ZOOM ) );
ShowCursor(true);
}
else
{
@@ -4059,6 +4048,12 @@ LRESULT APIENTRY MainWndProc(
OutputDebug( L"Exiting liveDraw after snip\n" );
SendMessage( hWnd, WM_KEYDOWN, VK_ESCAPE, 0 );
}
else
{
// Set wparam to 1 to exit without animation
OutputDebug(L"Exiting zoom after snip\n" );
SendMessage( hWnd, WM_HOTKEY, ZOOM_HOTKEY, SHALLOW_DESTROY );
}
}
break;
}
@@ -5783,26 +5778,17 @@ LRESULT APIENTRY MainWndProc(
if( !g_DrawingShape ) {
// If the point has changed, draw a line to it
if (prevPt.x != LOWORD(lParam) || prevPt.y != HIWORD(lParam)) {
Gdiplus::Graphics dstGraphics(hdcScreenCompat);
if ((GetWindowLong(g_hWndMain, GWL_EXSTYLE) & WS_EX_LAYERED) == 0)
{
dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
}
Gdiplus::Color color = ColorFromColorRef(g_PenColor);
Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
Gdiplus::GraphicsPath path;
pen.SetLineJoin(Gdiplus::LineJoinRound);
path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam));
dstGraphics.DrawPath(&pen, &path);
}
// Draw a dot at the current point, if the point hasn't changed
else {
MoveToEx(hdcScreenCompat, prevPt.x, prevPt.y, NULL);
LineTo(hdcScreenCompat, LOWORD(lParam), HIWORD(lParam));
InvalidateRect(hWnd, NULL, FALSE);
Gdiplus::Graphics dstGraphics(hdcScreenCompat);
if( ( GetWindowLong( g_hWndMain, GWL_EXSTYLE ) & WS_EX_LAYERED ) == 0 )
{
dstGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
}
Gdiplus::Color color = ColorFromColorRef(g_PenColor);
Gdiplus::Pen pen(color, static_cast<Gdiplus::REAL>(g_PenWidth));
Gdiplus::GraphicsPath path;
pen.SetLineJoin(Gdiplus::LineJoinRound);
path.AddLine(prevPt.x, prevPt.y, LOWORD(lParam), HIWORD(lParam));
dstGraphics.DrawPath(&pen, &path);
prevPt.x = LOWORD( lParam );
prevPt.y = HIWORD( lParam );

View File

@@ -2,44 +2,28 @@
# You can modify the rules from these initially generated values to suit your own policies.
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference.
##################################################
# Global settings
##################################################
[*.{cs,vb}]
tab_width = 4
indent_size = 4
end_of_line = crlf
indent_style = space
insert_final_newline = true
file_header_template = Copyright (c) Microsoft Corporation\nThe Microsoft Corporation licenses this file to you under the MIT license.\nSee the LICENSE file in the project root for more information.
##################################################
# C# specific formatting
##################################################
[*.cs]
# ----------------------------------------------
# Core editorconfig formatting - indentation
# ----------------------------------------------
file_header_template = Copyright (c) Microsoft Corporation\r\nThe Microsoft Corporation licenses this file to you under the MIT license.\r\nSee the LICENSE file in the project root for more information.
#Core editorconfig formatting - indentation
#use soft tabs (spaces) for indentation
indent_style = space
#Formatting - new line options
#place else statements on a new line
csharp_new_line_before_else = true
#require braces to be on a new line for lambdas, methods, control_blocks, types, properties, and accessors (also known as "Allman" style)
csharp_new_line_before_open_brace = all
# ----------------------------------------------
# Formatting - organize using options
# ----------------------------------------------
#Formatting - organize using options
# sort System.* using directives alphabetically, and place them before other usings
#sort System.* using directives alphabetically, and place them before other usings
dotnet_sort_system_directives_first = true
# Do not place System.* using directives before other using directives.
dotnet_separate_import_directive_groups = false
# ----------------------------------------------
# Formatting - spacing options
# ----------------------------------------------
#Formatting - spacing options
#require NO space between a cast and the value
csharp_space_after_cast = false
@@ -60,29 +44,17 @@ csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
csharp_space_between_method_declaration_parameter_list_parentheses = false
# ----------------------------------------------
# Formatting - wrapping options
# ----------------------------------------------
#Formatting - wrapping options
#leave code block on separate lines
csharp_preserve_single_line_blocks = true
#put each statement on a separate line
csharp_preserve_single_line_statements = false
##################################################
# C# style rules
##################################################
# ----------------------------------------------
# Style - Code block preferences
# ----------------------------------------------
#Style - Code block preferences
#prefer curly braces even for one line of code
csharp_prefer_braces = true:suggestion
# ----------------------------------------------
# Style - expression bodied member options
# ----------------------------------------------
#Style - expression bodied member options
#prefer expression bodies for accessors
csharp_style_expression_bodied_accessors = true:warning
@@ -93,73 +65,55 @@ csharp_style_expression_bodied_methods = when_on_single_line:silent
#prefer expression-bodied members for properties
csharp_style_expression_bodied_properties = true:warning
# ----------------------------------------------
# Style - expression level options
# ----------------------------------------------
#Style - expression level options
#prefer out variables to be declared before the method call
csharp_style_inlined_variable_declaration = false:suggestion
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_member_access = true:suggestion
# ----------------------------------------------
# Style - Expression-level preferences
# ----------------------------------------------
#Style - Expression-level preferences
#prefer default over default(T)
csharp_prefer_simple_default_expression = true:suggestion
#prefer objects to be initialized using object initializers when possible
dotnet_style_object_initializer = true:suggestion
# ----------------------------------------------
# Style - implicit and explicit types
# ----------------------------------------------
#Style - implicit and explicit types
#prefer var over explicit type in all cases, unless overridden by another code style rule
csharp_style_var_elsewhere = true:suggestion
#prefer var is used to declare variables with built-in system types such as int
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_for_built_in_types = true:suggestion
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
csharp_style_var_when_type_is_apparent = true:suggestion
# ----------------------------------------------
# Style - language keyword and framework type options
# ----------------------------------------------
#Style - language keyword and framework type options
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
# ----------------------------------------------
# Style - Language rules
# ----------------------------------------------
#Style - Language rules
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_var_for_built_in_types = true:warning
# ----------------------------------------------
# Style - modifier options
# ----------------------------------------------
#Style - modifier options
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# ----------------------------------------------
# Style - Modifier preferences
# ----------------------------------------------
#Style - Modifier preferences
#when this rule is set to a list of modifiers, prefer the specified ordering.
csharp_preferred_modifier_order = public,private,protected,internal,static,async,readonly,override,sealed,abstract,virtual:warning
dotnet_style_readonly_field = true:warning
# ----------------------------------------------
# Style - Pattern matching
# ----------------------------------------------
#Style - Pattern matching
#prefer pattern matching instead of is expression with type casts
csharp_style_pattern_matching_over_as_with_null_check = true:warning
# ----------------------------------------------
# Style - qualification options
# ----------------------------------------------
#Style - qualification options
#prefer events not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_event = false:suggestion
@@ -169,26 +123,20 @@ dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
#prefer properties not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_property = false:suggestion
# ----------------------------------------------
# Style - expression bodies
# ----------------------------------------------
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:warning
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
# ----------------------------------------------
# Style - Miscellaneous preferences
# ----------------------------------------------
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:warning
csharp_style_namespace_declarations = file_scoped:warning
[*.{cs,vb}]
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
@@ -198,13 +146,12 @@ dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
[*.{cs,vb}]
#Style - Unnecessary code rules
csharp_style_unused_value_assignment_preference = discard_variable:warning
##################################################
# Naming rules
##################################################
[*.{cs,vb}]
#### Naming styles ####
# Naming rules
@@ -256,11 +203,7 @@ dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:warning
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Diagnostic configuration
##################################################
# Diagnostics
##################################################
[*.{cs,vb}]
# CS8305: Type is for evaluation purposes only and is subject to change or removal in future updates.
dotnet_diagnostic.CS8305.severity = suggestion

View File

@@ -9,7 +9,7 @@
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="System.Text.Json" Version="9.0.8" />

View File

@@ -1,155 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Common.Commands;
public sealed partial class ConfirmableCommand : InvokableCommand
{
private readonly IInvokableCommand? _command;
public Func<bool>? IsConfirmationRequired { get; init; }
public required string ConfirmationTitle { get; init; }
public required string ConfirmationMessage { get; init; }
public required IInvokableCommand Command
{
get => _command!;
init
{
if (_command is INotifyPropChanged oldNotifier)
{
oldNotifier.PropChanged -= InnerCommand_PropChanged;
}
_command = value;
if (_command is INotifyPropChanged notifier)
{
notifier.PropChanged += InnerCommand_PropChanged;
}
OnPropertyChanged(nameof(Name));
OnPropertyChanged(nameof(Id));
OnPropertyChanged(nameof(Icon));
}
}
public override string Name
{
get => (_command as Command)?.Name ?? base.Name;
set
{
if (_command is Command cmd)
{
cmd.Name = value;
}
else
{
base.Name = value;
}
}
}
public override string Id
{
get => (_command as Command)?.Id ?? base.Id;
set
{
var previous = Id;
if (_command is Command cmd)
{
cmd.Id = value;
}
else
{
base.Id = value;
}
if (previous != Id)
{
OnPropertyChanged(nameof(Id));
}
}
}
public override IconInfo Icon
{
get => (_command as Command)?.Icon ?? base.Icon;
set
{
if (_command is Command cmd)
{
cmd.Icon = value;
}
else
{
base.Icon = value;
}
}
}
public ConfirmableCommand()
{
// Allow init-only construction
}
[SetsRequiredMembers]
public ConfirmableCommand(IInvokableCommand command, string confirmationTitle, string confirmationMessage, Func<bool>? isConfirmationRequired = null)
{
ArgumentNullException.ThrowIfNull(command);
ArgumentException.ThrowIfNullOrWhiteSpace(confirmationMessage);
ArgumentNullException.ThrowIfNull(confirmationMessage);
IsConfirmationRequired = isConfirmationRequired;
ConfirmationTitle = confirmationTitle;
ConfirmationMessage = confirmationMessage;
Command = command;
}
private void InnerCommand_PropChanged(object sender, IPropChangedEventArgs args)
{
var property = args.PropertyName;
if (string.IsNullOrEmpty(property) || property == nameof(Name))
{
OnPropertyChanged(nameof(Name));
}
if (string.IsNullOrEmpty(property) || property == nameof(Id))
{
OnPropertyChanged(nameof(Id));
}
if (string.IsNullOrEmpty(property) || property == nameof(Icon))
{
OnPropertyChanged(nameof(Icon));
}
}
public override ICommandResult Invoke()
{
var showConfirmationDialog = IsConfirmationRequired?.Invoke() ?? true;
if (showConfirmationDialog)
{
return CommandResult.Confirm(new ConfirmationArgs
{
Title = ConfirmationTitle,
Description = ConfirmationMessage,
PrimaryCommand = Command,
IsPrimaryCommandCritical = true,
});
}
else
{
return Command.Invoke(this) ?? CommandResult.Dismiss();
}
}
}

View File

@@ -1,51 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;
namespace Microsoft.CmdPal.Common.Helpers;
/// <summary>
/// Well-known key chords used in the Command Palette and extensions.
/// </summary>
/// <remarks>
/// Assigned key chords should not conflict with system or application shortcuts.
/// However, the key chords in this class are not guaranteed to be unique and may conflict
/// with each other, especially when commands appear together in the same menu.
/// </remarks>
public static class WellKnownKeyChords
{
/// <summary>
/// Gets the well-known key chord for opening the file location. Shortcut: Ctrl+Shift+E.
/// </summary>
public static KeyChord OpenFileLocation { get; } = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: (int)VirtualKey.E);
/// <summary>
/// Gets the well-known key chord for copying the file path. Shortcut: Ctrl+Shift+C.
/// </summary>
public static KeyChord CopyFilePath { get; } = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: (int)VirtualKey.C);
/// <summary>
/// Gets the well-known key chord for opening the current location in a console. Shortcut: Ctrl+Shift+R.
/// </summary>
public static KeyChord OpenInConsole { get; } = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: (int)VirtualKey.R);
/// <summary>
/// Gets the well-known key chord for running the selected item as administrator. Shortcut: Ctrl+Shift+Enter.
/// </summary>
public static KeyChord RunAsAdministrator { get; } = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: (int)VirtualKey.Enter);
/// <summary>
/// Gets the well-known key chord for running the selected item as a different user. Shortcut: Ctrl+Shift+U.
/// </summary>
public static KeyChord RunAsDifferentUser { get; } = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: (int)VirtualKey.U);
/// <summary>
/// Gets the well-known key chord for toggling the pin state. Shortcut: Ctrl+P.
/// </summary>
public static KeyChord TogglePin { get; } = KeyChordHelpers.FromModifiers(ctrl: true, vkey: (int)VirtualKey.P);
}

View File

@@ -5,7 +5,6 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -118,46 +117,36 @@ public partial class ContextMenuViewModel : ObservableObject,
/// Generates a mapping of key -> command item for this particular item's
/// MoreCommands. (This won't include the primary Command, but it will
/// include the secondary one). This map can be used to quickly check if a
/// shortcut key was pressed. In case there are duplicate keybindings, the first
/// one is used and the rest are ignored.
/// shortcut key was pressed
/// </summary>
/// <returns>a dictionary of KeyChord -> Context commands, for all commands
/// that have a shortcut key set.</returns>
private Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
public Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
{
var result = new Dictionary<KeyChord, CommandContextItemViewModel>();
var menu = CurrentContextMenu;
if (menu is null)
if (CurrentContextMenu is null)
{
return result;
return [];
}
foreach (var item in menu)
{
if (item is CommandContextItemViewModel cmd && cmd.HasRequestedShortcut)
{
var key = cmd.RequestedShortcut ?? new KeyChord(0, 0, 0);
var added = result.TryAdd(key, cmd);
if (!added)
{
Logger.LogWarning($"Ignoring duplicate keyboard shortcut {KeyChordHelpers.FormatForDebug(key)} on command '{cmd.Title ?? cmd.Name ?? "(unknown)"}'");
}
}
}
return result;
return CurrentContextMenu
.OfType<CommandContextItemViewModel>()
.Where(c => c.HasRequestedShortcut)
.ToDictionary(
c => c.RequestedShortcut ?? new KeyChord(0, 0, 0),
c => c);
}
public ContextKeybindingResult? CheckKeybinding(bool ctrl, bool alt, bool shift, bool win, VirtualKey key)
{
var keybindings = Keybindings();
// Does the pressed key match any of the keybindings?
var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0);
if (keybindings.TryGetValue(pressedKeyChord, out var item))
if (keybindings is not null)
{
return InvokeCommand(item);
// Does the pressed key match any of the keybindings?
var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0);
if (keybindings.TryGetValue(pressedKeyChord, out var item))
{
return InvokeCommand(item);
}
}
return null;

View File

@@ -51,36 +51,6 @@ public abstract partial class ExtensionObjectViewModel : ObservableObject
DoOnUiThread(() => OnPropertyChanged(propertyName));
}
protected void UpdateProperty(string propertyName1, string propertyName2)
{
DoOnUiThread(() =>
{
OnPropertyChanged(propertyName1);
OnPropertyChanged(propertyName2);
});
}
protected void UpdateProperty(string propertyName1, string propertyName2, string propertyName3)
{
DoOnUiThread(() =>
{
OnPropertyChanged(propertyName1);
OnPropertyChanged(propertyName2);
OnPropertyChanged(propertyName3);
});
}
protected void UpdateProperty(params string[] propertyNames)
{
DoOnUiThread(() =>
{
foreach (var propertyName in propertyNames)
{
OnPropertyChanged(propertyName);
}
});
}
protected void ShowException(Exception ex, string? extensionHint = null)
{
if (PageContext.TryGetTarget(out var pageContext))

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
@@ -9,11 +10,14 @@ namespace Microsoft.CmdPal.Core.ViewModels;
public partial class FiltersViewModel : ExtensionObjectViewModel
{
private readonly ExtensionObject<IFilters> _filtersModel;
private readonly ExtensionObject<IFilters> _filtersModel = new(null);
public string CurrentFilterId { get; private set; } = string.Empty;
[ObservableProperty]
public partial string CurrentFilterId { get; set; } = string.Empty;
public IFilterItemViewModel[] Filters { get; private set; } = [];
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShouldShowFilters))]
public partial IFilterItemViewModel[] Filters { get; set; } = [];
public bool ShouldShowFilters => Filters.Length > 0;
@@ -30,11 +34,23 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
if (_filtersModel.Unsafe is not null)
{
var filters = _filtersModel.Unsafe.GetFilters();
Filters = BuildFilters(filters ?? []);
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters));
Filters = filters.Select<IFilterItem, IFilterItemViewModel>(filter =>
{
var filterItem = filter as IFilter;
if (filterItem != null)
{
var filterVM = new FilterItemViewModel(filterItem!, PageContext);
filterVM.InitializeProperties();
CurrentFilterId = _filtersModel.Unsafe.CurrentFilterId ?? string.Empty;
UpdateProperty(nameof(CurrentFilterId));
return filterVM;
}
else
{
return new SeparatorViewModel();
}
}).ToArray();
CurrentFilterId = _filtersModel.Unsafe.CurrentFilterId;
return;
}
@@ -45,27 +61,7 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
}
Filters = [];
UpdateProperty(nameof(Filters), nameof(ShouldShowFilters));
CurrentFilterId = string.Empty;
UpdateProperty(nameof(CurrentFilterId));
}
private IFilterItemViewModel[] BuildFilters(IFilterItem[] filters)
{
return [..filters.Select<IFilterItem, IFilterItemViewModel>(filter =>
{
if (filter is IFilter filterItem)
{
var filterItemViewModel = new FilterItemViewModel(filterItem!, PageContext);
filterItemViewModel.InitializeProperties();
return filterItemViewModel;
}
else
{
return new SeparatorViewModel();
}
})];
}
public override void SafeCleanup()
@@ -74,9 +70,9 @@ public partial class FiltersViewModel : ExtensionObjectViewModel
foreach (var filter in Filters)
{
if (filter is FilterItemViewModel filterItemViewModel)
if (filter is FilterItemViewModel filterVM)
{
filterItemViewModel.SafeCleanup();
filterVM.SafeCleanup();
}
}

View File

@@ -1,34 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Core.ViewModels;
public class GalleryGridPropertiesViewModel : IGridPropertiesViewModel
{
private readonly ExtensionObject<IGalleryGridLayout> _model;
public GalleryGridPropertiesViewModel(IGalleryGridLayout galleryGridLayout)
{
_model = new(galleryGridLayout);
}
public bool ShowTitle { get; set; }
public bool ShowSubtitle { get; set; }
public void InitializeProperties()
{
var model = _model.Unsafe;
if (model is null)
{
return; // throw?
}
ShowTitle = model.ShowTitle;
ShowSubtitle = model.ShowSubtitle;
}
}

View File

@@ -1,10 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.Core.ViewModels;
public interface IGridPropertiesViewModel
{
void InitializeProperties();
}

View File

@@ -45,10 +45,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
(!_isFetching) &&
IsLoading == false;
public bool IsGridView { get; private set; }
public IGridPropertiesViewModel? GridProperties { get; private set; }
// Remember - "observable" properties from the model (via PropChanged)
// cannot be marked [ObservableProperty]
public bool ShowDetails { get; private set; }
@@ -520,13 +516,6 @@ public partial class ListViewModel : PageViewModel, IDisposable
_isDynamic = model is IDynamicListPage;
IsGridView = model.GridProperties is not null;
UpdateProperty(nameof(IsGridView));
GridProperties = LoadGridPropertiesViewModel(model.GridProperties);
GridProperties?.InitializeProperties();
UpdateProperty(nameof(GridProperties));
ShowDetails = model.ShowDetails;
UpdateProperty(nameof(ShowDetails));
@@ -548,27 +537,9 @@ public partial class ListViewModel : PageViewModel, IDisposable
model.ItemsChanged += Model_ItemsChanged;
}
private IGridPropertiesViewModel? LoadGridPropertiesViewModel(IGridProperties? gridProperties)
{
if (gridProperties is IMediumGridLayout mediumGridLayout)
{
return new MediumGridPropertiesViewModel(mediumGridLayout);
}
else if (gridProperties is IGalleryGridLayout galleryGridLayout)
{
return new GalleryGridPropertiesViewModel(galleryGridLayout);
}
else if (gridProperties is ISmallGridLayout smallGridLayout)
{
return new SmallGridPropertiesViewModel(smallGridLayout);
}
return null;
}
public void LoadMoreIfNeeded()
{
var model = _model.Unsafe;
var model = this._model.Unsafe;
if (model is null)
{
return;
@@ -612,7 +583,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
{
base.FetchProperty(propertyName);
var model = _model.Unsafe;
var model = this._model.Unsafe;
if (model is null)
{
return; // throw?
@@ -620,20 +591,14 @@ public partial class ListViewModel : PageViewModel, IDisposable
switch (propertyName)
{
case nameof(GridProperties):
IsGridView = model.GridProperties is not null;
GridProperties = LoadGridPropertiesViewModel(model.GridProperties);
GridProperties?.InitializeProperties();
UpdateProperty(nameof(IsGridView));
break;
case nameof(ShowDetails):
ShowDetails = model.ShowDetails;
this.ShowDetails = model.ShowDetails;
break;
case nameof(PlaceholderText):
_modelPlaceholderText = model.PlaceholderText;
this._modelPlaceholderText = model.PlaceholderText;
break;
case nameof(SearchText):
SearchText = model.SearchText;
this.SearchText = model.SearchText;
break;
case nameof(EmptyContent):
EmptyContent = new(new(model.EmptyContent), PageContext);

View File

@@ -1,31 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Core.ViewModels;
public class MediumGridPropertiesViewModel : IGridPropertiesViewModel
{
private readonly ExtensionObject<IMediumGridLayout> _model;
public MediumGridPropertiesViewModel(IMediumGridLayout mediumGridLayout)
{
_model = new(mediumGridLayout);
}
public bool ShowTitle { get; set; }
public void InitializeProperties()
{
var model = _model.Unsafe;
if (model is null)
{
return; // throw?
}
ShowTitle = model.ShowTitle;
}
}

View File

@@ -3,9 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.ComponentModel;
using ManagedCommon;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
@@ -34,28 +32,12 @@ public interface IContextMenuContext : INotifyPropertyChanged
/// that have a shortcut key set.</returns>
public Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
{
var result = new Dictionary<KeyChord, CommandContextItemViewModel>();
var menu = MoreCommands;
if (menu is null)
{
return result;
}
foreach (var item in menu)
{
if (item is CommandContextItemViewModel cmd && cmd.HasRequestedShortcut)
{
var key = cmd.RequestedShortcut ?? new KeyChord(0, 0, 0);
var added = result.TryAdd(key, cmd);
if (!added)
{
Logger.LogWarning($"Ignoring duplicate keyboard shortcut {KeyChordHelpers.FormatForDebug(key)} on command '{cmd.Title ?? cmd.Name ?? "(unknown)"}'");
}
}
}
return result;
return MoreCommands
.OfType<CommandContextItemViewModel>()
.Where(c => c.HasRequestedShortcut)
.ToDictionary(
c => c.RequestedShortcut ?? new KeyChord(0, 0, 0),
c => c);
}
}

View File

@@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Core.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
namespace Microsoft.CmdPal.Core.ViewModels;
public class SmallGridPropertiesViewModel : IGridPropertiesViewModel
{
private readonly ExtensionObject<ISmallGridLayout> _model;
public SmallGridPropertiesViewModel(ISmallGridLayout smallGridLayout)
{
_model = new(smallGridLayout);
}
public void InitializeProperties()
{
}
}

View File

@@ -94,4 +94,16 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<!-- Just mark it as AOT compatible. Do not publish with AOT now. We need fully test before we really publish it as AOT enabled-->
<!--<PropertyGroup>
<SelfContained>true</SelfContained>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<PublishTrimmed>true</PublishTrimmed>
<PublishSingleFile>true</PublishSingleFile>
--><!-- <DisableRuntimeMarshalling>true</DisableRuntimeMarshalling> --><!--
<PublishAot>true</PublishAot>
<EnableMsixTooling>true</EnableMsixTooling>
</PropertyGroup>-->
</Project>

View File

@@ -42,8 +42,6 @@ public partial class SettingsModel : ObservableObject
public bool IgnoreShortcutWhenFullscreen { get; set; }
public bool AllowExternalReload { get; set; }
public Dictionary<string, ProviderSettings> ProviderSettings { get; set; } = [];
public Dictionary<string, CommandAlias> Aliases { get; set; } = [];

View File

@@ -38,16 +38,6 @@ public partial class SettingsViewModel : INotifyPropertyChanged
}
}
public bool AllowExternalReload
{
get => _settings.AllowExternalReload;
set
{
_settings.AllowExternalReload = value;
Save();
}
}
public bool ShowAppDetails
{
get => _settings.ShowAppDetails;

View File

@@ -85,7 +85,7 @@ public partial class App : Application
AppWindow = new MainWindow();
var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);
((MainWindow)AppWindow).HandleLaunch(activatedEventArgs);
}
/// <summary>

View File

@@ -134,15 +134,6 @@ public sealed partial class CommandBar : UserControl,
WeakReferenceMessenger.Default.Send<OpenContextMenuMessage>(new OpenContextMenuMessage(null, null, null, ContextMenuFilterLocation.Bottom));
}
/// <summary>
/// Sets focus to the "More" button after closing the context menu,
/// keeping keyboard navigation intuitive.
/// </summary>
public void FocusMoreCommandsButton()
{
MoreCommandsButton?.Focus(FocusState.Programmatic);
}
private void ContextMenuFlyout_Opened(object sender, object e)
{
// We need to wait until our flyout is opened to try and toss focus

View File

@@ -15,7 +15,6 @@
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
Background="Transparent"
PreviewKeyDown="UserControl_PreviewKeyDown"
mc:Ignorable="d">
<UserControl.Resources>

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Messages;
@@ -116,24 +115,6 @@ public sealed partial class ContextMenu : UserControl,
}
}
/// <summary>
/// Handles Escape to close the context menu and return focus to the "More" button.
/// </summary>
private void UserControl_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Escape)
{
// Close the context menu (if not already handled)
WeakReferenceMessenger.Default.Send(new CloseContextMenuMessage());
// Find the parent CommandBar and set focus to MoreCommandsButton
var parent = this.FindParent<CommandBar>();
parent?.FocusMoreCommandsButton();
e.Handled = true;
}
}
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var prop = e.PropertyName;

View File

@@ -59,7 +59,7 @@
<Rectangle
Height="1"
Margin="-16,-12,-12,-12"
Fill="{ThemeResource MenuFlyoutSeparatorBackground}" />
Fill="{ThemeResource MenuFlyoutSeparatorThemeBrush}" />
</DataTemplate>
</ResourceDictionary>
@@ -68,14 +68,11 @@
<ComboBox
Name="FiltersComboBox"
x:Uid="FiltersComboBox"
MinWidth="200"
VerticalAlignment="Center"
ItemTemplateSelector="{StaticResource FilterTemplateSelector}"
ItemsSource="{x:Bind ViewModel.Filters, Mode=OneWay}"
PlaceholderText="Filters"
PreviewKeyDown="FiltersComboBox_PreviewKeyDown"
SelectedValue="{x:Bind ViewModel.CurrentFilterId, Mode=OneWay}"
SelectedValuePath="Id"
SelectionChanged="FiltersComboBox_SelectionChanged"
Style="{StaticResource ComboBoxStyle}"
Visibility="{x:Bind ViewModel.ShouldShowFilters, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">

View File

@@ -2,10 +2,8 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using ManagedCommon;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.UI.Deferred;
using Microsoft.Terminal.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -57,8 +55,6 @@ public partial class IconBox : ContentControl
{
TabFocusNavigation = KeyboardNavigationMode.Once;
IsTabStop = false;
HorizontalContentAlignment = HorizontalAlignment.Center;
VerticalContentAlignment = VerticalAlignment.Center;
}
private static void OnSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
@@ -79,8 +75,6 @@ public partial class IconBox : ContentControl
IconSourceElement elem = new()
{
IconSource = fontIco,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
};
@this.Content = elem;
break;
@@ -104,20 +98,14 @@ public partial class IconBox : ContentControl
else
{
// TODO GH #239 switch back when using the new MD text block
// Switching back to EnqueueAsync has broken icons in tags (they don't show)
// _ = @this._queue.EnqueueAsync(() =>
@this._queue.TryEnqueue(async void () =>
@this._queue.TryEnqueue(new(async () =>
{
try
var requestedTheme = @this.ActualTheme;
var eventArgs = new SourceRequestedEventArgs(e.NewValue, requestedTheme);
if (@this.SourceRequested is not null)
{
if (@this.SourceRequested is null)
{
return;
}
var requestedTheme = @this.ActualTheme;
var eventArgs = new SourceRequestedEventArgs(e.NewValue, requestedTheme);
await @this.SourceRequested.InvokeAsync(@this, eventArgs);
// After the await:
@@ -142,35 +130,37 @@ public partial class IconBox : ContentControl
// So, if the icon we get back was a font icon,
// and the glyph for that icon is NOT in the range of
// Segoe icons, then let's give the icon some extra space
var iconData = eventArgs.Key switch
{
IconDataViewModel key => key,
IconInfoViewModel info => requestedTheme == ElementTheme.Light ? info.Light : info.Dark,
_ => null,
};
@this.Padding = new Thickness(0);
if (iconData?.Icon is not null && @this.Source is FontIconSource)
IconDataViewModel? iconData = null;
if (eventArgs.Key is IconDataViewModel)
{
var iconSize =
!double.IsNaN(@this.Width) ? @this.Width :
!double.IsNaN(@this.Height) ? @this.Height :
@this.ActualWidth > 0 ? @this.ActualWidth :
@this.ActualHeight;
@this.Padding = new Thickness(Math.Round(iconSize * -0.2));
iconData = eventArgs.Key as IconDataViewModel;
}
else
else if (eventArgs.Key is IconInfoViewModel info)
{
@this.Padding = default;
iconData = requestedTheme == ElementTheme.Light ? info.Light : info.Dark;
}
if (iconData is not null &&
@this.Source is FontIconSource)
{
if (!string.IsNullOrEmpty(iconData.Icon) && iconData.Icon.Length <= 2)
{
var ch = iconData.Icon[0];
// The range of MDL2 Icons isn't explicitly defined, but
// we're using this based off the table on:
// https://docs.microsoft.com/en-us/windows/uwp/design/style/segoe-ui-symbol-font
var isMDL2Icon = ch is >= '\uE700' and <= '\uF8FF';
if (!isMDL2Icon)
{
@this.Padding = new Thickness(-4);
}
}
}
}
catch (Exception ex)
{
// Exception from TryEnqueue bypasses the global error handler,
// and crashes the app.
Logger.LogError("Failed to set icon", ex);
}
});
}));
}
}
}

View File

@@ -1,20 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
namespace Microsoft.CmdPal.UI.Controls;
public sealed partial class IconMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// Only include a margin if there is text to separate from the icon.
var text = value as string;
return string.IsNullOrEmpty(text) ? new Thickness(0) : new Thickness(0, 0, 4, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException();
}

View File

@@ -131,11 +131,6 @@ public sealed partial class SearchBar : UserControl,
WeakReferenceMessenger.Default.Send<OpenContextMenuMessage>(new OpenContextMenuMessage(null, null, null, ContextMenuFilterLocation.Bottom));
e.Handled = true;
}
else if (ctrlPressed && e.Key == VirtualKey.I)
{
// Today you learned that Ctrl+I in a TextBox will insert a tab
e.Handled = true;
}
else if (e.Key == VirtualKey.Escape)
{
if (string.IsNullOrEmpty(FilterBox.Text))

View File

@@ -27,8 +27,6 @@
<Thickness x:Key="TagPadding">4,2,4,2</Thickness>
<Thickness x:Key="TagBorderThickness">1</Thickness>
<local:IconMarginConverter x:Key="IconMarginConverter" />
<Style BasedOn="{StaticResource DefaultTagStyle}" TargetType="local:Tag" />
<Style x:Key="DefaultTagStyle" TargetType="local:Tag">
@@ -73,7 +71,7 @@
x:Name="PART_Icon"
Grid.Column="0"
Height="12"
Margin="{Binding Text, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource IconMarginConverter}}"
Margin="0,0,4,0"
SourceKey="{TemplateBinding Icon}" />
<TextBlock
Grid.Column="1"

View File

@@ -2,7 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.CodeAnalysis;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -15,7 +14,6 @@ internal sealed partial class FilterTemplateSelector : DataTemplateSelector
public DataTemplate? Separator { get; set; }
[DynamicDependency(DynamicallyAccessedMemberTypes.All, "Microsoft.UI.Xaml.Controls.ComboBoxItem", "Microsoft.WinUI")]
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject dependencyObject)
{
DataTemplate? dataTemplate = Default;

View File

@@ -1,40 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.CmdPal.UI;
internal sealed partial class GridItemTemplateSelector : DataTemplateSelector
{
public IGridPropertiesViewModel? GridProperties { get; set; }
public DataTemplate? Small { get; set; }
public DataTemplate? Medium { get; set; }
public DataTemplate? Gallery { get; set; }
protected override DataTemplate? SelectTemplateCore(object item, DependencyObject dependencyObject)
{
DataTemplate? dataTemplate = Medium;
if (GridProperties is SmallGridPropertiesViewModel)
{
dataTemplate = Small;
}
else if (GridProperties is MediumGridPropertiesViewModel)
{
dataTemplate = Medium;
}
else if (GridProperties is GalleryGridPropertiesViewModel)
{
dataTemplate = Gallery;
}
return dataTemplate;
}
}

View File

@@ -3,7 +3,6 @@
x:Class="Microsoft.CmdPal.UI.ListPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cmdpalUI="using:Microsoft.CmdPal.UI"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
xmlns:coreViewModels="using:Microsoft.CmdPal.Core.ViewModels"
@@ -12,11 +11,8 @@
xmlns:help="using:Microsoft.CmdPal.UI.Helpers"
xmlns:local="using:Microsoft.CmdPal.UI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
x:Name="PageRoot"
Background="Transparent"
DataContext="{x:Bind ViewModel, Mode=OneWay}"
mc:Ignorable="d">
<Page.Resources>
@@ -27,7 +23,6 @@
IsSourceGrouped="True"
Source="{x:Bind ViewModel.Items, Mode=OneWay}" />-->
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:StringVisibilityConverter
x:Key="StringVisibilityConverter"
EmptyValue="Collapsed"
@@ -44,14 +39,6 @@
ToolTipService.ToolTip="{x:Bind ToolTip, Mode=OneWay}" />
</DataTemplate>
<cmdpalUI:GridItemTemplateSelector
x:Key="GridItemTemplateSelector"
x:DataType="coreViewModels:ListItemViewModel"
Gallery="{StaticResource GalleryGridItemViewModelTemplate}"
GridProperties="{x:Bind ViewModel.GridProperties}"
Medium="{StaticResource MediumGridItemViewModelTemplate}"
Small="{StaticResource SmallGridItemViewModelTemplate}" />
<!-- https://learn.microsoft.com/windows/apps/design/controls/itemsview#specify-the-look-of-the-items -->
<DataTemplate x:Key="ListItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
<Grid
@@ -115,145 +102,6 @@
</ItemsControl>
</Grid>
</DataTemplate>
<!-- Grid item templates for visual grid representation -->
<DataTemplate x:Key="SmallGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
<StackPanel
Width="60"
Height="60"
Padding="8,16"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind Title}"
BorderThickness="0"
CornerRadius="8"
Orientation="Vertical"
ToolTipService.ToolTip="{x:Bind Title}">
<cpcontrols:IconBox
x:Name="GridIconBorder"
Width="28"
Height="28"
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorPrimary}"
SourceKey="{x:Bind Icon, Mode=OneWay}"
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="MediumGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
<StackPanel
Width="100"
Height="100"
Padding="8,16"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind Title}"
BorderThickness="0"
CornerRadius="8"
Orientation="Vertical"
ToolTipService.ToolTip="{x:Bind Title}">
<cpcontrols:IconBox
x:Name="GridIconBorder"
Width="36"
Height="36"
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CharacterSpacing="12"
FontSize="14"
Foreground="{ThemeResource TextFillColorPrimary}"
SourceKey="{x:Bind Icon, Mode=OneWay}"
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
<TextBlock
x:Name="TitleTextBlock"
MaxHeight="40"
Margin="0,8,0,4"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CharacterSpacing="12"
FontSize="12"
Text="{x:Bind Title}"
TextAlignment="Center"
TextTrimming="WordEllipsis"
TextWrapping="Wrap"
Visibility="{Binding ElementName=PageRoot, Path=DataContext.GridProperties.ShowTitle, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="GalleryGridItemViewModelTemplate" x:DataType="coreViewModels:ListItemViewModel">
<StackPanel
Width="160"
Margin="4"
Padding="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind Title}"
BorderThickness="0"
CornerRadius="4"
Orientation="Vertical"
ToolTipService.ToolTip="{x:Bind Title}">
<Grid
Width="160"
Height="160"
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CornerRadius="4">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Viewbox
Grid.Row="1"
HorizontalAlignment="Center"
Stretch="UniformToFill"
StretchDirection="Both">
<cpcontrols:IconBox
CornerRadius="4"
Foreground="{ThemeResource TextFillColorPrimary}"
SourceKey="{x:Bind Icon, Mode=OneWay}"
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" />
</Viewbox>
</Grid>
<StackPanel Padding="4" Orientation="Vertical">
<TextBlock
x:Name="TitleTextBlock"
MaxWidth="152"
MaxHeight="40"
Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
CharacterSpacing="12"
FontSize="14"
Foreground="{ThemeResource TextFillColorPrimary}"
Text="{x:Bind Title}"
TextAlignment="Center"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap" />
<TextBlock
x:Name="SubTitleTextBlock"
MaxWidth="152"
MaxHeight="40"
Margin="0,4,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
CharacterSpacing="11"
FontSize="11"
Foreground="{ThemeResource TextFillColorTertiary}"
Text="{x:Bind Subtitle}"
TextAlignment="Center"
TextTrimming="WordEllipsis"
TextWrapping="NoWrap" />
</StackPanel>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
@@ -262,49 +110,34 @@
TargetType="x:Boolean"
Value="{x:Bind ViewModel.ShowEmptyContent, Mode=OneWay}">
<controls:Case Value="False">
<controls:SwitchPresenter
HorizontalAlignment="Stretch"
TargetType="x:Boolean"
Value="{x:Bind ViewModel.IsGridView, Mode=OneWay}">
<controls:Case Value="False">
<ListView
x:Name="ItemsList"
Padding="0,2,0,0"
ContextCanceled="Items_OnContextCanceled"
ContextRequested="Items_OnContextRequested"
DoubleTapped="Items_DoubleTapped"
IsDoubleTapEnabled="True"
IsItemClickEnabled="True"
ItemClick="Items_ItemClick"
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
RightTapped="Items_RightTapped"
SelectionChanged="Items_SelectionChanged">
<ListView.ItemContainerTransitions>
<TransitionCollection />
</ListView.ItemContainerTransitions>
</ListView>
</controls:Case>
<controls:Case Value="True">
<GridView
x:Name="ItemsGrid"
Padding="8"
ContextCanceled="Items_OnContextCanceled"
ContextRequested="Items_OnContextRequested"
DoubleTapped="Items_DoubleTapped"
IsDoubleTapEnabled="True"
IsItemClickEnabled="True"
ItemClick="Items_ItemClick"
ItemTemplateSelector="{StaticResource GridItemTemplateSelector}"
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
RightTapped="Items_RightTapped"
SelectionChanged="Items_SelectionChanged">
<GridView.ItemContainerTransitions>
<TransitionCollection />
</GridView.ItemContainerTransitions>
</GridView>
</controls:Case>
</controls:SwitchPresenter>
<ListView
x:Name="ItemsList"
Padding="0,2,0,0"
ContextCanceled="ItemsList_OnContextCanceled"
ContextRequested="ItemsList_OnContextRequested"
DoubleTapped="ItemsList_DoubleTapped"
IsDoubleTapEnabled="True"
IsItemClickEnabled="True"
ItemClick="ItemsList_ItemClick"
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
SelectionChanged="ItemsList_SelectionChanged">
<ListView.ItemContainerTransitions>
<TransitionCollection />
</ListView.ItemContainerTransitions>
<!--<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock
Margin="0,16,0,0"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Key, Mode=OneWay}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>-->
</ListView>
</controls:Case>
<controls:Case Value="True">
<StackPanel
@@ -337,5 +170,6 @@
</StackPanel>
</controls:Case>
</controls:SwitchPresenter>
</Grid>
</Page>

View File

@@ -13,7 +13,6 @@ using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
@@ -39,21 +38,13 @@ public sealed partial class ListPage : Page,
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(nameof(ViewModel), typeof(ListViewModel), typeof(ListPage), new PropertyMetadata(null, OnViewModelChanged));
private ListViewBase ItemView
{
get
{
return ViewModel?.IsGridView == true ? ItemsGrid : ItemsList;
}
}
public ListPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Disabled;
this.ItemView.Loaded += Items_Loaded;
this.ItemView.PreviewKeyDown += Items_PreviewKeyDown;
this.ItemView.PointerPressed += Items_PointerPressed;
this.ItemsList.Loaded += ItemsList_Loaded;
this.ItemsList.PreviewKeyDown += ItemsList_PreviewKeyDown;
this.ItemsList.PointerPressed += ItemsList_PointerPressed;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
@@ -64,11 +55,11 @@ public sealed partial class ListPage : Page,
}
if (e.NavigationMode == NavigationMode.Back
|| (e.NavigationMode == NavigationMode.New && ItemView.Items.Count > 0))
|| (e.NavigationMode == NavigationMode.New && ItemsList.Items.Count > 0))
{
// Upon navigating _back_ to this page, immediately select the
// first item in the list
ItemView.SelectedIndex = 0;
ItemsList.SelectedIndex = 0;
}
// RegisterAll isn't AOT compatible
@@ -99,6 +90,7 @@ public sealed partial class ListPage : Page,
{
ViewModel?.SafeCleanup();
CleanupHelper.Cleanup(this);
Bindings.StopTracking();
}
// Clean-up event listeners
@@ -108,7 +100,7 @@ public sealed partial class ListPage : Page,
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
private void Items_ItemClick(object sender, ItemClickEventArgs e)
private void ItemsList_ItemClick(object sender, ItemClickEventArgs e)
{
if (e.ClickedItem is ListItemViewModel item)
{
@@ -131,9 +123,9 @@ public sealed partial class ListPage : Page,
}
}
private void Items_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
private void ItemsList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
if (ItemView.SelectedItem is ListItemViewModel vm)
if (ItemsList.SelectedItem is ListItemViewModel vm)
{
var settings = App.Current.Services.GetService<SettingsModel>()!;
if (!settings.SingleClickActivates)
@@ -144,10 +136,10 @@ public sealed partial class ListPage : Page,
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
private void Items_SelectionChanged(object sender, SelectionChangedEventArgs e)
private void ItemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var vm = ViewModel;
var li = ItemView.SelectedItem as ListItemViewModel;
var li = ItemsList.SelectedItem as ListItemViewModel;
_ = Task.Run(() =>
{
vm?.UpdateSelectedItemCommand.Execute(li);
@@ -162,12 +154,12 @@ public sealed partial class ListPage : Page,
// here, then in Page_ItemsUpdated trying to select that cached item if
// it's in the list (otherwise, clear the cache), but that seems
// aggressively BODGY for something that mostly just works today.
if (ItemView.SelectedItem is not null)
if (ItemsList.SelectedItem is not null)
{
ItemView.ScrollIntoView(ItemView.SelectedItem);
ItemsList.ScrollIntoView(ItemsList.SelectedItem);
// Automation notification for screen readers
var listViewPeer = Microsoft.UI.Xaml.Automation.Peers.ListViewAutomationPeer.CreatePeerForElement(ItemView);
var listViewPeer = Microsoft.UI.Xaml.Automation.Peers.ListViewAutomationPeer.CreatePeerForElement(ItemsList);
if (listViewPeer is not null && li is not null)
{
var notificationText = li.Title;
@@ -180,37 +172,10 @@ public sealed partial class ListPage : Page,
}
}
private void Items_RightTapped(object sender, RightTappedRoutedEventArgs e)
private void ItemsList_Loaded(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is FrameworkElement element &&
element.DataContext is ListItemViewModel item)
{
if (ItemView.SelectedItem != item)
{
ItemView.SelectedItem = item;
}
ViewModel?.UpdateSelectedItemCommand.Execute(item);
var pos = e.GetPosition(element);
_ = DispatcherQueue.TryEnqueue(
() =>
{
WeakReferenceMessenger.Default.Send<OpenContextMenuMessage>(
new OpenContextMenuMessage(
element,
Microsoft.UI.Xaml.Controls.Primitives.FlyoutPlacementMode.BottomEdgeAlignedLeft,
pos,
ContextMenuFilterLocation.Top));
});
}
}
private void Items_Loaded(object sender, RoutedEventArgs e)
{
// Find the ScrollViewer in the ItemView (ItemsList or ItemsGrid)
var listViewScrollViewer = FindScrollViewer(this.ItemView);
// Find the ScrollViewer in the ListView
var listViewScrollViewer = FindScrollViewer(this.ItemsList);
if (listViewScrollViewer is not null)
{
@@ -242,25 +207,25 @@ public sealed partial class ListPage : Page,
// And then have these commands manipulate that state being bound to the UI instead
// We may want to see how other non-list UIs need to behave to make this decision
// At least it's decoupled from the SearchBox now :)
if (ItemView.SelectedIndex < ItemView.Items.Count - 1)
if (ItemsList.SelectedIndex < ItemsList.Items.Count - 1)
{
ItemView.SelectedIndex++;
ItemsList.SelectedIndex++;
}
else
{
ItemView.SelectedIndex = 0;
ItemsList.SelectedIndex = 0;
}
}
public void Receive(NavigatePreviousCommand message)
{
if (ItemView.SelectedIndex > 0)
if (ItemsList.SelectedIndex > 0)
{
ItemView.SelectedIndex--;
ItemsList.SelectedIndex--;
}
else
{
ItemView.SelectedIndex = ItemView.Items.Count - 1;
ItemsList.SelectedIndex = ItemsList.Items.Count - 1;
}
}
@@ -270,7 +235,7 @@ public sealed partial class ListPage : Page,
{
ViewModel?.InvokeItemCommand.Execute(null);
}
else if (ItemView.SelectedItem is ListItemViewModel item)
else if (ItemsList.SelectedItem is ListItemViewModel item)
{
ViewModel?.InvokeItemCommand.Execute(item);
}
@@ -282,7 +247,7 @@ public sealed partial class ListPage : Page,
{
ViewModel?.InvokeSecondaryCommandCommand.Execute(null);
}
else if (ItemView.SelectedItem is ListItemViewModel item)
else if (ItemsList.SelectedItem is ListItemViewModel item)
{
ViewModel?.InvokeSecondaryCommandCommand.Execute(item);
}
@@ -318,19 +283,19 @@ public sealed partial class ListPage : Page,
//
// It's important to do this here, because once there's no selection
// (which can happen as the list updates) we won't get an
// ItemView_SelectionChanged again to give us another chance to change
// ItemsList_SelectionChanged again to give us another chance to change
// the selection from null -> something. Better to just update the
// selection once, at the end of all the updating.
if (ItemView.SelectedItem is null)
if (ItemsList.SelectedItem is null)
{
ItemView.SelectedIndex = 0;
ItemsList.SelectedIndex = 0;
}
// Always reset the selected item when the top-level list page changes
// its items
if (!sender.IsNested)
{
ItemView.SelectedIndex = 0;
ItemsList.SelectedIndex = 0;
}
}
@@ -339,7 +304,7 @@ public sealed partial class ListPage : Page,
var prop = e.PropertyName;
if (prop == nameof(ViewModel.FilteredItems))
{
Debug.WriteLine($"ViewModel.FilteredItems {ItemView.SelectedItem}");
Debug.WriteLine($"ViewModel.FilteredItems {ItemsList.SelectedItem}");
}
}
@@ -363,12 +328,12 @@ public sealed partial class ListPage : Page,
return null;
}
private void Items_OnContextRequested(UIElement sender, ContextRequestedEventArgs e)
private void ItemsList_OnContextRequested(UIElement sender, ContextRequestedEventArgs e)
{
var (item, element) = e.OriginalSource switch
{
// caused by keyboard shortcut (e.g. Context menu key or Shift+F10)
SelectorItem selectorItem => (ItemView.ItemFromContainer(selectorItem) as ListItemViewModel, selectorItem),
ListViewItem listViewItem => (ItemsList.ItemFromContainer(listViewItem) as ListItemViewModel, listViewItem),
// caused by right-click on the ListViewItem
FrameworkElement { DataContext: ListItemViewModel itemViewModel } frameworkElement => (itemViewModel, frameworkElement),
@@ -381,9 +346,9 @@ public sealed partial class ListPage : Page,
return;
}
if (ItemView.SelectedItem != item)
if (ItemsList.SelectedItem != item)
{
ItemView.SelectedItem = item;
ItemsList.SelectedItem = item;
}
ViewModel?.UpdateSelectedItemCommand.Execute(item);
@@ -406,14 +371,14 @@ public sealed partial class ListPage : Page,
e.Handled = true;
}
private void Items_OnContextCanceled(UIElement sender, RoutedEventArgs e)
private void ItemsList_OnContextCanceled(UIElement sender, RoutedEventArgs e)
{
_ = DispatcherQueue.TryEnqueue(() => WeakReferenceMessenger.Default.Send<CloseContextMenuMessage>());
}
private void Items_PointerPressed(object sender, PointerRoutedEventArgs e) => _lastInputSource = InputSource.Pointer;
private void ItemsList_PointerPressed(object sender, PointerRoutedEventArgs e) => _lastInputSource = InputSource.Pointer;
private void Items_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
private void ItemsList_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key is VirtualKey.Enter or VirtualKey.Space)
{

View File

@@ -123,9 +123,6 @@ public sealed partial class MainWindow : WindowEx,
_localKeyboardListener = new LocalKeyboardListener();
_localKeyboardListener.KeyPressed += LocalKeyboardListener_OnKeyPressed;
_localKeyboardListener.Start();
// Force window to be created, and then cloaked. This will offset initial animation when the window is shown.
HideWindow();
}
private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
@@ -236,6 +233,9 @@ public sealed partial class MainWindow : WindowEx,
{
var hwnd = new HWND(hwndValue != 0 ? hwndValue : _hwnd);
// Make sure our HWND is cloaked before any possible window manipulations
Cloak();
// Remember, IsIconic == "minimized", which is entirely different state
// from "show/hide"
// If we're currently minimized, restore us first, before we reveal
@@ -243,9 +243,6 @@ public sealed partial class MainWindow : WindowEx,
// which would remain not visible to the user.
if (PInvoke.IsIconic(hwnd))
{
// Make sure our HWND is cloaked before any possible window manipulations
Cloak();
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
}
@@ -484,13 +481,8 @@ public sealed partial class MainWindow : WindowEx,
}
}
public void HandleLaunchNonUI(AppActivationArguments? activatedEventArgs)
public void HandleLaunch(AppActivationArguments? activatedEventArgs)
{
// LOAD BEARING
// Any reading and processing of the activation arguments must be done
// synchronously in this method, before it returns. The sending instance
// remains blocked until this returns; afterward it may quit, causing
// the activation arguments to be lost.
if (activatedEventArgs is null)
{
Summon(string.Empty);
@@ -521,47 +513,15 @@ public sealed partial class MainWindow : WindowEx,
WeakReferenceMessenger.Default.Send<OpenSettingsMessage>(new());
return;
}
else if (uri.StartsWith("x-cmdpal://reload", StringComparison.OrdinalIgnoreCase))
{
var settings = App.Current.Services.GetService<SettingsModel>();
if (settings?.AllowExternalReload == true)
{
Logger.LogInfo("External Reload triggered");
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
}
else
{
Logger.LogInfo("External Reload is disabled");
}
return;
}
}
}
}
}
catch (COMException ex)
{
// https://learn.microsoft.com/en-us/windows/win32/rpc/rpc-return-values
const int RPC_S_SERVER_UNAVAILABLE = -2147023174;
const int RPC_S_CALL_FAILED = 2147023170;
// Accessing properties activatedEventArgs.Kind and activatedEventArgs.Data might cause COMException
// if the args are not valid or not passed correctly.
if (ex.HResult is RPC_S_SERVER_UNAVAILABLE or RPC_S_CALL_FAILED)
{
Logger.LogWarning(
$"COM exception (HRESULT {ex.HResult}) when accessing activation arguments. " +
$"This might be due to the calling application not passing them correctly or exiting before we could read them. " +
$"The application will continue running and fall back to showing the Command Palette window.");
}
else
{
Logger.LogError(
$"COM exception (HRESULT {ex.HResult}) when activating the application. " +
$"The application will continue running and fall back to showing the Command Palette window.",
ex);
}
Logger.LogError("COM exception when activating the application", ex);
}
Summon(string.Empty);
@@ -650,20 +610,6 @@ public sealed partial class MainWindow : WindowEx,
}
private void HandleSummon(string commandId)
{
if (_ignoreHotKeyWhenFullScreen)
{
// If we're in full screen mode, ignore the hotkey
if (WindowHelper.IsWindowFullscreen())
{
return;
}
}
HandleSummonCore(commandId);
}
private void HandleSummonCore(string commandId)
{
var isRootHotkey = string.IsNullOrEmpty(commandId);
PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey));
@@ -688,6 +634,8 @@ public sealed partial class MainWindow : WindowEx,
// so that we can bind hotkeys to individual commands
if (!isVisible || !isRootHotkey)
{
Activate();
Summon(commandId);
}
else if (isRootHotkey)
@@ -723,6 +671,15 @@ public sealed partial class MainWindow : WindowEx,
var hotkeyIndex = (int)wParam.Value;
if (hotkeyIndex < _hotkeys.Count)
{
if (_ignoreHotKeyWhenFullScreen)
{
// If we're in full screen mode, ignore the hotkey
if (WindowHelper.IsWindowFullscreen())
{
return (LRESULT)IntPtr.Zero;
}
}
var hotkey = _hotkeys[hotkeyIndex];
HandleSummon(hotkey.CommandId);
}

View File

@@ -39,8 +39,6 @@
<PropertyGroup Condition="'$(CIBuild)'=='true'">
<GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
<AppxBundle>Never</AppxBundle>
<AppxPackageTestDir>$(OutputPath)\AppPackages\Microsoft.CmdPal.UI_$(Version)_Test\</AppxPackageTestDir>
</PropertyGroup>
<PropertyGroup>

View File

@@ -327,6 +327,11 @@
x:Name="FiltersDropDown"
HorizontalAlignment="Right"
CurrentPageViewModel="{x:Bind ViewModel.CurrentPage, Mode=OneWay}" />
<Grid.Transitions>
<TransitionCollection>
<RepositionThemeTransition />
</TransitionCollection>
</Grid.Transitions>
</Grid>
</Grid>

View File

@@ -107,33 +107,12 @@ internal sealed class Program
{
// Do the redirection on another thread, and use a non-blocking
// wait method to wait for the redirection to complete.
using var redirectSemaphore = new Semaphore(0, 1);
var redirectTimeout = TimeSpan.FromSeconds(32);
_ = Task.Run(() =>
var redirectSemaphore = new Semaphore(0, 1);
Task.Run(() =>
{
using var cts = new CancellationTokenSource(redirectTimeout);
try
{
keyInstance.RedirectActivationToAsync(args)
.AsTask(cts.Token)
.GetAwaiter()
.GetResult();
}
catch (OperationCanceledException)
{
Logger.LogError($"Failed to activate existing instance; timed out after {redirectTimeout}.");
}
catch (Exception ex)
{
Logger.LogError("Failed to activate existing instance", ex);
}
finally
{
redirectSemaphore.Release();
}
keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
redirectSemaphore.Release();
});
_ = PInvoke.CoWaitForMultipleObjects(
(uint)CWMO_FLAGS.CWMO_DEFAULT,
PInvoke.INFINITE,
@@ -145,14 +124,13 @@ internal sealed class Program
{
// If we already have a form, display the message now.
// Otherwise, add it to the collection for displaying later.
if (App.Current?.AppWindow is MainWindow mainWindow)
if (App.Current is App thisApp)
{
// LOAD BEARING
// This must be synchronous to ensure the method does not return
// before the activation is fully handled and the parameters are processed.
// The sending instance remains blocked until this returns; afterward it may quit,
// causing the activation arguments to be lost.
mainWindow.HandleLaunchNonUI(args);
if (thisApp.AppWindow is not null and
MainWindow mainWindow)
{
uiContext?.Post(_ => mainWindow.HandleLaunch(args), null);
}
}
}
}

View File

@@ -34,8 +34,6 @@
<RepositionThemeTransition IsStaggeringEnabled="False" />
</StackPanel.ChildrenTransitions>-->
<!-- 'Activation' section -->
<TextBlock x:Uid="ActivationSettingsHeader" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
@@ -68,8 +66,6 @@
</ComboBox>
</controls:SettingsCard>
<!-- 'Behavior' section -->
<TextBlock x:Uid="BehaviorSettingsHeader" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="Settings_GeneralPage_ShowAppDetails_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=&#xE8A0;}">
@@ -88,16 +84,7 @@
<ToggleSwitch IsOn="{x:Bind viewModel.ShowSystemTrayIcon, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- 'For Developers' section -->
<TextBlock x:Uid="ForDevelopersSettingsHeader" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="Settings_GeneralPage_AllowExternalReload_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=&#xE777;}">
<ToggleSwitch IsOn="{x:Bind viewModel.AllowExternalReload, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- 'About' section -->
<!-- Example 'About' section -->
<TextBlock x:Uid="AboutSettingsHeader" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander x:Uid="Settings_GeneralPage_About_SettingsExpander" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/StoreLogo.png}">

View File

@@ -24,15 +24,23 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="TitleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
Height="16"
Margin="16,0,0,0"
Source="ms-appx:///Assets/icon.svg" />
</TitleBar.LeftHeader>
</TitleBar>
<!-- TO DO: Replace this with WinUI TitleBar once that ships. -->
<StackPanel
x:Name="AppTitleBar"
Grid.Row="0"
Height="48"
Margin="16,0,0,0"
Orientation="Horizontal">
<Image
Width="16"
Height="16"
Source="ms-appx:///Assets/icon.svg" />
<TextBlock
x:Uid="CmdPalSettingsHeader"
Margin="12,0,0,0"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<NavigationView
x:Name="NavView"
Grid.Row="1"
@@ -69,6 +77,7 @@
x:Name="NavigationBreadcrumbBar"
Grid.Row="0"
MaxWidth="1000"
Margin="16,0,0,0"
ItemClicked="NavigationBreadcrumbBar_ItemClicked"
ItemsSource="{x:Bind BreadCrumbs, Mode=OneWay}">
<BreadcrumbBar.ItemTemplate>

View File

@@ -31,10 +31,8 @@ public sealed partial class SettingsWindow : WindowEx,
this.InitializeComponent();
this.ExtendsContentIntoTitleBar = true;
this.SetIcon();
var title = RS_.GetString("SettingsWindowTitle");
this.AppWindow.Title = title;
this.AppWindow.Title = RS_.GetString("SettingsWindowTitle");
this.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
this.TitleBar.Title = title;
PositionCentered();
WeakReferenceMessenger.Default.Register<NavigateToExtensionSettingsMessage>(this);

View File

@@ -441,13 +441,4 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="NavigationPaneOpened" xml:space="preserve">
<value>Navigation page opened</value>
</data>
<data name="Settings_GeneralPage_AllowExternalReload_SettingsCard.Header" xml:space="preserve">
<value>Enable external reload</value>
</data>
<data name="Settings_GeneralPage_AllowExternalReload_SettingsCard.Description" xml:space="preserve">
<value>Trigger reload of the extension externally with the x-cmdpal://reload command</value>
</data>
<data name="ForDevelopersSettingsHeader.Text" xml:space="preserve">
<value>For Developers</value>
</data>
</root>

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