Compare commits

...

17 Commits

Author SHA1 Message Date
Jeremy Sinclair
808066f272 Update NOTICE.md 2025-07-08 12:42:21 -07:00
Jeremy Sinclair
6e571d44a3 [Cmdpal][Deps] Update System.Text.Json package in extension template to 9.0.7 2025-07-08 12:39:05 -07:00
Jeremy Sinclair
69aa98fabc [Deps] Update .NET packages from 9.0.6 to 9.0.7 2025-07-08 12:31:28 -07:00
Michael Jolley
f34735edeb Adding fallback command for Windows Settings extension (#40331)
Two changes:

- Added a new fallback command for Windows Settings extension. If only
one setting or one exact match for the query was found, display that
setting in the list and open that setting on <Enter>. If more than one
setting was found, display a message to open Windows Settings to see the
search results for your query.


![image](https://github.com/user-attachments/assets/bd5708a5-b1d5-466e-9c62-cd1cd7bb1f74)


![image](https://github.com/user-attachments/assets/98f4ac20-efe1-4782-8133-30afa17e3b7d)


![image](https://github.com/user-attachments/assets/e5da90e1-f89b-480c-bd26-214c68ac013a)

- Modified the titles/subtitles of the extension to pull from Resources
to aid in internationalization.

Closes: #38548 and possibly #40308
2025-07-08 10:05:02 -05:00
Niels Laute
c6cee94456 [CmdPal] UX tweaks (#40381)
Based on guidance from the design team, this PR introduces a bunch of
small UX tweaks:

- Standardizing body text on 14px (e.g. for the Adaptive Cards related
code).
- Left align all content in the details pane
- Brush tweaks to the hotkey / tags for better visibility


![image](https://github.com/user-attachments/assets/4d9bf699-29bb-42e0-af96-b9b72c34f259)


![image](https://github.com/user-attachments/assets/905a268b-2e29-408c-a301-10a98b5885f1)


![image](https://github.com/user-attachments/assets/b9050693-f4bb-4d74-8701-fb30b46698e0)


![image](https://github.com/user-attachments/assets/5f6f93a0-1d6e-4476-bad5-dc7a9e179e92)

Closes #38858
2025-07-07 10:31:56 -05:00
Gordon Lam
0323ebea58 Fix Random unitttest failure (#40390)
The test
`Win32ProgramRepositoryMustCallOnAppRenamedForLnkAppsWhenRenamedEventIsRaised`
was experiencing random failures due to object identity mismatches in
the repository's hash-based storage system.

## Root Cause

The test was manually creating `Win32Program` objects:

```csharp
Win32Program olditem = new Win32Program
{
    Name = "oldpath",
    ExecutableName = oldpath,
    FullPath = linkingTo,
};
```

However, the `DoOnAppRenamedAsync` method creates the `oldApp` object
for removal using a different approach for .lnk files:

```csharp
oldApp = new Win32Program() { 
    Name = Path.GetFileNameWithoutExtension(e.OldName), 
    ExecutableName = Path.GetFileName(e.OldName), 
    FullPath = newApp?.FullPath ?? oldPath 
};
```

Since the repository uses `GetHashCode()` (based on `Name`,
`ExecutableName`, and `FullPath`) to identify objects for removal, any
subtle differences in these properties would cause the `Remove()`
operation to fail, leading to test assertion failures.

## Fix

Changed the test to use `Win32Program.GetAppFromPath()` instead of
manual object creation:

```csharp
Win32Program olditem = Win32Program.GetAppFromPath(oldFullPath);
Win32Program newitem = Win32Program.GetAppFromPath(newFullPath);
```

This mirrors the approach used in the working
`Win32ProgramRepositoryMustCallOnAppRenamedForUrlAppsWhenRenamedEventIsRaised`
test and ensures that test objects are created using the same code path
as the production code, eliminating hash code mismatches.

## Why This Was Random

The test failure appeared random because it depended on subtle
differences in object creation that could vary based on timing, mock
setup, or other environmental factors. By using the same object creation
method as the production code, the test becomes deterministic.
2025-07-07 17:35:09 +08:00
Gordon Lam
bd132d9b40 Fix 6 UI Automation on FanzyZone given there is renamed in Setting UI. (#40434)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
It has renamed on the "Make dragged window transparent" => "Make the
dragged window transparent"

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
Kicked off UI automation pipeline

https://microsoft.visualstudio.com/Dart/_build/results?buildId=125833108&view=ms.vss-test-web.build-test-results-tab&runId=924788779&resultId=100072&paneView=debug
No more failure on FancyZone
2025-07-07 17:34:57 +08:00
leileizhang
69064fab99 Resolve spelling check noise (#40433)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
These appear in every PR

![image](https://github.com/user-attachments/assets/06bb02f1-d828-44c7-8c18-624d3e10def3)
Resolve all spelling issues that were generating excessive noise in PRs.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-07 13:08:48 +08:00
leileizhang
a64de74f3b [UI Tests] Fix incorrect Settings Page launch method in UI test framework (#40367)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
### Root Cause
The UI test framework previously attempted to launch
PowerToys.Settings.exe directly.
However, this bypasses the PowerToys Runner, which is required for
proper interaction
and communication between runner.

### Fix
1. This change updates the launch mechanism to start PowerToys through
the Runner
with the appropriate argument ("--open-settings").
2. Prevents the Debug dialog from appearing in test runs

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-07 12:58:49 +08:00
leileizhang
7772bfb777 Fix: File explorer preview didn't work with per-user installation (#40314)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This Bug started when the Win11 context menu integration was first
introduced by Image Resizer in version v0.60.0.

Move Image Resizer to the WinUI3Apps folder to fix file preview issue
when PowerToys is installed on a non-C:\Program Files.
This aligns with the current structure used by File Locksmith and
PowerRename, which are not WinUI 3 apps either, but are already located
there.

### Root Cause:
When registering an MSIX package, the Windows API adds certain user
permissions to the installation folders. Since Image Resizer was
previously placed under the main PowerToys directory, these permission
changes could prevent Explorer from loading its preview handler properly
in per-user scenarios.


![image](https://github.com/user-attachments/assets/a8626314-19ce-4e25-87d6-d5e74a015e68)

Interestingly, this issue only affects per-user installs, not
machine-wide installs (e.g., to Program Files), even though both
locations receive additional permissions. The exact reason is still
unclear and requires further investigation.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #24384 #29644 #32113 #34139 #37866 #40345
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-07 09:41:19 +08:00
Mike Griese
0f279002f8 Fix loading our app state (#40401)
Yea this is dumb. Cant have private setters when json deserializing.
closes #40399
2025-07-06 16:10:15 -05:00
leileizhang
0c425fd1d7 [Fix][CmdPal] CmdPal Apps extension is missing all Win32 applications (#40392)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
### Root Cause
Calling `"CreateFile"` without an explicit `EntryPoint` fails because
kernel32.dll only exports `CreateFileA` and `CreateFileW`.

### Fix
Add EntryPoint = "CreateFileW"

### How to reproduce
The issue is reproducible when the PC is set to Chinese or another
non-English language.

![image](https://github.com/user-attachments/assets/2bdfd644-3ccf-4ad0-a470-2bd7de29049c)

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #40389, #40340 #40388 #40378 #40341
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-04 16:18:18 +08:00
Kai Tao
03a9ac1ac7 [UI automation] workspaces ui automation (#39812)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed


![image](https://github.com/user-attachments/assets/1be219be-1d06-432c-8acb-e3a2ba56d1b6)


https://microsoft.visualstudio.com/Dart/_build/results?buildId=125637396&view=results

---------

Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
Co-authored-by: Jerry Xu <n.xu@outlook.com>
Co-authored-by: Zhaopeng Wang <zhaopengwang@microsoft.com>
Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
Co-authored-by: Mengyuan <162882040+chenmy77@users.noreply.github.com>
Co-authored-by: yaqingmi <miyaqing01@gmail.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
Co-authored-by: Yaqing Mi (from Dev Box) <yaqingmi@microsoft.com>
Co-authored-by: XiaofengWang <709586527@qq.com>
Co-authored-by: zhaopeng wang <33367956+wang563681252@users.noreply.github.com>
Co-authored-by: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com>
Co-authored-by: RokyZevon <12629919+RokyZevon@users.noreply.github.com>
Co-authored-by: Yu Leng <42196638+moooyo@users.noreply.github.com>
Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
Co-authored-by: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com>
Co-authored-by: Gordon Lam <73506701+yeelam-gordon@users.noreply.github.com>
Co-authored-by: ruslanlap <106077551+ruslanlap@users.noreply.github.com>
Co-authored-by: Muhammad Danish <mdanishkhdev@gmail.com>
Co-authored-by: Bennett Blodinger <benwa@users.noreply.github.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Ionuț Manța <ionut@janeasystems.com>
Co-authored-by: Hao Liu <liuhaobupt@163.com>
Co-authored-by: OlegHarchevkin <40352094+OlegKharchevkin@users.noreply.github.com>
Co-authored-by: dcog989 <89043002+dcog989@users.noreply.github.com>
Co-authored-by: PesBandi <127593627+PesBandi@users.noreply.github.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
Co-authored-by: Typpi <20943337+Nick2bad4u@users.noreply.github.com>
Co-authored-by: Mike Griese <migrie@microsoft.com>
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
Co-authored-by: Abhyudit <64366765+bitmap4@users.noreply.github.com>
Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>
Co-authored-by: Ved Nig <vednig12@outlook.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Aung Khaing Khant <aungkhaingkhant.dev@gmail.com>
Co-authored-by: Aung Khaing Khant <aungkhaingkhant@advent-soft.com>
Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
Co-authored-by: leileizhang <leilzh@microsoft.com>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
Co-authored-by: Shawn Yuan <128874481+shuaiyuanxx@users.noreply.github.com>
Co-authored-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
Co-authored-by: cryolithic <cryolithic@gmail.com>
Co-authored-by: Lemonyte <49930425+lemonyte@users.noreply.github.com>
Co-authored-by: Gordon Lam (SH) <yeelam@microsoft.com>
Co-authored-by: Corey Hayward <72159232+CoreyHayward@users.noreply.github.com>
Co-authored-by: Jerry Xu <nxu@microsoft.com>
Co-authored-by: Shawn Yuan <shuaiyuan@microsoft.com>
Co-authored-by: Kayla Cinnamon <cinnamon@microsoft.com>
Co-authored-by: Jeremy Sinclair <4016293+snickler@users.noreply.github.com>
2025-07-04 10:07:37 +08:00
Copilot
837d5ca543 Fix signing configuration: Remove 25 obsolete file references from ESRPSigning_core.json (#40241)
## Summary

This PR fixes the signing pipeline by removing 25 obsolete file
references from `ESRPSigning_core.json` that were causing "0 files out
of: 0 files" errors during the signing process. These references pointed
to files that are either no longer built or were never produced by the
current project structure.

## Root Cause Analysis

The signing configuration contained references to files that fall into
three categories:

1. **Static libraries incorrectly listed as DLLs** - Projects configured
as `StaticLibrary` don't produce `.dll` files
2. **Obsolete/non-existent projects** - References to projects that were
removed or renamed
3. **WinExe projects incorrectly listed as producing DLLs** - C#
projects with `OutputType=WinExe` only produce `.exe` files, not `.dll`
files

## Changes Made

### Static Libraries (3 files removed):
- `Notifications.dll` - notifications project is a StaticLibrary
- `os-detection.dll` - no corresponding project found
- `Telemetry.dll` - telemetry projects are StaticLibraries

### Obsolete Projects (3 files removed):
- `fancyzones.dll` - FancyZones now produces `PowerToys.FancyZones.exe`
- `Wox.dll` - only `Wox.Plugin.dll` and `Wox.Infrastructure.dll` exist
- Duplicate `PowerToys.ManagedTelemetry.dll` entry

### WinExe Projects (19 files removed):
**Preview/Thumbnail Handlers (11 files):**
All preview and thumbnail handler C# projects have `OutputType=WinExe`
and only produce `.exe` files:
- Removed `.dll` entries for: GcodePreviewHandler,
MarkdownPreviewHandler, MonacoPreviewHandler, PdfPreviewHandler,
QoiPreviewHandler, SvgPreviewHandler, GcodeThumbnailProvider,
PdfThumbnailProvider, QoiThumbnailProvider, StlThumbnailProvider,
SvgThumbnailProvider

**Application Modules (8 files):**
- `PowerToys.WorkspacesEditor.dll` and
`PowerToys.WorkspacesLauncherUI.dll`
- `PowerToys.Awake.dll` and `PowerToys.ImageResizer.dll` 
- `PowerToys.ColorPickerUI.dll` and `PowerToys.PowerOCR.dll`
- `PowerToys.PowerAccent.dll` and `PowerToys.PowerLauncher.dll`

## Verification

All removed entries were verified by:
1. Checking project files for `OutputType` and `ConfigurationType`
settings
2. Confirming `AssemblyName` and `TargetName` properties
3. Ensuring no actual built artifacts are affected

The signing process should now successfully find all expected files and
eliminate the "0 files out of: 0 files" pattern.

Fixes #40240.

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 Share your feedback on Copilot coding agent for the chance to win a
$200 gift card! Click
[here](https://survey.alchemer.com/s3/8343779/Copilot-Coding-agent) to
start the survey.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vanzue <69313318+vanzue@users.noreply.github.com>
2025-07-04 09:50:22 +08:00
Kai Tao
2ff5adbdd4 [Settings] Complete the settings deeplink (#40376)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Many settings page's deep link is not implemented, this PR complete them
and make them aligned with the settings page.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-04 09:41:19 +08:00
Niels Laute
faebd21a2d [Settings] String updates and minor UX tweaks (#40249)
## Summary of the Pull Request

- I asked Copilot nicely to rewrite some of our strings to be inline
with the Windows writing style guide and to be less verbose or provide a
better explaination.
- Since the Sound expander on the Always on Top page had a single
setting, turning that into a card.

Before:

![image](https://github.com/user-attachments/assets/04ec18f1-06a4-4185-ac50-655070dcf3fc)

After:

![image](https://github.com/user-attachments/assets/e5d62e00-5d0a-4a2d-9011-493232689f41)


Simplified UI + less XAML for diagnostics data:

Before:

![image](https://github.com/user-attachments/assets/c0b0468b-7587-4684-8fbf-bad856e4d30c)

After:

![image](https://github.com/user-attachments/assets/185f2ff8-a3b1-49bd-8617-24b2c5a454a2)



<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

---------

Co-authored-by: Kai Tao <kaitao@microsoft.com>
2025-07-03 16:20:22 +02:00
Niels Laute
6412d5722e [UX] Settings - fix header position (#40160)
## Summary of the Pull Request
Due to a padding in the scrollviewer hosting the settingspage content,
the title drifts away upon resizing the settings window.
This PR fixes that issue by adding the (20px) padding to the maxwidth of
the header textblock.

Before:

![header](https://github.com/user-attachments/assets/6704a59d-843c-49a7-a109-e22e2cecccfe)

After:

![header2](https://github.com/user-attachments/assets/c9b0aa46-fcab-43ac-be87-ea423d810606)



## PR Checklist

- [x] **Closes:** #40159
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-03 04:33:56 -05:00
59 changed files with 2235 additions and 563 deletions

View File

@@ -127,3 +127,4 @@
^tools/Verification scripts/Check preview handler registration\.ps1$
ignore$
^src/modules/registrypreview/RegistryPreviewUILib/Controls/HexBox/.*$
^src/common/CalculatorEngineCommon/exprtk\.hpp$

View File

@@ -141,7 +141,7 @@ boxmodel
BPBF
bpmf
bpp
Breadcrumb
breadcrumb
Browsable
BROWSEINFO
bsd
@@ -192,7 +192,6 @@ CImage
cla
CLASSDC
CLASSNOTAVAILABLE
cleanmgr
clickable
clickonce
CLIENTEDGE
@@ -204,7 +203,6 @@ CLIPSIBLINGS
closesocket
clp
CLSCTX
CLSCTXLOCALSERVER
clsids
Clusion
cmder
@@ -496,7 +494,6 @@ FFFF
FILEEXPLORER
FILEFLAGS
FILEFLAGSMASK
FILELOCKSMITH
FILELOCKSMITHCONTEXTMENU
FILELOCKSMITHEXT
FILELOCKSMITHLIBINTEROP
@@ -762,7 +759,6 @@ IUI
IUnknown
IUse
IWIC
iwr
jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jjw
@@ -1287,7 +1283,6 @@ prvpane
psapi
pscid
PSECURITY
psexec
psfgao
psfi
PSMODULEPATH
@@ -1545,7 +1540,6 @@ SLGP
sln
SMALLICON
smartphone
smileys
SMTO
SNAPPROCESS
snk
@@ -1682,7 +1676,6 @@ TESTONLY
testprocess
TEXCOORD
TEXTBOXNEWLINE
TEXTEXTRACTOR
TEXTINCLUDE
tfopen
tgz
@@ -1890,7 +1883,6 @@ WINDOWPOSCHANGING
WINDOWSBUILDNUMBER
windowssearch
windowssettings
windowsterminal
WINDOWSTYLES
WINDOWSTYLESICON
winerror
@@ -1994,7 +1986,6 @@ YStr
YTM
YVIRTUALSCREEN
ZEROINIT
Zhiwei
zonable
zoneset
Zoneszonabletester
@@ -2013,10 +2004,47 @@ culori
Evercoder
LCh
CIELCh
CLSCTXINPROCALL
IIDI
irow
lcid
ppwsz
rguid
VARTYPE
advancedpaste
appxpackage
Ashcraft
CDPX
commandnotfound
copyable
Corpor
cropandlock
environmentvariables
fileexploreraddons
filelocksmith
findmymouse
fucntion
fuzzingtesting
hostsfileeditor
hotfixes
IDOn
lcl
LIBFUZZER
makepri
mikeclayton
mousehighlighter
mousejump
mousepointer
mouseutils
MVPs
onebranch
PMs
Psr
quickaccent
regsvr
screenruler
sharpfuzz
sourced
stuttery
textextractor
Windowss
XLoc
zonability

View File

@@ -11,14 +11,13 @@
"PowerToys.ActionRunner.exe",
"PowerToys.Update.exe",
"PowerToys.BackgroundActivatorDLL.dll",
"Notifications.dll",
"os-detection.dll",
"PowerToys.exe",
"PowerToys.FilePreviewCommon.dll",
"PowerToys.Interop.dll",
"Tools\\PowerToys.BugReportTool.exe",
"StylesReportTool\\PowerToys.StylesReportTool.exe",
"Telemetry.dll",
"CalculatorEngineCommon.dll",
"PowerToys.ManagedTelemetry.dll",
"PowerToys.ManagedCommon.dll",
@@ -33,7 +32,7 @@
"PowerToys.AlwaysOnTopModuleInterface.dll",
"PowerToys.CmdNotFoundModuleInterface.dll",
"PowerToys.CmdNotFound.dll",
"PowerToys.ColorPicker.dll",
"PowerToys.ColorPickerUI.dll",
@@ -54,7 +53,7 @@
"PowerToys.Awake.exe",
"PowerToys.Awake.dll",
"fancyzones.dll",
"PowerToys.FancyZonesEditor.exe",
"PowerToys.FancyZonesEditor.dll",
"PowerToys.FancyZonesEditorCommon.dll",
@@ -136,7 +135,7 @@
"PowerToys.PowerLauncher.dll",
"PowerToys.PowerLauncher.exe",
"PowerToys.PowerLauncher.Telemetry.dll",
"Wox.dll",
"Wox.Infrastructure.dll",
"Wox.Plugin.dll",
"RunPlugins\\Calculator\\Microsoft.PowerToys.Run.Plugin.Calculator.dll",
@@ -275,16 +274,16 @@
"Mono.Cecil.Pdb.dll",
"Mono.Cecil.Rocks.dll",
"Newtonsoft.Json.dll",
"Newtonsoft.Json.Bson.dll",
"NLog.dll",
"HtmlAgilityPack.dll",
"Markdig.Signed.dll",
"HelixToolkit.dll",
"HelixToolkit.Core.Wpf.dll",
"Mages.Core.dll",
"JetBrains.Annotations.dll",
"NLog.Extensions.Logging.dll",
"getfilesiginforedist.dll",
"concrt140_app.dll",
"msvcp140_1_app.dll",
"msvcp140_2_app.dll",
@@ -294,22 +293,8 @@
"vcomp140_app.dll",
"vcruntime140_1_app.dll",
"vcruntime140_app.dll",
"WinUI3Apps\\CommunityToolkit.Labs.WinUI.SettingsControls.dll",
"UnicodeInformation.dll",
"Vanara.Core.dll",
"Vanara.PInvoke.ComCtl32.dll",
"Vanara.PInvoke.Cryptography.dll",
"Vanara.PInvoke.Gdi32.dll",
"Vanara.PInvoke.Kernel32.dll",
"Vanara.PInvoke.Ole.dll",
"Vanara.PInvoke.Rpc.dll",
"Vanara.PInvoke.Security.dll",
"Vanara.PInvoke.Shared.dll",
"Vanara.PInvoke.Shell32.dll",
"Vanara.PInvoke.ShlwApi.dll",
"Vanara.PInvoke.User32.dll",
"WinUI3Apps\\clrcompression.dll",
"WinUI3Apps\\Microsoft.Graphics.Canvas.Interop.dll",
"Microsoft.Web.WebView2.Core.dll",
"Microsoft.Web.WebView2.WinForms.dll",
"Microsoft.Web.WebView2.Wpf.dll",
@@ -336,7 +321,7 @@
"Testably.Abstractions.FileSystem.Interface.dll",
"WinUI3Apps\\Testably.Abstractions.FileSystem.Interface.dll",
"ColorCode.Core.dll",
"ColorCode.UWP.dll",
"UnitsNet.dll",
"UtfUnknown.dll",
"Wpf.Ui.dll"

View File

@@ -32,22 +32,22 @@
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.6" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.7" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.6" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.7" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.7" />
<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.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.6" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.7" />
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.340" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.6" />
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.7" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.183" />
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
<!--
@@ -75,28 +75,28 @@
<PackageVersion Include="StreamJsonRpc" Version="2.21.69" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.CodeDom" Version="9.0.6" />
<PackageVersion Include="System.CodeDom" Version="9.0.7" />
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.6" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.6" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.6" />
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.7" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.7" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.7" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.6" />
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.7" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.6" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.6" />
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.7" />
<PackageVersion Include="System.Drawing.Common" Version="9.0.7" />
<PackageVersion Include="System.IO.Abstractions" Version="22.0.13" />
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="22.0.13" />
<PackageVersion Include="System.Management" Version="9.0.6" />
<PackageVersion Include="System.Management" Version="9.0.7" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.6" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.6" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.6" />
<PackageVersion Include="System.Runtime.Caching" Version="9.0.7" />
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.7" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.7" />
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="5.56.0" />

View File

@@ -1517,23 +1517,23 @@ SOFTWARE.
- Mages 3.0.0
- Markdig.Signed 0.34.0
- MessagePack 3.1.3
- Microsoft.Bcl.AsyncInterfaces 9.0.6
- Microsoft.Bcl.AsyncInterfaces 9.0.7
- Microsoft.Bot.AdaptiveExpressions.Core 4.23.0
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
- Microsoft.Data.Sqlite 9.0.6
- Microsoft.Data.Sqlite 9.0.7
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.DotNet.ILCompiler (A)
- Microsoft.Extensions.DependencyInjection 9.0.6
- Microsoft.Extensions.Hosting 9.0.6
- Microsoft.Extensions.Hosting.WindowsServices 9.0.6
- Microsoft.Extensions.Logging 9.0.6
- Microsoft.Extensions.Logging.Abstractions 9.0.6
- Microsoft.Extensions.DependencyInjection 9.0.7
- Microsoft.Extensions.Hosting 9.0.7
- Microsoft.Extensions.Hosting.WindowsServices 9.0.7
- Microsoft.Extensions.Logging 9.0.7
- Microsoft.Extensions.Logging.Abstractions 9.0.7
- Microsoft.NET.ILLink.Tasks (A)
- Microsoft.SemanticKernel 1.15.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2903.40
- Microsoft.Win32.SystemEvents 9.0.6
- Microsoft.Windows.Compatibility 9.0.6
- Microsoft.Win32.SystemEvents 9.0.7
- Microsoft.Windows.Compatibility 9.0.7
- Microsoft.Windows.CsWin32 0.3.183
- Microsoft.Windows.CsWinRT 2.2.0
- Microsoft.Windows.SDK.BuildTools 10.0.26100.4188
@@ -1553,25 +1553,25 @@ SOFTWARE.
- SkiaSharp.Views.WinUI 2.88.9
- StreamJsonRpc 2.21.69
- StyleCop.Analyzers 1.2.0-beta.556
- System.CodeDom 9.0.6
- System.CodeDom 9.0.7
- System.CommandLine 2.0.0-beta4.22272.1
- System.ComponentModel.Composition 9.0.6
- System.Configuration.ConfigurationManager 9.0.6
- System.Data.OleDb 9.0.6
- System.ComponentModel.Composition 9.0.7
- System.Configuration.ConfigurationManager 9.0.7
- System.Data.OleDb 9.0.7
- System.Data.SqlClient 4.9.0
- System.Diagnostics.EventLog 9.0.6
- System.Diagnostics.PerformanceCounter 9.0.6
- System.Drawing.Common 9.0.6
- System.Diagnostics.EventLog 9.0.7
- System.Diagnostics.PerformanceCounter 9.0.7
- System.Drawing.Common 9.0.7
- System.IO.Abstractions 22.0.13
- System.IO.Abstractions.TestingHelpers 22.0.13
- System.Management 9.0.6
- System.Management 9.0.7
- System.Net.Http 4.3.4
- System.Private.Uri 4.3.2
- System.Reactive 6.0.1
- System.Runtime.Caching 9.0.6
- System.ServiceProcess.ServiceController 9.0.6
- System.Text.Encoding.CodePages 9.0.6
- System.Text.Json 9.0.6
- System.Runtime.Caching 9.0.7
- System.ServiceProcess.ServiceController 9.0.7
- System.Text.Encoding.CodePages 9.0.7
- System.Text.Json 9.0.7
- System.Text.RegularExpressions 4.3.1
- UnicodeInformation 2.6.0
- UnitsNet 5.56.0

View File

@@ -14,7 +14,7 @@
## Localization on the pipeline (CDPX)
[The localization step](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L45-L52) is run on the pipeline before the solution is built. This step runs the [build-localization](https://github.com/microsoft/PowerToys/blob/main/.pipelines/build-localization.cmd) script, which generates resx files for all the projects with localization enabled using the `Localization.XLoc` package.
The [`Localization.XLoc`](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/build-localization.cmd#L24-L25) tool is run on the repo root, and it checks for all occurrences of `LocProject.json`. Each localized project has a `LocProject.json` file in the project root, which contains the location of the English resx file, list of languages for localization, and the output path where the localized resx files are to be copied to. In addition to this, some other parameters can be set, such as whether the language ID should be added as a folder in the file path or in the file name. When the CDPX pipeline is run, the localization team is notified of changes in the English resx files. For each project with localization enabled, a `loc` folder (see [this](https://github.com/microsoft/PowerToys/tree/main/src/modules/launcher/Microsoft.Launcher/loc) for example) is created in the same directory as the `LocProject.json` file. The folder contains language specific folders which in turn have a nested folder path equivalent to `OutputPath` in the `LocProject.json`. Each of these folders contain one `lcl` file. The `lcl` files contain the English resources along with their translation for that language. These are described in more detail in the [Lcl files section](#lcl-files). Once the `.resx` files are generated, they will be used during the `Build PowerToys` step for localized versions of the modules.
The [`Localization.XLoc`](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/build-localization.cmd#L24-L25) tool is run on the repo root, and it checks for all occurrences of `LocProject.json`. Each localized project has a `LocProject.json` file in the project root, which contains the location of the English resx file, list of languages for localization, and the output path where the localized resx files are to be copied to. In addition to this, some other parameters can be set, such as whether the language ID should be added as a folder in the file path or in the file name. When the CDPX pipeline is run, the localization team is notified of changes in the English resx files. For each project with localization enabled, a `loc` folder (see [the loc folder in the Microsoft.Launcher module](https://github.com/microsoft/PowerToys/tree/main/src/modules/launcher/Microsoft.Launcher/loc) for example) is created in the same directory as the `LocProject.json` file. The folder contains language specific folders which in turn have a nested folder path equivalent to `OutputPath` in the `LocProject.json`. Each of these folders contain one `lcl` file. The `lcl` files contain the English resources along with their translation for that language. These are described in more detail in the [Lcl files section](#lcl-files). Once the `.resx` files are generated, they will be used during the `Build PowerToys` step for localized versions of the modules.
Since the localization script requires certain nuget packages, the [`restore-localization`](https://github.com/microsoft/PowerToys/blob/main/.pipelines/restore-localization.cmd) script is run before running `build-localization` to install all the required packages. This script must [run in the `restore` step](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L37-L39) of pipeline because [the host is network isolated](https://onebranch.visualstudio.com/Pipeline/_wiki/wikis/Pipeline.wiki/2066/Consuming-Packages-in-a-CDPx-Pipeline?anchor=overview) at the `build` step. The [Toolset package source](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L23) is used for this.

View File

@@ -31,7 +31,7 @@ This document outlines the process for preparing and publishing PowerToys releas
- Uses semantic versioning: `MAJOR.MINOR.PATCH`
- MINOR version increases with regular releases (e.g., 0.89.0)
- PATCH version increases for hotfixes (e.g., 0.87.0 → 0.87.1)
- Each release version must be higher than the previous one for proper updating
- Each release version must be greater than the previous one for proper updating
## Testing Process

View File

@@ -5,10 +5,10 @@
<?include $(sys.CURRENTDIR)\Common.wxi?>
<?define ImageResizerAssetsFiles=?>
<?define ImageResizerAssetsFilesPath=$(var.BinDir)Assets\ImageResizer\?>
<?define ImageResizerAssetsFilesPath=$(var.BinDir)WinUI3Apps\Assets\ImageResizer\?>
<Fragment>
<DirectoryRef Id="BaseApplicationsAssetsFolder">
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<Directory Id="ImageResizerAssetsFolder" Name="ImageResizer" />
</DirectoryRef>
@@ -18,7 +18,7 @@
<Component Id="Module_ImageResizer_Registry" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
<RegistryValue Value="[INSTALLFOLDER]PowerToys.ImageResizerExt.dll" Type="string" />
<RegistryValue Value="[WinUI3AppsInstallFolder]PowerToys.ImageResizerExt.dll" Type="string" />
<RegistryValue Name="ThreadingModel" Value="Apartment" Type="string" />
</RegistryKey>

View File

@@ -186,7 +186,7 @@
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="ImageResizer_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="ImageResizer_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.ImageResizer.resources.dll" />
<File Id="ImageResizer_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\WinUI3Apps\$(var.Language)\PowerToys.ImageResizer.resources.dll" />
</Component>
<Component
Id="ColorPicker_$(var.IdSafeLanguage)_Component"

View File

@@ -179,7 +179,7 @@ Generate-FileList -fileDepsJson "" -fileListName HostsAssetsFiles -wxsFilePath $
Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot
#ImageResizer
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ImageResizer"
Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ImageResizer"
Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot
#New+

View File

@@ -11,26 +11,35 @@ namespace Common.UI
{
public enum SettingsWindow
{
Overview = 0,
Dashboard = 0,
Overview,
AlwaysOnTop,
Awake,
ColorPicker,
CmdNotFound,
FancyZones,
FileLocksmith,
Run,
ImageResizer,
KBM,
MouseUtils,
MouseWithoutBorders,
Peek,
PowerAccent,
PowerLauncher,
PowerPreview,
PowerRename,
FileExplorer,
ShortcutGuide,
Hosts,
MeasureTool,
PowerOCR,
Workspaces,
RegistryPreview,
CropAndLock,
EnvironmentVariables,
Dashboard,
AdvancedPaste,
Workspaces,
NewPlus,
CmdPal,
ZoomIt,
}
@@ -39,14 +48,22 @@ namespace Common.UI
{
switch (value)
{
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.Overview:
return "Overview";
case SettingsWindow.AlwaysOnTop:
return "AlwaysOnTop";
case SettingsWindow.Awake:
return "Awake";
case SettingsWindow.ColorPicker:
return "ColorPicker";
case SettingsWindow.CmdNotFound:
return "CmdNotFound";
case SettingsWindow.FancyZones:
return "FancyZones";
case SettingsWindow.FileLocksmith:
return "FileLocksmith";
case SettingsWindow.Run:
return "Run";
case SettingsWindow.ImageResizer:
@@ -55,6 +72,16 @@ namespace Common.UI
return "KBM";
case SettingsWindow.MouseUtils:
return "MouseUtils";
case SettingsWindow.MouseWithoutBorders:
return "MouseWithoutBorders";
case SettingsWindow.Peek:
return "Peek";
case SettingsWindow.PowerAccent:
return "PowerAccent";
case SettingsWindow.PowerLauncher:
return "PowerLauncher";
case SettingsWindow.PowerPreview:
return "PowerPreview";
case SettingsWindow.PowerRename:
return "PowerRename";
case SettingsWindow.FileExplorer:
@@ -67,18 +94,18 @@ namespace Common.UI
return "MeasureTool";
case SettingsWindow.PowerOCR:
return "PowerOcr";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.RegistryPreview:
return "RegistryPreview";
case SettingsWindow.CropAndLock:
return "CropAndLock";
case SettingsWindow.EnvironmentVariables:
return "EnvironmentVariables";
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.AdvancedPaste:
return "AdvancedPaste";
case SettingsWindow.Workspaces:
return "Workspaces";
case SettingsWindow.NewPlus:
return "NewPlus";
case SettingsWindow.CmdPal:
return "CmdPal";
case SettingsWindow.ZoomIt:

View File

@@ -21,7 +21,7 @@ public static partial class Kernel32
out int pBytesReturned,
IntPtr lpOverlapped);
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16, EntryPoint = "CreateFileW")]
public static partial int CreateFile(
string lpFileName,
FileAccessType dwDesiredAccess,

View File

@@ -9,6 +9,7 @@ using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -41,18 +42,6 @@ namespace Microsoft.PowerToys.UITest
this.locationPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
CheckWinAppDriverAndRoot();
var runnerProcessInfo = new ProcessStartInfo
{
FileName = locationPath + this.runnerPath,
Verb = "runas",
};
if (scope == PowerToysModule.PowerToysSettings)
{
this.ExitExe(runnerProcessInfo.FileName);
this.runner = Process.Start(runnerProcessInfo);
}
}
/// <summary>
@@ -76,6 +65,7 @@ namespace Microsoft.PowerToys.UITest
public SessionHelper Init()
{
this.ExitExe(this.locationPath + this.sessionPath);
this.StartExe(this.locationPath + this.sessionPath);
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
@@ -89,19 +79,6 @@ namespace Microsoft.PowerToys.UITest
public void Cleanup()
{
ExitScopeExe();
try
{
if (this.scope == PowerToysModule.PowerToysSettings)
{
runner?.Kill();
runner?.WaitForExit(); // Optional: Wait for the process to exit
}
}
catch (Exception ex)
{
// Handle exceptions if needed
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
}
}
/// <summary>
@@ -135,10 +112,65 @@ namespace Microsoft.PowerToys.UITest
public void StartExe(string appPath)
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
// if we want to start settings, we need to use the runner exe to open settings
if (scope == PowerToysModule.PowerToysSettings)
{
TryLaunchPowerToysSettings(opts);
}
else
{
opts.AddAdditionalCapability("app", appPath);
}
this.Driver = NewWindowsDriver(opts);
}
private void TryLaunchPowerToysSettings(AppiumOptions opts)
{
CheckWinAppDriverAndRoot();
var runnerProcessInfo = new ProcessStartInfo
{
FileName = locationPath + this.runnerPath,
Verb = "runas",
Arguments = "--open-settings",
};
this.ExitExe(runnerProcessInfo.FileName);
this.runner = Process.Start(runnerProcessInfo);
Thread.Sleep(5000);
if (root != null)
{
const int maxRetries = 3;
const int delayMs = 5000;
var windowName = "PowerToys Settings";
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
var settingsWindow = ApiHelper.FindDesktopWindowHandler(
new[] { windowName, AdministratorPrefix + windowName });
if (settingsWindow.Count > 0)
{
var hexHwnd = settingsWindow[0].HWnd.ToString("x");
opts.AddAdditionalCapability("appTopLevelWindow", hexHwnd);
return;
}
if (attempt < maxRetries)
{
Thread.Sleep(delayMs);
}
else
{
throw new TimeoutException("Failed to find PowerToys Settings window after multiple attempts.");
}
}
}
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
@@ -176,6 +208,19 @@ namespace Microsoft.PowerToys.UITest
public void ExitScopeExe()
{
ExitExe(sessionPath);
try
{
if (this.scope == PowerToysModule.PowerToysSettings)
{
runner?.Kill();
runner?.WaitForExit(); // Optional: Wait for the process to exit
}
}
catch (Exception ex)
{
// Handle exceptions if needed
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
}
}
/// <summary>

View File

@@ -4,18 +4,9 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using Windows.Devices.Display.Core;
using Windows.Foundation.Metadata;
using static Microsoft.PowerToys.UITest.UITestBase.NativeMethods;
using static Microsoft.PowerToys.UITest.WindowHelper;
namespace Microsoft.PowerToys.UITest
{
@@ -77,16 +68,6 @@ namespace Microsoft.PowerToys.UITest
this.sessionHelper = new SessionHelper(scope).Init();
this.Session = new Session(this.sessionHelper.GetRoot(), this.sessionHelper.GetDriver(), scope, size);
if (this.scope == PowerToysModule.PowerToysSettings)
{
// close Debug warning dialog if any
// Such debug warning dialog seems only appear in PowerToys Settings
if (this.FindAll("DEBUG").Count > 0)
{
this.Find("DEBUG").Find<Button>("Close").Click();
}
}
}
/// <summary>

View File

@@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace Microsoft.PowerToys.UITest
{
internal static class WindowHelper
public static class WindowHelper
{
internal const string AdministratorPrefix = "Administrator: ";

View File

@@ -1,4 +1,4 @@
<Page
<Page
x:Class="WorkspacesEditor.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -172,7 +172,7 @@
VerticalContentAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
Visibility="{Binding IsWorkspacesViewEmpty, Mode=OneWay, Converter={StaticResource BooleanToInvertedVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl x:Name="WorkspacesItemsControl" ItemsSource="{Binding WorkspacesView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
@@ -235,7 +235,10 @@
Grid.Column="1"
Margin="12,12,12,12"
Orientation="Vertical">
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<StackPanel
x:Name="WorkspaceActionGroup"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
x:Name="MoreButton"
HorizontalAlignment="Right"
@@ -248,7 +251,8 @@
</Button>
<Popup
AllowsTransparency="True"
IsOpen="{Binding IsPopupVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Closed="PopupClosed"
IsOpen="{Binding IsPopupVisible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Placement="Left"
PlacementTarget="{Binding ElementName=MoreButton}"
StaysOpen="False">

View File

@@ -4,7 +4,8 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using ManagedCommon;
using WorkspacesEditor.Models;
using WorkspacesEditor.ViewModels;
@@ -42,17 +43,28 @@ namespace WorkspacesEditor
e.Handled = true;
Button button = sender as Button;
Project selectedProject = button.DataContext as Project;
selectedProject.IsPopupVisible = false;
_mainViewModel.DeleteProject(selectedProject);
}
private void MoreButton_Click(object sender, RoutedEventArgs e)
{
_mainViewModel.CloseAllPopups();
e.Handled = true;
Button button = sender as Button;
Project project = button.DataContext as Project;
project.IsPopupVisible = true;
}
private void PopupClosed(object sender, object e)
{
if (sender is Popup p && p.DataContext is Project proj)
{
proj.IsPopupVisible = false;
}
}
private void LaunchButton_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;

View File

@@ -437,7 +437,11 @@ namespace WorkspacesEditor.ViewModels
private void RunSnapshotTool(bool isExistingProjectLaunched)
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo(@".\PowerToys.WorkspacesSnapshotTool.exe");
var exeDir = Path.GetDirectoryName(Environment.ProcessPath);
var snapshotUtilsPath = Path.Combine(exeDir, "PowerToys.WorkspacesSnapshotTool.exe");
process.StartInfo = new ProcessStartInfo(snapshotUtilsPath);
process.StartInfo.CreateNoWindow = true;
process.StartInfo.Arguments = isExistingProjectLaunched ? $"{(int)InvokePoint.LaunchAndEdit}" : string.Empty;

View File

@@ -51,6 +51,8 @@
MouseLeave="AppBorder_MouseLeave">
<Expander
Margin="0,0,20,0"
AutomationProperties.AutomationId="{Binding AppName}"
AutomationProperties.Name="{Binding AppName}"
FlowDirection="RightToLeft"
IsEnabled="{Binding IsIncluded, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsExpanded="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
@@ -399,7 +401,11 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}" ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
<ItemsControl
x:Name="CapturedAppList"
AutomationProperties.Name="Captured Application List"
ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}"
ItemsSource="{Binding ApplicationsListed, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</StackPanel>
</ScrollViewer>

View File

@@ -0,0 +1,605 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
[Ignore("not stable")]
public class WorkspacesEditingPageTests : WorkspacesUiAutomationBase
{
public WorkspacesEditingPageTests()
: base()
{
}
[TestMethod("WorkspacesEditingPage.RemoveApp")]
[TestCategory("Workspaces Editing Page UI")]
public void TestRemoveAppFromWorkspace()
{
// Find app list
var appList = Find<Custom>("AppList");
var initialAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
if (initialAppCount == 0)
{
Assert.Inconclusive("No apps in workspace to remove");
return;
}
// Remove first app
var firstApp = appList.FindAll<Custom>(By.ClassName("AppItem"))[0];
var appName = firstApp.GetAttribute("Name");
var removeButton = firstApp.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(500);
// Verify app removed from list
var finalAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
Assert.AreEqual(initialAppCount - 1, finalAppCount, "App should be removed from list");
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
Assert.AreEqual(finalAppCount, windowPreviews.Count, "Preview should show correct number of windows");
}
[TestMethod("WorkspacesEditingPage.RemoveAndAddBackApp")]
[TestCategory("Workspaces Editing Page UI")]
public void TestRemoveAndAddBackApp()
{
// Find app list
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
var appName = firstApp.GetAttribute("Name");
// Remove app
var removeButton = firstApp.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(500);
// Verify removed app shows in "removed apps" section
Assert.IsTrue(Has<Button>("Add back"), "Should have 'Add back' button for removed apps");
// Add back the app
var addBackButton = Find<Button>("Add back");
addBackButton.Click();
Thread.Sleep(500);
// Verify app is back in the list
var restoredApp = appList.Find<Custom>(By.Name(appName), timeoutMS: 2000);
Assert.IsNotNull(restoredApp, "App should be restored to the list");
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
var currentAppCount = appList.FindAll<Custom>(By.ClassName("AppItem")).Count;
Assert.AreEqual(currentAppCount, windowPreviews.Count, "Preview should show all windows again");
}
[TestMethod("WorkspacesEditingPage.SetAppMinimized")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetAppMinimized()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find and toggle minimized checkbox
var minimizedCheckbox = firstApp.Find<CheckBox>("Minimized");
bool wasMinimized = minimizedCheckbox.IsChecked;
minimizedCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasMinimized, minimizedCheckbox.IsChecked, "Minimized state should toggle");
// Verify preview reflects the change
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
// The first window preview should indicate minimized state
if (minimizedCheckbox.IsChecked && windowPreviews.Count > 0)
{
var firstPreview = windowPreviews[0];
var opacity = firstPreview.GetAttribute("Opacity");
// Minimized windows might have reduced opacity or other visual indicator
Assert.IsNotNull(opacity, "Minimized window should have visual indication in preview");
}
}
[TestMethod("WorkspacesEditingPage.SetAppMaximized")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetAppMaximized()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find and toggle maximized checkbox
var maximizedCheckbox = firstApp.Find<CheckBox>("Maximized");
bool wasMaximized = maximizedCheckbox.IsChecked;
maximizedCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasMaximized, maximizedCheckbox.IsChecked, "Maximized state should toggle");
// Verify preview reflects the change
var previewPane = Find<Pane>("Preview");
if (maximizedCheckbox.IsChecked)
{
// Maximized window should fill the preview area
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
if (windowPreviews.Count > 0)
{
var firstPreview = windowPreviews[0];
// Check if preview shows maximized state
var width = firstPreview.GetAttribute("Width");
var height = firstPreview.GetAttribute("Height");
Assert.IsNotNull(width, "Maximized window should have width in preview");
Assert.IsNotNull(height, "Maximized window should have height in preview");
}
}
}
[TestMethod("WorkspacesEditingPage.LaunchAsAdmin")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSetLaunchAsAdmin()
{
// Find app that supports admin launch
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
bool foundAdminCapableApp = false;
foreach (var app in apps)
{
try
{
var adminCheckbox = app.Find<CheckBox>("Launch as admin", timeoutMS: 1000);
if (adminCheckbox != null && adminCheckbox.IsChecked)
{
foundAdminCapableApp = true;
bool wasAdmin = adminCheckbox.IsChecked;
adminCheckbox.Click();
Thread.Sleep(500);
// Verify state changed
Assert.AreNotEqual(wasAdmin, adminCheckbox.IsChecked, "Admin launch state should toggle");
break;
}
}
catch
{
// This app doesn't support admin launch
continue;
}
}
if (!foundAdminCapableApp)
{
Assert.Inconclusive("No apps in workspace support admin launch");
}
}
[TestMethod("WorkspacesEditingPage.AddCLIArgs")]
[TestCategory("Workspaces Editing Page UI")]
public void TestAddCommandLineArguments()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find CLI args textbox
var cliArgsTextBox = firstApp.Find<TextBox>("Command line arguments", timeoutMS: 2000);
if (cliArgsTextBox == null)
{
Assert.Inconclusive("App does not support command line arguments");
return;
}
// Add test arguments
string testArgs = "--test-arg value";
cliArgsTextBox.SetText(testArgs);
Thread.Sleep(500);
// Verify arguments were entered
Assert.AreEqual(testArgs, cliArgsTextBox.Text, "Command line arguments should be set");
}
[TestMethod("WorkspacesEditingPage.ChangeAppPosition")]
[TestCategory("Workspaces Editing Page UI")]
public void TestManuallyChangeAppPosition()
{
// Find first app
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
if (apps.Count == 0)
{
Assert.Inconclusive("No apps in workspace to test");
return;
}
var firstApp = apps[0];
// Find position controls
var xPositionBox = firstApp.Find<TextBox>("X position", timeoutMS: 2000);
var yPositionBox = firstApp.Find<TextBox>("Y position", timeoutMS: 2000);
if (xPositionBox == null || yPositionBox == null)
{
// Try alternate approach with spinners
var positionSpinners = firstApp.FindAll<Custom>(By.ClassName("SpinBox"));
if (positionSpinners.Count >= 2)
{
xPositionBox = positionSpinners[0].Find<TextBox>(By.ClassName("TextBox"));
yPositionBox = positionSpinners[1].Find<TextBox>(By.ClassName("TextBox"));
}
}
if (xPositionBox != null && yPositionBox != null)
{
// Change position
xPositionBox.SetText("200");
Thread.Sleep(500);
yPositionBox.SetText("150");
Thread.Sleep(500);
// Verify preview updated
var previewPane = Find<Pane>("Preview");
var windowPreviews = previewPane.FindAll<Custom>(By.ClassName("WindowPreview"));
Assert.IsTrue(windowPreviews.Count > 0, "Preview should show window at new position");
}
else
{
Assert.Inconclusive("Could not find position controls");
}
}
[TestMethod("WorkspacesEditingPage.ChangeWorkspaceName")]
[TestCategory("Workspaces Editing Page UI")]
public void TestChangeWorkspaceName()
{
// Find workspace name textbox
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
// Change name
string newName = "Renamed_Workspace_" + DateTime.Now.Ticks;
nameTextBox.SetText(newName);
Thread.Sleep(500);
// Save changes
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify we're back at main list
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list after saving");
// Verify workspace was renamed
var workspacesList = Find<Custom>("WorkspacesList");
var renamedWorkspace = workspacesList.Find<Custom>(By.Name(newName), timeoutMS: 2000);
Assert.IsNotNull(renamedWorkspace, "Workspace should be renamed in the list");
}
[TestMethod("WorkspacesEditingPage.SaveAndCancelWork")]
[TestCategory("Workspaces Editing Page UI")]
public void TestSaveAndCancelButtons()
{
// Make a change
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
string tempName = originalName + "_temp";
nameTextBox.SetText(tempName);
Thread.Sleep(500);
// Test Cancel button
var cancelButton = Find<Button>("Cancel");
cancelButton.Click();
Thread.Sleep(1000);
// Verify returned to main list without saving
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
// Go back to editing
var workspacesList = Find<Custom>("WorkspacesList");
var workspace = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
workspace.Click();
Thread.Sleep(1000);
// Verify name wasn't changed
nameTextBox = Find<TextBox>("Workspace name");
Assert.AreEqual(originalName, nameTextBox.Text, "Name should not be changed after cancel");
// Now test Save button
nameTextBox.SetText(tempName);
Thread.Sleep(500);
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify saved
workspacesList = Find<Custom>("WorkspacesList");
var savedWorkspace = workspacesList.Find<Custom>(By.Name(tempName), timeoutMS: 2000);
Assert.IsNotNull(savedWorkspace, "Workspace should be saved with new name");
}
[TestMethod("WorkspacesEditingPage.NavigateWithoutSaving")]
[TestCategory("Workspaces Editing Page UI")]
public void TestNavigateToMainPageWithoutSaving()
{
// Make a change
var nameTextBox = Find<TextBox>("Workspace name");
string originalName = nameTextBox.Text;
nameTextBox.SetText(originalName + "_unsaved");
Thread.Sleep(500);
// Click on "Workspaces" navigation/breadcrumb
if (Has<NavigationViewItem>("Workspaces", timeoutMS: 1000))
{
var workspacesNav = Find<NavigationViewItem>("Workspaces");
workspacesNav.Click();
Thread.Sleep(1000);
}
else if (Has<HyperlinkButton>("Workspaces", timeoutMS: 1000))
{
var workspacesBreadcrumb = Find<HyperlinkButton>("Workspaces");
workspacesBreadcrumb.Click();
Thread.Sleep(1000);
}
// If there's a confirmation dialog, handle it
if (Has<Button>("Discard", timeoutMS: 1000))
{
Find<Button>("Discard").Click();
Thread.Sleep(500);
}
// Verify returned to main list
Assert.IsTrue(Has<Custom>("WorkspacesList"), "Should return to main list");
// Verify changes weren't saved
var workspacesList = Find<Custom>("WorkspacesList");
var unsavedWorkspace = workspacesList.Find<Custom>(By.Name(originalName + "_unsaved"), timeoutMS: 1000);
Assert.IsNull(unsavedWorkspace, "Unsaved changes should not persist");
}
[TestMethod("WorkspacesEditingPage.CreateDesktopShortcut")]
[TestCategory("Workspaces Editing Page UI")]
public void TestCreateDesktopShortcut()
{
// Find desktop shortcut checkbox
var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
// Get desktop path
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// Get workspace name to check for shortcut
var nameTextBox = Find<TextBox>("Workspace name");
string workspaceName = nameTextBox.Text;
string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
// Clean up any existing shortcut
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
Thread.Sleep(500);
}
// Check the checkbox
if (!shortcutCheckbox.IsChecked)
{
shortcutCheckbox.Click();
Thread.Sleep(500);
}
// Save
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(2000); // Give time for shortcut creation
// Verify shortcut was created
Assert.IsTrue(File.Exists(shortcutPath), "Desktop shortcut should be created");
// Clean up
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
}
}
[TestMethod("WorkspacesEditingPage.DesktopShortcutState")]
[TestCategory("Workspaces Editing Page UI")]
public void TestDesktopShortcutCheckboxState()
{
// Get workspace name
var nameTextBox = Find<TextBox>("Workspace name");
string workspaceName = nameTextBox.Text;
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string shortcutPath = Path.Combine(desktopPath, $"{workspaceName}.lnk");
// Find checkbox
var shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
// Test 1: When shortcut exists
if (!File.Exists(shortcutPath))
{
// Create shortcut first
if (!shortcutCheckbox.IsChecked)
{
shortcutCheckbox.Click();
Thread.Sleep(500);
}
Find<Button>("Save").Click();
Thread.Sleep(2000);
// Navigate back to editing
NavigateToEditingPage();
}
shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
Assert.IsTrue(shortcutCheckbox.IsChecked, "Checkbox should be checked when shortcut exists");
// Test 2: Remove shortcut
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
Thread.Sleep(500);
}
// Re-navigate to refresh state
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
NavigateToEditingPage();
shortcutCheckbox = Find<CheckBox>("Create desktop shortcut");
Assert.IsFalse(shortcutCheckbox.IsChecked, "Checkbox should be unchecked when shortcut doesn't exist");
}
[TestMethod("WorkspacesEditingPage.LaunchAndEdit")]
[TestCategory("Workspaces Editing Page UI")]
public void TestLaunchAndEditCapture()
{
// Find Launch and Edit button
var launchEditButton = Find<Button>("Launch and Edit");
launchEditButton.Click();
Thread.Sleep(3000); // Wait for apps to launch
// Open a new application
Process.Start("calc.exe");
Thread.Sleep(2000);
// Click Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Verify new app was added
var appList = Find<Custom>("AppList");
var apps = appList.FindAll<Custom>(By.ClassName("AppItem"));
bool foundCalculator = false;
foreach (var app in apps)
{
var appName = app.GetAttribute("Name");
if (appName.Contains("Calculator", StringComparison.OrdinalIgnoreCase))
{
foundCalculator = true;
break;
}
}
Assert.IsTrue(foundCalculator, "Newly opened Calculator should be captured and added");
// Clean up
foreach (var process in Process.GetProcessesByName("CalculatorApp"))
{
process.Kill();
}
foreach (var process in Process.GetProcessesByName("Calculator"))
{
process.Kill();
}
}
// Helper methods
private void NavigateToEditingPage()
{
// Ensure we have at least one workspace
if (!Has<Custom>("WorkspacesList", timeoutMS: 1000))
{
CreateTestWorkspace();
}
// Click on first workspace to edit
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
if (workspaceItems.Count == 0)
{
CreateTestWorkspace();
workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
}
workspaceItems[0].Click();
Thread.Sleep(1000);
}
private void CreateTestWorkspace()
{
// Open a test app
Process.Start("notepad.exe");
Thread.Sleep(1000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save with default name
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Close test app
foreach (var process in Process.GetProcessesByName("notepad"))
{
process.Kill();
}
}
}

View File

@@ -8,10 +8,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesEditorTests : UITestBase
public class WorkspacesEditorTests : WorkspacesUiAutomationBase
{
public WorkspacesEditorTests()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
: base()
{
}
@@ -21,4 +21,217 @@ public class WorkspacesEditorTests : UITestBase
{
Assert.IsTrue(this.Has<Button>("Create Workspace"), "Should have create workspace button");
}
/*
[TestMethod("WorkspacesEditor.Editor.NewWorkspaceAppearsInList")]
[TestCategory("Workspaces UI")]
public void TestNewWorkspaceAppearsInListAfterCapture()
{
// Open test application
OpenNotepad();
Thread.Sleep(2000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save workspace
var saveButton = Find<Button>("Save");
saveButton.Click();
Thread.Sleep(1000);
// Verify workspace appears in list
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
Assert.IsTrue(workspaceItems.Count > 0, "New workspace should appear in the list");
CloseNotepad();
}
[TestMethod("WorkspacesEditor.Editor.CancelCaptureDoesNotAddWorkspace")]
[TestCategory("Workspaces UI")]
public void TestCancelCaptureDoesNotAddWorkspace()
{
// Count existing workspaces
var workspacesList = Find<Custom>("WorkspacesList");
var initialCount = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem")).Count;
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Cancel
var cancelButton = Find<Button>("Cancel");
cancelButton.Click();
Thread.Sleep(1000);
// Verify count hasn't changed
var finalCount = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem")).Count;
Assert.AreEqual(initialCount, finalCount, "Workspace count should not change after canceling");
}
[TestMethod("WorkspacesEditor.Editor.SearchFiltersWorkspaces")]
[TestCategory("Workspaces UI")]
public void TestSearchFiltersWorkspaces()
{
// Create test workspaces first
CreateTestWorkspace("TestWorkspace1");
CreateTestWorkspace("TestWorkspace2");
CreateTestWorkspace("DifferentName");
// Find search box
var searchBox = Find<TextBox>("Search");
searchBox.SetText("TestWorkspace");
Thread.Sleep(1000);
// Verify filtered results
var workspacesList = Find<Custom>("WorkspacesList");
var visibleItems = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"));
// Should only show items matching "TestWorkspace"
Assert.IsTrue(visibleItems.Count >= 2, "Should show at least 2 TestWorkspace items");
// Clear search
searchBox.SetText(string.Empty);
Thread.Sleep(500);
}
[TestMethod("WorkspacesEditor.Editor.SortByWorks")]
[TestCategory("Workspaces UI")]
public void TestSortByFunctionality()
{
// Find sort dropdown
var sortDropdown = Find<ComboBox>("SortBy");
sortDropdown.Click();
Thread.Sleep(500);
// Select different sort options
var sortOptions = FindAll<Custom>(By.ClassName("ComboBoxItem"));
if (sortOptions.Count > 1)
{
sortOptions[1].Click(); // Select second option
Thread.Sleep(1000);
// Verify list is updated (we can't easily verify sort order in UI tests)
var workspacesList = Find<Custom>("WorkspacesList");
Assert.IsNotNull(workspacesList, "Workspaces list should still be visible after sorting");
}
}
[TestMethod("WorkspacesEditor.Editor.SortByPersists")]
[TestCategory("Workspaces UI")]
public void TestSortByPersistsAfterRestart()
{
// Set sort option
var sortDropdown = Find<ComboBox>("SortBy");
sortDropdown.Click();
Thread.Sleep(500);
var sortOptions = FindAll<Custom>(By.ClassName("ComboBoxItem"));
string selectedOption = string.Empty;
if (sortOptions.Count > 1)
{
sortOptions[1].Click(); // Select second option
selectedOption = sortDropdown.Text;
Thread.Sleep(1000);
}
// Restart editor
RestartScopeExe();
Thread.Sleep(2000);
// Verify sort option persisted
sortDropdown = Find<ComboBox>("SortBy");
Assert.AreEqual(selectedOption, sortDropdown.Text, "Sort option should persist after restart");
}
[TestMethod("WorkspacesEditor.Editor.RemoveWorkspace")]
[TestCategory("Workspaces UI")]
public void TestRemoveWorkspace()
{
// Create a test workspace
CreateTestWorkspace("WorkspaceToRemove");
// Find the workspace in the list
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.Find<Custom>(By.Name("WorkspaceToRemove"));
// Click remove button
var removeButton = workspaceItem.Find<Button>("Remove");
removeButton.Click();
Thread.Sleep(1000);
// Confirm removal if dialog appears
if (Has<Button>("Yes"))
{
Find<Button>("Yes").Click();
Thread.Sleep(1000);
}
// Verify workspace is removed
Assert.IsFalse(Has(By.Name("WorkspaceToRemove")), "Workspace should be removed from list");
}
[TestMethod("WorkspacesEditor.Editor.EditOpensEditingPage")]
[TestCategory("Workspaces UI")]
public void TestEditOpensEditingPage()
{
// Create a test workspace if none exist
if (!Has<Custom>("WorkspacesList"))
{
CreateTestWorkspace("TestWorkspace");
}
// Find first workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
// Click edit button
var editButton = workspaceItem.Find<Button>("Edit");
editButton.Click();
Thread.Sleep(1000);
// Verify editing page opened
Assert.IsTrue(Has<Button>("Save"), "Should have Save button on editing page");
Assert.IsTrue(Has<Button>("Cancel"), "Should have Cancel button on editing page");
// Go back
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
}
[TestMethod("WorkspacesEditor.Editor.ClickWorkspaceOpensEditingPage")]
[TestCategory("Workspaces UI")]
public void TestClickWorkspaceOpensEditingPage()
{
// Create a test workspace if none exist
if (!Has<Custom>("WorkspacesList"))
{
CreateTestWorkspace("TestWorkspace");
}
// Find first workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
// Click on the workspace item itself
workspaceItem.Click();
Thread.Sleep(1000);
// Verify editing page opened
Assert.IsTrue(Has<Button>("Save"), "Should have Save button on editing page");
Assert.IsTrue(Has<Button>("Cancel"), "Should have Cancel button on editing page");
// Go back
Find<Button>("Cancel").Click();
Thread.Sleep(1000);
}
*/
}

View File

@@ -23,6 +23,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" />
<ProjectReference Include="..\WorkspacesEditor\WorkspacesEditor.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,100 @@
// 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 WorkspacesEditorUITest;
[TestClass]
[Ignore("NOT STABLE")]
public class WorkspacesLauncherTest : WorkspacesUiAutomationBase
{
public WorkspacesLauncherTest()
: base()
{
}
[TestMethod("WorkspacesEditor.Launcher.LaunchFromEditor")]
[TestCategory("Workspaces UI")]
public void TestLaunchWorkspaceFromEditor()
{
ClearWorkspaces();
var uuid = Guid.NewGuid().ToString("N").Substring(0, 8);
CreateTestWorkspace(uuid);
CloseNotepad();
var launchButton = Find<Button>(By.Name("Launch"));
launchButton.Click();
Task.Delay(2000).Wait();
var processes = System.Diagnostics.Process.GetProcessesByName("notepad");
Assert.IsTrue(processes?.Length > 0);
}
[TestMethod("WorkspacesEditor.Launcher.CancelLaunch")]
[TestCategory("Workspaces UI")]
public void TestCancelLaunch()
{
// Create workspace with multiple apps
CreateWorkspaceWithApps();
// Launch workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
var launchButton = workspaceItem.Find<Button>("Launch");
launchButton.Click();
Thread.Sleep(1000);
// Cancel launch
if (Has<Button>("Cancel launch"))
{
Find<Button>("Cancel launch").Click();
Thread.Sleep(1000);
// Verify launcher closed
Assert.IsFalse(Has<Window>("Workspaces Launcher"), "Launcher window should close after cancel");
}
// Close any apps that may have launched
CloseTestApplications();
}
[TestMethod("WorkspacesEditor.Launcher.DismissKeepsLaunching")]
[TestCategory("Workspaces UI")]
public void TestDismissKeepsAppsLaunching()
{
// Create workspace with apps
CreateWorkspaceWithApps();
// Launch workspace
var workspacesList = Find<Custom>("WorkspacesList");
var workspaceItem = workspacesList.FindAll<Custom>(By.ClassName("WorkspaceItem"))[0];
var launchButton = workspaceItem.Find<Button>("Launch");
launchButton.Click();
Thread.Sleep(1000);
// Dismiss launcher
if (Has<Button>("Dismiss"))
{
Find<Button>("Dismiss").Click();
Thread.Sleep(1000);
// Verify launcher closed but apps continue launching
Assert.IsFalse(Has<Window>("Workspaces Launcher"), "Launcher window should close after dismiss");
// Wait for apps to finish launching
Thread.Sleep(3000);
// Verify apps launched (notepad should be open)
Assert.IsTrue(WindowHelper.IsWindowOpen("Notepad"), "Apps should continue launching after dismiss");
}
// Close launched apps
CloseTestApplications();
}
}

View File

@@ -0,0 +1,195 @@
// 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 WorkspacesEditorUITest;
[TestClass]
public class WorkspacesSettingsTests : UITestBase
{
public WorkspacesSettingsTests()
: base(PowerToysModule.PowerToysSettings, WindowSize.Medium)
{
}
[TestMethod("WorkspacesSettings.LaunchFromSettings")]
[TestCategory("Workspaces Settings UI")]
public void TestLaunchEditorFromSettingsPage()
{
GoToSettingsPageAndEnable();
}
[TestMethod("WorkspacesSettings.ActivationShortcut")]
[TestCategory("Workspaces Settings UI")]
public void TestActivationShortcutCustomization()
{
GoToSettingsPageAndEnable();
// Find the activation shortcut control
var shortcutButton = Find<Button>("Activation shortcut");
Assert.IsNotNull(shortcutButton, "Activation shortcut control should exist");
// Test customizing the shortcut
shortcutButton.Click();
Task.Delay(1000).Wait();
// Send new key combination (Win+Ctrl+W)
SendKeys(Key.Win, Key.Ctrl, Key.W);
var saveButton = Find<Button>("Save");
Assert.IsNotNull(saveButton, "Save button should exist after editing shortcut");
saveButton.Click();
var helpText = shortcutButton.HelpText;
Assert.AreEqual("Win + Ctrl + W", helpText, "Activation shortcut should be updated to Win + Ctrl + W");
}
[TestMethod("WorkspacesSettings.EnableToggle")]
[TestCategory("Workspaces Settings UI")]
public void TestEnableDisableModule()
{
GoToSettingsPageAndEnable();
// Find the enable toggle
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
Assert.IsNotNull(enableToggle, "Enable Workspaces toggle should exist");
Assert.IsTrue(enableToggle.IsOn, "Enable Workspaces toggle should be in the 'on' state");
// Toggle the state
enableToggle.Click();
Task.Delay(500).Wait();
// Verify state changed
Assert.IsFalse(enableToggle.IsOn, "Toggle state should change");
// Verify related controls are enabled/disabled accordingly
var launchButton = Find<Button>("Launch editor");
Assert.IsFalse(launchButton.Enabled, "Launch editor button should be disabled when module is disabled");
}
[TestMethod("WorkspacesSettings.LaunchByActivationShortcut")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until settings & runner can be connected in framework")]
public void TestLaunchEditorByActivationShortcut()
{
// Ensure module is enabled
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (!enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
// Close settings window to test shortcut
ExitScopeExe();
Thread.Sleep(1000);
// Default shortcut is Win+Ctrl+`
SendKeys(Key.Win, Key.Ctrl, Key.W);
Thread.Sleep(2000);
// Verify editor opened
Assert.IsTrue(WindowHelper.IsWindowOpen("Workspaces Editor"), "Workspaces Editor should open with activation shortcut");
// Reopen settings for next tests
RestartScopeExe();
NavigateToWorkspacesSettings();
}
[TestMethod("WorkspacesSettings.DisableModuleNoLaunch")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until settings & runner can be connected in framework")]
public void TestDisabledModuleDoesNotLaunchByShortcut()
{
// Disable the module
var enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
// Close settings to test shortcut
ExitScopeExe();
Thread.Sleep(1000);
// Try to launch with shortcut
SendKeys(Key.Win, Key.Ctrl, Key.W);
Thread.Sleep(2000);
// Verify editor did not open
Assert.IsFalse(WindowHelper.IsWindowOpen("Workspaces Editor"), "Workspaces Editor should not open when module is disabled");
// Reopen settings and re-enable the module
RestartScopeExe();
NavigateToWorkspacesSettings();
enableToggle = Find<ToggleSwitch>("Enable Workspaces");
if (!enableToggle.IsOn)
{
enableToggle.Click();
Thread.Sleep(500);
}
}
[TestMethod("WorkspacesSettings.QuickAccessLaunch")]
[TestCategory("Workspaces Settings UI")]
[Ignore("Wait until tray icon supported is in framework")]
public void TestLaunchFromQuickAccess()
{
// This test verifies the "quick access" mention in settings
// Look for any quick access related UI elements
var quickAccessInfo = FindAll(By.LinkText("quick access"));
if (quickAccessInfo.Count > 0)
{
Assert.IsTrue(quickAccessInfo.Count > 0, "Quick access information should be present in settings");
}
// Note: Actual system tray/quick access interaction would require
// more complex automation that might be platform-specific
}
private void NavigateToWorkspacesSettings()
{
// Find and click Workspaces in the navigation
var workspacesNavItem = Find<NavigationViewItem>("Workspaces");
workspacesNavItem.Click();
Thread.Sleep(1000);
}
private void GoToSettingsPageAndEnable()
{
if (this.FindAll<NavigationViewItem>("Workspaces").Count == 0)
{
this.Find<NavigationViewItem>("Windowing & Layouts").Click();
}
this.Find<NavigationViewItem>("Workspaces").Click();
var enableButton = this.Find<ToggleSwitch>("Enable Workspaces");
Assert.IsNotNull(enableButton, "Enable Workspaces toggle should exist");
if (!enableButton.IsOn)
{
enableButton.Click();
Task.Delay(500).Wait(); // Wait for the toggle animation and state change
}
// Verify it's now enabled
Assert.IsTrue(enableButton.IsOn, "Enable Workspaces toggle should be in the 'on' state");
}
private void AttachWorkspacesEditor()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.Workspaces);
}
}

View File

@@ -0,0 +1,82 @@
// 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;
using WorkspacesEditor.Utils;
namespace WorkspacesEditorUITest;
[TestClass]
public class WorkspacesSnapshotTests : WorkspacesUiAutomationBase
{
public WorkspacesSnapshotTests()
: base()
{
}
[TestMethod("WorkspacesSnapshot.CancelCapture")]
[TestCategory("Workspaces Snapshot UI")]
public void TestCaptureCancel()
{
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(1000).Wait();
AttachSnapshotWindow();
var cancelButton = Find<Button>("Cancel");
Assert.IsNotNull(cancelButton, "Capture button should exist");
cancelButton.Click();
}
[TestMethod("WorkspacesSnapshot.CapturePackagedApps")]
[TestCategory("Workspaces Snapshot UI")]
public void TestCapturePackagedApplications()
{
OpenCalculator();
// OpenWindowsSettings();
Task.Delay(2000).Wait();
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(1000).Wait();
AttachSnapshotWindow();
var captureButton = Find<Button>("Capture");
captureButton.Click();
Task.Delay(3000).Wait();
// Verify captured windows by reading the temporary workspaces file as the ground truth.
var editorIO = new WorkspacesEditorIO();
var workspace = editorIO.ParseTempProject();
Assert.IsNotNull(workspace, "Workspace data should be deserialized.");
Assert.IsNotNull(workspace.Applications, "Workspace should contain a list of apps.");
bool isCalculatorFound = workspace.Applications.Any(app => app.AppPath.Contains("Calculator", StringComparison.OrdinalIgnoreCase));
// bool isSettingsFound = workspace.Applications.Any(app => app.AppPath.Contains("Settings", StringComparison.OrdinalIgnoreCase));
Assert.IsTrue(isCalculatorFound, "Calculator should be captured in the workspace data.");
// Assert.IsTrue(isSettingsFound, "Settings should be captured in the workspace data.");
// Cancel to clean up
AttachWorkspacesEditor();
Find<Button>("Cancel").Click();
Task.Delay(1000).Wait();
// Close test applications
CloseCalculator();
// CloseWindowsSettings();
}
}

View File

@@ -0,0 +1,253 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.PowerToys.UITest;
namespace WorkspacesEditorUITest
{
public class WorkspacesUiAutomationBase : UITestBase
{
public WorkspacesUiAutomationBase()
: base(PowerToysModule.Workspaces, WindowSize.Medium)
{
}
protected void CreateTestWorkspace(string name)
{
// Open notepad for capture
OpenNotepad();
Task.Delay(1000).Wait();
// Create workspace
AttachWorkspacesEditor();
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Task.Delay(500).Wait();
// Capture
AttachSnapshotWindow();
var captureButton = Find<Button>("Capture");
captureButton.Click();
Task.Delay(5000).Wait();
// Set name
var nameTextBox = Find<TextBox>("EditNameTextBox");
nameTextBox.SetText(name);
// Save
Find<Button>("Save Workspace").Click();
// Close notepad
CloseNotepad();
}
// DO NOT USE UNTIL FRAMEWORK AVAILABLE, CAN'T FIND BUTTON FOR SECOND LOOP
protected void ClearWorkspaces()
{
while (true)
{
try
{
var root = Find<Element>(By.AccessibilityId("WorkspacesItemsControl"));
var buttons = root.FindAll<Button>(By.AccessibilityId("MoreButton"));
Debug.WriteLine($"Found {buttons.Count} button");
var button = buttons[^1];
button.Click();
Task.Delay(500).Wait();
var remove = Find<Button>(By.Name("Remove"));
remove.Click();
Task.Delay(500).Wait();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
break;
}
}
}
protected void CreateWorkspaceWithApps()
{
// Open multiple test applications
OpenTestApplications();
Thread.Sleep(3000);
// Create workspace
var createButton = Find<Button>("Create Workspace");
createButton.Click();
Thread.Sleep(1000);
// Capture
var captureButton = Find<Button>("Capture");
captureButton.Click();
Thread.Sleep(2000);
// Save
Find<Button>("Save").Click();
Thread.Sleep(1000);
// Close test applications
CloseTestApplications();
}
protected void OpenTestApplications()
{
OpenNotepad();
// Could add more applications here
Thread.Sleep(1000);
}
protected void CloseTestApplications()
{
CloseNotepad();
}
protected void CloseWorkspacesEditor()
{
// Find and close the Workspaces Editor window
if (WindowHelper.IsWindowOpen("Workspaces Editor"))
{
var editorWindow = Find<Window>("Workspaces Editor");
editorWindow.Close();
Thread.Sleep(1000);
}
}
protected void ResetShortcutToDefault(Custom shortcutControl)
{
// Right-click on the shortcut control to open context menu
shortcutControl.Click(rightClick: true);
Thread.Sleep(500);
// Look for a "Reset to default" or similar option in the context menu
try
{
// Try to find various possible menu item texts for reset option
var resetOption = Find("Reset to default");
resetOption?.Click();
}
catch
{
try
{
// Try alternative text
var resetOption = Find("Reset");
resetOption?.Click();
}
catch
{
try
{
// Try another alternative
var resetOption = Find("Default");
resetOption?.Click();
}
catch
{
// If context menu doesn't have reset option, try keyboard shortcut
// ESC to close any open menus first
SendKeys(Key.Esc);
Thread.Sleep(200);
// Click on the control and try to clear it with standard shortcuts
shortcutControl.Click();
Thread.Sleep(200);
// Try Ctrl+A to select all, then Delete to clear
SendKeys(Key.Ctrl, Key.A);
Thread.Sleep(100);
SendKeys(Key.Delete);
Thread.Sleep(500);
}
}
}
}
protected void OpenNotepad()
{
var process = System.Diagnostics.Process.Start("notepad.exe");
Task.Delay(1000).Wait();
}
protected void CloseNotepad()
{
var processes = System.Diagnostics.Process.GetProcessesByName("notepad");
foreach (var process in processes)
{
try
{
process.Kill();
process.WaitForExit();
}
catch
{
// ignore
}
}
}
private void AttachPowertoySetting()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.PowerToysSettings);
}
protected void AttachWorkspacesEditor()
{
Task.Delay(200).Wait();
this.Session.Attach(PowerToysModule.Workspaces);
}
protected void AttachSnapshotWindow()
{
Task.Delay(5000).Wait();
this.Session.Attach("Snapshot Creator");
}
protected void OpenCalculator()
{
Process.Start("calc.exe");
Task.Delay(1000).Wait();
}
protected void CloseCalculator()
{
foreach (var process in Process.GetProcessesByName("CalculatorApp"))
{
process.Kill();
}
foreach (var process in Process.GetProcessesByName("Calculator"))
{
process.Kill();
}
}
protected void OpenWindowsSettings()
{
Process.Start(new ProcessStartInfo
{
FileName = "ms-settings:",
UseShellExecute = true,
});
Task.Delay(500).Wait();
}
protected void CloseWindowsSettings()
{
foreach (var process in Process.GetProcessesByName("SystemSettings"))
{
process.Kill();
}
}
}
}

View File

@@ -12,6 +12,6 @@
<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.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.7" />
</ItemGroup>
</Project>

View File

@@ -21,7 +21,7 @@ public partial class AppStateModel : ObservableObject
///////////////////////////////////////////////////////////////////////////
// STATE HERE
public RecentCommandsManager RecentCommands { get; private set; } = new();
public RecentCommandsManager RecentCommands { get; set; } = new();
// END SETTINGS
///////////////////////////////////////////////////////////////////////////

View File

@@ -32,22 +32,6 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_title)}},
"size": "large"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_text)}},
"wrap": true
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_header)}},
"weight": "bolder",
"size": "default"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}},
"wrap": true
},
{
"type": "Input.Text",
"label": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_label)}},
@@ -59,14 +43,11 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_header)}},
"weight": "bolder",
"size": "default"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}},
"wrap": true
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}},
"wrap": true,
"size": "small",
"isSubtle": true,
"spacing": "none"
},
{
"type": "Input.Text",
@@ -74,18 +55,16 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
"isRequired": true,
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_required)}},
"id": "DisplayName",
"placeholder": "My new extension"
"placeholder": "My new extension",
"spacing": "medium"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_header)}},
"weight": "bolder",
"size": "default"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}},
"wrap": true
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}},
"wrap": true,
"size": "small",
"isSubtle": true,
"spacing": "none"
},
{
"type": "Input.Text",
@@ -93,7 +72,16 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase
"isRequired": true,
"errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_required)}},
"id": "OutputPath",
"placeholder": "C:\\users\\me\\dev"
"placeholder": "C:\\users\\me\\dev",
"spacing": "medium"
},
{
"type": "TextBlock",
"text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}},
"wrap": true,
"size": "small",
"isSubtle": true,
"spacing": "none"
}
],
"actions": [

View File

@@ -70,7 +70,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
/// <summary>
/// Looks up a localized string similar to Where should the new extension be created? This path will be created if it doesn&apos;t exist.
/// Looks up a localized string similar to Select the folder where the new extension will be created. The path will be created if it doesn&apos;t exist..
/// </summary>
public static string builtin_create_extension_directory_description {
get {
@@ -78,15 +78,6 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Output path.
/// </summary>
public static string builtin_create_extension_directory_header {
get {
return ResourceManager.GetString("builtin_create_extension_directory_header", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Output path.
/// </summary>
@@ -106,7 +97,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
/// <summary>
/// Looks up a localized string similar to The name of your extension as users will see it..
/// Looks up a localized string similar to The name of the extension as it will appear to users..
/// </summary>
public static string builtin_create_extension_display_name_description {
get {
@@ -114,15 +105,6 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Display name.
/// </summary>
public static string builtin_create_extension_display_name_header {
get {
return ResourceManager.GetString("builtin_create_extension_display_name_header", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Display name.
/// </summary>
@@ -151,7 +133,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
/// <summary>
/// Looks up a localized string similar to This is the name of your new extension project. It should be a valid C# class name. Best practice is to also include the word &apos;Extension&apos; in the name..
/// Looks up a localized string similar to Enter a valid C# class name for the new extension project. It&apos;s recommended to include the word &quot;Extension&quot; in the name..
/// </summary>
public static string builtin_create_extension_name_description {
get {
@@ -159,15 +141,6 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Extension name.
/// </summary>
public static string builtin_create_extension_name_header {
get {
return ResourceManager.GetString("builtin_create_extension_name_header", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Extension name.
/// </summary>
@@ -205,16 +178,7 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
}
/// <summary>
/// Looks up a localized string similar to Use this page to create a new extension project..
/// </summary>
public static string builtin_create_extension_page_text {
get {
return ResourceManager.GetString("builtin_create_extension_page_text", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create your new extension.
/// Looks up a localized string similar to Create a new extension.
/// </summary>
public static string builtin_create_extension_page_title {
get {

View File

@@ -180,16 +180,10 @@
<value>Once you're ready to test deploy the package locally with Visual Studio, then run the \"Reload\" command in the Command Palette to load your new extension.</value>
</data>
<data name="builtin_create_extension_page_title" xml:space="preserve">
<value>Create your new extension</value>
</data>
<data name="builtin_create_extension_page_text" xml:space="preserve">
<value>Use this page to create a new extension project.</value>
</data>
<data name="builtin_create_extension_name_header" xml:space="preserve">
<value>Extension name</value>
<value>Create a new extension</value>
</data>
<data name="builtin_create_extension_name_description" xml:space="preserve">
<value>This is the name of your new extension project. It should be a valid C# class name. Best practice is to also include the word 'Extension' in the name.</value>
<value>Enter a valid C# class name for the new extension project. It's recommended to include the word "Extension" in the name.</value>
</data>
<data name="builtin_create_extension_name_label" xml:space="preserve">
<value>Extension name</value>
@@ -197,11 +191,8 @@
<data name="builtin_create_extension_name_required" xml:space="preserve">
<value>Extension name is required and must be a valid C# identifier (start with a letter or underscore, followed by letters, numbers, or underscores)</value>
</data>
<data name="builtin_create_extension_display_name_header" xml:space="preserve">
<value>Display name</value>
</data>
<data name="builtin_create_extension_display_name_description" xml:space="preserve">
<value>The name of your extension as users will see it.</value>
<value>The name of the extension as it will appear to users.</value>
</data>
<data name="builtin_create_extension_display_name_label" xml:space="preserve">
<value>Display name</value>
@@ -209,11 +200,8 @@
<data name="builtin_create_extension_display_name_required" xml:space="preserve">
<value>Display name is required</value>
</data>
<data name="builtin_create_extension_directory_header" xml:space="preserve">
<value>Output path</value>
</data>
<data name="builtin_create_extension_directory_description" xml:space="preserve">
<value>Where should the new extension be created? This path will be created if it doesn't exist</value>
<value>Select the folder where the new extension will be created. The path will be created if it doesn't exist.</value>
</data>
<data name="builtin_create_extension_directory_label" xml:space="preserve">
<value>Output path</value>

View File

@@ -130,7 +130,7 @@ public partial class SettingsModel : ObservableObject
{
foreach (var item in newSettings)
{
savedSettings[item.Key] = item.Value != null ? item.Value.DeepClone() : null;
savedSettings[item.Key] = item.Value?.DeepClone();
}
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.Options);
@@ -188,6 +188,8 @@ public partial class SettingsModel : ObservableObject
[JsonSerializable(typeof(HistoryItem))]
[JsonSerializable(typeof(SettingsModel))]
[JsonSerializable(typeof(AppStateModel))]
[JsonSerializable(typeof(RecentCommandsManager))]
[JsonSerializable(typeof(List<string>), TypeInfoPropertyName = "StringList")]
[JsonSerializable(typeof(List<HistoryItem>), TypeInfoPropertyName = "HistoryList")]
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)]

View File

@@ -38,7 +38,7 @@ public sealed class AdaptiveCardsConfig
"fontFamily": "'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
"fontSizes": {
"small": 12,
"default": 12,
"default": 14,
"medium": 14,
"large": 20,
"extraLarge": 26
@@ -199,7 +199,7 @@ public sealed class AdaptiveCardsConfig
"fontFamily": "'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
"fontSizes": {
"small": 12,
"default": 12,
"default": 14,
"medium": 14,
"large": 20,
"extraLarge": 26

View File

@@ -100,6 +100,16 @@
</Grid>
</DataTemplate>
<Style x:Key="HotkeyStyle" TargetType="Border">
<Style.Setters>
<Setter Property="Padding" Value="4" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Background" Value="{ThemeResource ControlFillColorSecondaryBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource DividerStrokeColorDefaultBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="6" />
</Style.Setters>
</Style>
</ResourceDictionary>
</UserControl.Resources>
@@ -193,17 +203,12 @@
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.PrimaryCommand.Name, Mode=OneWay}" />
<Border
Padding="4"
Background="{ThemeResource SubtleFillColorSecondaryBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="4">
<Border Style="{StaticResource HotkeyStyle}">
<FontIcon
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Glyph="&#xE751;" />
</Border>
</StackPanel>
@@ -221,32 +226,20 @@
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.SecondaryCommand.Name, Mode=OneWay}" />
<StackPanel Orientation="Horizontal" Spacing="2">
<Border
Padding="4,2,4,2"
VerticalAlignment="Center"
Background="{ThemeResource SubtleFillColorSecondaryBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="4">
<StackPanel Orientation="Horizontal" Spacing="4">
<Border Padding="4,2,4,2" Style="{StaticResource HotkeyStyle}">
<TextBlock
CharacterSpacing="4"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Text="Ctrl" />
</Border>
<Border
Padding="4"
VerticalAlignment="Center"
Background="{ThemeResource SubtleFillColorSecondaryBrush}"
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="4">
<Border Style="{StaticResource HotkeyStyle}">
<FontIcon
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="10"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Glyph="&#xE751;" />
</Border>
</StackPanel>
@@ -267,7 +260,6 @@
Opened="Flyout_Opened"
Placement="TopEdgeAlignedRight">
<StackPanel>
<ListView
x:Name="CommandsDropdown"
MinWidth="248"
@@ -288,7 +280,6 @@
<TransitionCollection />
</ListView.ItemContainerTransitions>
</ListView>
<TextBox
x:Name="ContextFilterBox"
x:Uid="ContextFilterBox"
@@ -296,7 +287,6 @@
KeyDown="ContextFilterBox_KeyDown"
PreviewKeyDown="ContextFilterBox_PreviewKeyDown"
TextChanged="ContextFilterBox_TextChanged" />
</StackPanel>
</Flyout>
</Button.Flyout>

View File

@@ -19,6 +19,5 @@
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="ContentGrid" />
</UserControl>

View File

@@ -6,14 +6,14 @@
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="TagBackground" ResourceKey="ControlSolidFillColorDefaultBrush" />
<StaticResource x:Key="TagBorderBrush" ResourceKey="ControlStrokeColorSecondaryBrush" />
<StaticResource x:Key="TagBackground" ResourceKey="ControlFillColorSecondaryBrush" />
<StaticResource x:Key="TagBorderBrush" ResourceKey="DividerStrokeColorDefaultBrush" />
<StaticResource x:Key="TagForeground" ResourceKey="TextFillColorTertiaryBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="TagBackground" ResourceKey="ControlSolidFillColorDefaultBrush" />
<StaticResource x:Key="TagBorderBrush" ResourceKey="ControlStrokeColorSecondaryBrush" />
<StaticResource x:Key="TagBackground" ResourceKey="ControlFillColorSecondaryBrush" />
<StaticResource x:Key="TagBorderBrush" ResourceKey="DividerStrokeColorDefaultBrush" />
<StaticResource x:Key="TagForeground" ResourceKey="TextFillColorTertiaryBrush" />
</ResourceDictionary>

View File

@@ -108,7 +108,7 @@
</ResourceDictionary>
</Page.Resources>
<Grid Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
<Grid>
<ScrollView VerticalAlignment="Top" VerticalScrollMode="Enabled">
<ItemsRepeater
VerticalAlignment="Stretch"

View File

@@ -380,19 +380,19 @@
To="24,0,0"
Duration="0:0:0.187" />
</animations:Implicit.HideAnimations>
<Grid Margin="12">
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<cpcontrols:IconBox
x:Name="HeroImageBorder"
Width="64"
Margin="16,8,16,16"
HorizontalAlignment="Center"
Margin="0,0,16,16"
HorizontalAlignment="Left"
AutomationProperties.AccessibilityView="Raw"
SourceKey="{x:Bind ViewModel.Details.HeroImage, Mode=OneWay}"
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}"
@@ -400,17 +400,15 @@
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
FontSize="18"
FontWeight="SemiBold"
Text="{x:Bind ViewModel.Details.Title, Mode=OneWay}"
TextAlignment="Center"
TextWrapping="WrapWholeWords"
Visibility="{x:Bind ViewModel.Details.Title, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}" />
<toolkit:MarkdownTextBlock
Grid.Row="2"
Margin="0,12,0,24"
Margin="0,4,0,24"
Background="Transparent"
Header3FontSize="12"
Header3FontWeight="Normal"

View File

@@ -22,8 +22,7 @@ namespace Microsoft.CmdPal.Ext.WindowsSettings;
internal static class ResultHelper
{
internal static List<ListItem> GetResultList(
in IEnumerable<Classes.WindowsSetting> list,
string query)
in IEnumerable<Classes.WindowsSetting> list)
{
var resultList = new List<ListItem>(list.Count());

View File

@@ -0,0 +1,84 @@
// 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.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.WindowsSettings.Classes;
namespace Microsoft.CmdPal.Ext.WindowsSettings.Helpers;
internal static class ScoringHelper
{
// Rank settings by how they matched the search query. Order is:
// 1. Exact Name (10 points)
// 2. Name Starts With (8 points)
// 3. Name (5 points)
// 4. Area (4 points)
// 5. AltName (2 points)
// 6. Settings path (1 point)
internal static (WindowsSetting Setting, int Score) SearchScoringPredicate(string query, WindowsSetting setting)
{
if (string.IsNullOrWhiteSpace(query))
{
// If no search string is entered skip query comparison.
return (setting, 0);
}
if (string.Equals(setting.Name, query, StringComparison.OrdinalIgnoreCase))
{
return (setting, 10);
}
if (setting.Name.StartsWith(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 8);
}
if (setting.Name.Contains(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 5);
}
if (!(setting.Areas is null))
{
foreach (var area in setting.Areas)
{
// Search for areas on normal queries.
if (area.Contains(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 4);
}
// Search for Area only on queries with action char.
if (area.Contains(query.Replace(":", string.Empty), StringComparison.CurrentCultureIgnoreCase)
&& query.EndsWith(":", StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 4);
}
}
}
if (!(setting.AltNames is null))
{
foreach (var altName in setting.AltNames)
{
if (altName.Contains(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 2);
}
}
}
// Search by key char '>' for app name and settings path
if (query.Contains('>') && ResultHelper.FilterBySettingsPath(setting, query))
{
return (setting, 1);
}
return (setting, 0);
}
}

View File

@@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Microsoft.CmdPal.Ext.WindowsSettings.Classes;
using Microsoft.CmdPal.Ext.WindowsSettings.Commands;
using Microsoft.CmdPal.Ext.WindowsSettings.Helpers;
using Microsoft.CmdPal.Ext.WindowsSettings.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.WindowsSettings.Pages;
internal sealed partial class FallbackWindowsSettingsItem : FallbackCommandItem
{
private readonly Classes.WindowsSettings _windowsSettings;
private readonly string _title = Resources.settings_fallback_title;
private readonly string _subtitle = Resources.settings_fallback_subtitle;
public FallbackWindowsSettingsItem(Classes.WindowsSettings windowsSettings)
: base(new NoOpCommand(), Resources.settings_title)
{
Icon = IconHelpers.FromRelativePath("Assets\\WindowsSettings.svg");
_windowsSettings = windowsSettings;
}
public override void UpdateQuery(string query)
{
Command = new NoOpCommand();
Title = string.Empty;
Subtitle = string.Empty;
Icon = null;
MoreCommands = null;
if (string.IsNullOrWhiteSpace(query) ||
_windowsSettings?.Settings is null)
{
return;
}
var filteredList = _windowsSettings.Settings
.Select(setting => ScoringHelper.SearchScoringPredicate(query, setting))
.Where(scoredSetting => scoredSetting.Score > 0)
.OrderByDescending(scoredSetting => scoredSetting.Score);
if (!filteredList.Any())
{
return;
}
if (filteredList.Count() == 1 ||
filteredList.Any(a => a.Score == 10))
{
var setting = filteredList.First().Setting;
Title = setting.Name;
Subtitle = setting.JoinedFullSettingsPath;
Icon = IconHelpers.FromRelativePath("Assets\\WindowsSettings.svg");
Command = new OpenSettingsCommand(setting)
{
Icon = IconHelpers.FromRelativePath("Assets\\WindowsSettings.svg"),
Name = setting.Name,
};
// There is a case with MMC snap-ins where we don't have .msc files fort them. Then we need to show the note for this results in subtitle too.
// These results have mmc.exe as command and their note property is filled.
if (setting.Command == "mmc.exe" && !string.IsNullOrEmpty(setting.Note))
{
Subtitle += $"\u0020\u0020\u002D\u0020\u0020{Resources.Note}: {setting.Note}"; // "\u0020\u0020\u002D\u0020\u0020" = "<space><space><minus><space><space>"
}
return;
}
// We found more than one result. Make our command take
// us to the Windows Settings search page, prepopulated with this search.
var settingsPage = new WindowsSettingsListPage(_windowsSettings, query);
Title = string.Format(CultureInfo.CurrentCulture, _title, query);
Icon = IconHelpers.FromRelativePath("Assets\\WindowsSettings.svg");
Subtitle = _subtitle;
Command = settingsPage;
return;
}
}

View File

@@ -6,6 +6,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CmdPal.Ext.WindowsSettings.Classes;
using Microsoft.CmdPal.Ext.WindowsSettings.Helpers;
using Microsoft.CmdPal.Ext.WindowsSettings.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -18,11 +20,17 @@ internal sealed partial class WindowsSettingsListPage : DynamicListPage
public WindowsSettingsListPage(Classes.WindowsSettings windowsSettings)
{
Icon = IconHelpers.FromRelativePath("Assets\\WindowsSettings.svg");
Name = "Windows Settings";
Name = Resources.settings_title;
Id = "com.microsoft.cmdpal.windowsSettings";
_windowsSettings = windowsSettings;
}
public WindowsSettingsListPage(Classes.WindowsSettings windowsSettings, string query)
: this(windowsSettings)
{
SearchText = query;
}
public List<ListItem> Query(string query)
{
if (_windowsSettings?.Settings is null)
@@ -31,87 +39,21 @@ internal sealed partial class WindowsSettingsListPage : DynamicListPage
}
var filteredList = _windowsSettings.Settings
.Select(SearchScoringPredicate)
.Select(setting => ScoringHelper.SearchScoringPredicate(query, setting))
.Where(scoredSetting => scoredSetting.Score > 0)
.OrderByDescending(scoredSetting => scoredSetting.Score)
.Select(scoredSetting => scoredSetting.Setting);
var newList = ResultHelper.GetResultList(filteredList, query);
var newList = ResultHelper.GetResultList(filteredList);
return newList;
// Rank settings by how they matched the search query. Order is:
// 1. Exact Name (10 points)
// 2. Name Starts With (8 points)
// 3. Name (5 points)
// 4. Area (4 points)
// 5. AltName (2 points)
// 6. Settings path (1 point)
(WindowsSetting Setting, int Score) SearchScoringPredicate(WindowsSetting setting)
{
if (string.IsNullOrWhiteSpace(query))
{
// If no search string is entered skip query comparison.
return (setting, 0);
}
if (string.Equals(setting.Name, query, StringComparison.OrdinalIgnoreCase))
{
return (setting, 10);
}
if (setting.Name.StartsWith(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 8);
}
if (setting.Name.Contains(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 5);
}
if (!(setting.Areas is null))
{
foreach (var area in setting.Areas)
{
// Search for areas on normal queries.
if (area.Contains(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 4);
}
// Search for Area only on queries with action char.
if (area.Contains(query.Replace(":", string.Empty), StringComparison.CurrentCultureIgnoreCase)
&& query.EndsWith(":", StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 4);
}
}
}
if (!(setting.AltNames is null))
{
foreach (var altName in setting.AltNames)
{
if (altName.Contains(query, StringComparison.CurrentCultureIgnoreCase))
{
return (setting, 2);
}
}
}
// Search by key char '>' for app name and settings path
if (query.Contains('>') && ResultHelper.FilterBySettingsPath(setting, query))
{
return (setting, 1);
}
return (setting, 0);
}
}
public override void UpdateSearchText(string oldSearch, string newSearch)
{
RaiseItemsChanged(0);
if (oldSearch != newSearch)
{
RaiseItemsChanged(0);
}
}
public override IListItem[] GetItems()

View File

@@ -3840,6 +3840,42 @@ namespace Microsoft.CmdPal.Ext.WindowsSettings.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Search Windows settings for this device.
/// </summary>
internal static string settings_fallback_subtitle {
get {
return ResourceManager.GetString("settings_fallback_subtitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search for &quot;{0}&quot; in Windows settings.
/// </summary>
internal static string settings_fallback_title {
get {
return ResourceManager.GetString("settings_fallback_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Navigate to specific Windows settings.
/// </summary>
internal static string settings_subtitle {
get {
return ResourceManager.GetString("settings_subtitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Windows Settings.
/// </summary>
internal static string settings_title {
get {
return ResourceManager.GetString("settings_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Settings app.
/// </summary>

View File

@@ -2085,4 +2085,16 @@
<data name="WindowsSettingsProvider_DisplayName" xml:space="preserve">
<value>Windows Settings</value>
</data>
<data name="settings_title" xml:space="preserve">
<value>Windows Settings</value>
</data>
<data name="settings_subtitle" xml:space="preserve">
<value>Navigate to specific Windows settings</value>
</data>
<data name="settings_fallback_title" xml:space="preserve">
<value>Search for "{0}" in Windows settings</value>
</data>
<data name="settings_fallback_subtitle" xml:space="preserve">
<value>Search Windows settings for this device</value>
</data>
</root>

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Ext.WindowsSettings.Helpers;
using Microsoft.CmdPal.Ext.WindowsSettings.Pages;
using Microsoft.CmdPal.Ext.WindowsSettings.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -17,6 +18,8 @@ public partial class WindowsSettingsCommandsProvider : CommandProvider
private readonly WindowsSettings.Classes.WindowsSettings? _windowsSettings;
#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
private readonly FallbackWindowsSettingsItem _fallback;
public WindowsSettingsCommandsProvider()
{
Id = "Windows.Settings";
@@ -26,9 +29,10 @@ public partial class WindowsSettingsCommandsProvider : CommandProvider
_windowsSettings = JsonSettingsListHelper.ReadAllPossibleSettings();
_searchSettingsListItem = new CommandItem(new WindowsSettingsListPage(_windowsSettings))
{
Title = "Windows Settings",
Subtitle = "Navigate to specific Windows settings",
Title = Resources.settings_title,
Subtitle = Resources.settings_subtitle,
};
_fallback = new(_windowsSettings);
UnsupportedSettingsHelper.FilterByBuild(_windowsSettings);
@@ -42,4 +46,6 @@ public partial class WindowsSettingsCommandsProvider : CommandProvider
_searchSettingsListItem
];
}
public override IFallbackCommandItem[] FallbackCommands() => [_fallback];
}

View File

@@ -615,7 +615,7 @@ namespace UITests_FancyZones
// test settings
Microsoft.PowerToys.UITest.CheckBox useShiftCheckBox = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Hold Shift key to activate zones while dragging a window");
Microsoft.PowerToys.UITest.CheckBox useNonPrimaryMouseCheckBox = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Use a non-primary mouse button to toggle zone activation");
Microsoft.PowerToys.UITest.CheckBox makeDraggedWindowTransparent = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Make dragged window transparent");
Microsoft.PowerToys.UITest.CheckBox makeDraggedWindowTransparent = this.Find<Microsoft.PowerToys.UITest.CheckBox>("Make the dragged window transparent");
Find<Microsoft.PowerToys.UITest.CheckBox>("Show zone number").SetCheck(false, 100);
Find<Slider>("Opacity (%)").QuickSetValue(100); // make highlight color visible with opacity 100

View File

@@ -34,7 +34,7 @@
<TargetName>PowerToys.ImageResizerContextMenu</TargetName>
<!-- Needs a different int dir to avoid conflicts in msix creation. -->
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\TemporaryBuild\obj\$(ProjectName)\</IntDir>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>

View File

@@ -29,7 +29,7 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
</PropertyGroup>
<PropertyGroup>
<TargetName>PowerToys.$(ProjectName)</TargetName>

View File

@@ -26,7 +26,7 @@
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
<TargetName>PowerToys.ImageResizerExt</TargetName>
</PropertyGroup>
<ItemDefinitionGroup>

View File

@@ -5,7 +5,7 @@
<PropertyGroup>
<AssemblyTitle>PowerToys.ImageResizer</AssemblyTitle>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>

View File

@@ -363,7 +363,7 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage
RenamedEventArgs e = new RenamedEventArgs(WatcherChangeTypes.Renamed, directory, path, oldpath);
string oldFullPath = directory + "\\" + oldpath;
string fullPath = directory + "\\" + path;
string newFullPath = directory + "\\" + path;
string linkingTo = Directory.GetCurrentDirectory();
// ShellLinkHelper must be mocked for lnk applications
@@ -372,19 +372,8 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage
Win32Program.ShellLinkHelper = mockShellLink.Object;
// old item and new item are the actual items when they are in existence
Win32Program olditem = new Win32Program
{
Name = "oldpath",
ExecutableName = oldpath,
FullPath = linkingTo,
};
Win32Program newitem = new Win32Program
{
Name = "path",
ExecutableName = path,
FullPath = linkingTo,
};
Win32Program olditem = Win32Program.GetAppFromPath(oldFullPath);
Win32Program newitem = Win32Program.GetAppFromPath(newFullPath);
win32ProgramRepository.Add(olditem);

View File

@@ -149,7 +149,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
std::vector<std::wstring_view> knownModules = {
L"PowerToys.FancyZonesModuleInterface.dll",
L"PowerToys.powerpreview.dll",
L"PowerToys.ImageResizerExt.dll",
L"WinUI3Apps/PowerToys.ImageResizerExt.dll",
L"PowerToys.KeyboardManager.dll",
L"PowerToys.Launcher.dll",
L"WinUI3Apps/PowerToys.PowerRenameExt.dll",

View File

@@ -671,14 +671,22 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
{
switch (value)
{
case ESettingsWindowNames::Dashboard:
return "Dashboard";
case ESettingsWindowNames::Overview:
return "Overview";
case ESettingsWindowNames::AlwaysOnTop:
return "AlwaysOnTop";
case ESettingsWindowNames::Awake:
return "Awake";
case ESettingsWindowNames::ColorPicker:
return "ColorPicker";
case ESettingsWindowNames::CmdNotFound:
return "CmdNotFound";
case ESettingsWindowNames::FancyZones:
return "FancyZones";
case ESettingsWindowNames::FileLocksmith:
return "FileLocksmith";
case ESettingsWindowNames::Run:
return "Run";
case ESettingsWindowNames::ImageResizer:
@@ -687,6 +695,16 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
return "KBM";
case ESettingsWindowNames::MouseUtils:
return "MouseUtils";
case ESettingsWindowNames::MouseWithoutBorders:
return "MouseWithoutBorders";
case ESettingsWindowNames::Peek:
return "Peek";
case ESettingsWindowNames::PowerAccent:
return "PowerAccent";
case ESettingsWindowNames::PowerLauncher:
return "PowerLauncher";
case ESettingsWindowNames::PowerPreview:
return "PowerPreview";
case ESettingsWindowNames::PowerRename:
return "PowerRename";
case ESettingsWindowNames::FileExplorer:
@@ -707,8 +725,6 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
return "CropAndLock";
case ESettingsWindowNames::EnvironmentVariables:
return "EnvironmentVariables";
case ESettingsWindowNames::Dashboard:
return "Dashboard";
case ESettingsWindowNames::AdvancedPaste:
return "AdvancedPaste";
case ESettingsWindowNames::NewPlus:
@@ -728,10 +744,18 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
if (value == "Overview")
if (value == "Dashboard")
{
return ESettingsWindowNames::Dashboard;
}
else if (value == "Overview")
{
return ESettingsWindowNames::Overview;
}
else if (value == "AlwaysOnTop")
{
return ESettingsWindowNames::AlwaysOnTop;
}
else if (value == "Awake")
{
return ESettingsWindowNames::Awake;
@@ -740,10 +764,18 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::ColorPicker;
}
else if (value == "CmdNotFound")
{
return ESettingsWindowNames::CmdNotFound;
}
else if (value == "FancyZones")
{
return ESettingsWindowNames::FancyZones;
}
else if (value == "FileLocksmith")
{
return ESettingsWindowNames::FileLocksmith;
}
else if (value == "Run")
{
return ESettingsWindowNames::Run;
@@ -760,6 +792,26 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::MouseUtils;
}
else if (value == "MouseWithoutBorders")
{
return ESettingsWindowNames::MouseWithoutBorders;
}
else if (value == "Peek")
{
return ESettingsWindowNames::Peek;
}
else if (value == "PowerAccent")
{
return ESettingsWindowNames::PowerAccent;
}
else if (value == "PowerLauncher")
{
return ESettingsWindowNames::PowerLauncher;
}
else if (value == "PowerPreview")
{
return ESettingsWindowNames::PowerPreview;
}
else if (value == "PowerRename")
{
return ESettingsWindowNames::PowerRename;
@@ -800,10 +852,6 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::EnvironmentVariables;
}
else if (value == "Dashboard")
{
return ESettingsWindowNames::Dashboard;
}
else if (value == "AdvancedPaste")
{
return ESettingsWindowNames::AdvancedPaste;

View File

@@ -6,13 +6,21 @@ enum class ESettingsWindowNames
{
Dashboard = 0,
Overview,
AlwaysOnTop,
Awake,
ColorPicker,
CmdNotFound,
FancyZones,
FileLocksmith,
Run,
ImageResizer,
KBM,
MouseUtils,
MouseWithoutBorders,
Peek,
PowerAccent,
PowerLauncher,
PowerPreview,
PowerRename,
FileExplorer,
ShortcutGuide,

View File

@@ -436,6 +436,10 @@ namespace Microsoft.PowerToys.Settings.UI
case "KBM": return typeof(KeyboardManagerPage);
case "MouseUtils": return typeof(MouseUtilsPage);
case "MouseWithoutBorders": return typeof(MouseWithoutBordersPage);
case "Peek": return typeof(PeekPage);
case "PowerAccent": return typeof(PowerAccentPage);
case "PowerLauncher": return typeof(PowerLauncherPage);
case "PowerPreview": return typeof(PowerPreviewPage);
case "PowerRename": return typeof(PowerRenamePage);
case "QuickAccent": return typeof(PowerAccentPage);
case "FileExplorer": return typeof(PowerPreviewPage);
@@ -444,7 +448,6 @@ namespace Microsoft.PowerToys.Settings.UI
case "MeasureTool": return typeof(MeasureToolPage);
case "Hosts": return typeof(HostsPage);
case "RegistryPreview": return typeof(RegistryPreviewPage);
case "Peek": return typeof(PeekPage);
case "CropAndLock": return typeof(CropAndLockPage);
case "EnvironmentVariables": return typeof(EnvironmentVariablesPage);
case "NewPlus": return typeof(NewPlusPage);

View File

@@ -12,6 +12,7 @@
<UserControl.Resources>
<x:Double x:Key="PageMaxWidth">1000</x:Double>
<x:Double x:Key="PageHeaderMaxWidth">1020</x:Double>
<tkconverters:DoubleToVisibilityConverter
x:Name="doubleToVisibilityConverter"
FalseValue="Collapsed"
@@ -26,7 +27,7 @@
</Grid.RowDefinitions>
<TextBlock
x:Name="Header"
MaxWidth="{StaticResource PageMaxWidth}"
MaxWidth="{StaticResource PageHeaderMaxWidth}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutomationProperties.HeadingLevel="1"

View File

@@ -88,16 +88,9 @@
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsExpander
x:Uid="AlwaysOnTop_SoundTitle"
HeaderIcon="{ui:FontIcon Glyph=&#xE7F3;}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard ContentAlignment="Left">
<CheckBox x:Uid="AlwaysOnTop_Sound" IsChecked="{x:Bind ViewModel.SoundEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<tkcontrols:SettingsCard x:Uid="AlwaysOnTop_Sound" HeaderIcon="{ui:FontIcon Glyph=&#xE7F3;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.SoundEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="ExcludedApps" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">

View File

@@ -294,7 +294,7 @@
<tkcontrols:SettingsCard x:Uid="General_SettingsBackupAndRestoreLocationText">
<Grid ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
@@ -405,76 +405,83 @@
</InfoBar.IconSource>
</InfoBar>
</controls:SettingsGroup>
<controls:SettingsGroup x:Uid="General_DiagnosticsAndFeedback" Visibility="Visible">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<HyperlinkButton
x:Uid="GeneralPage_DiagnosticsAndFeedback_Link"
Margin="0,0,0,8"
Padding="0"
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
<tkcontrols:SettingsCard
x:Uid="GeneralPage_EnableDataDiagnostics"
HeaderIcon="{ui:FontIcon Glyph=&#xE9D9;}"
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<InfoBar
x:Uid="GPO_SettingIsManaged"
IsClosable="False"
IsOpen="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
IsTabStop="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
Severity="Informational">
<InfoBar.IconSource>
<FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE72E;" />
</InfoBar.IconSource>
</InfoBar>
<tkcontrols:SettingsExpander
x:Uid="GeneralPage_ViewDiagnosticData"
HeaderIcon="{ui:FontIcon Glyph=&#xE7EF;}"
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard x:Uid="GeneralPage_EnableViewDiagnosticData" IsEnabled="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableViewDataDiagnostics, Mode=TwoWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ViewDiagnosticDataViewer"
ActionIcon="{ui:FontIcon Glyph=&#xE8A7;}"
Click="ViewDiagnosticData_Click"
IsClickEnabled="True" />
</tkcontrols:SettingsExpander.Items>
</tkcontrols:SettingsExpander>
<InfoBar
x:Uid="GeneralPage_ViewDiagnosticDataViewerInfo"
IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
IsTabStop="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
Severity="Informational">
<InfoBar.ActionButton>
<Button x:Uid="GeneralPage_ViewDiagnosticDataViewerInfoButton" Click="Click_ViewDiagnosticDataViewerRestart" />
</InfoBar.ActionButton>
</InfoBar>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ReportBugPackage"
HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}">
<controls:SettingsGroup x:Uid="General_DiagnosticsAndFeedback">
<tkcontrols:SettingsExpander
x:Uid="GeneralPage_EnableDataDiagnostics"
HeaderIcon="{ui:FontIcon Glyph=&#xE9D9;}"
IsEnabled="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
IsExpanded="True">
<tkcontrols:SettingsExpander.Description>
<StackPanel Orientation="Vertical">
<TextBlock
x:Uid="GeneralPage_EnableDataDiagnosticsText"
Style="{StaticResource SecondaryTextStyle}"
TextWrapping="WrapWholeWords" />
<HyperlinkButton
x:Uid="GeneralPage_DiagnosticsAndFeedback_Link"
Margin="0,2,0,0"
FontWeight="SemiBold"
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
</StackPanel>
</tkcontrols:SettingsExpander.Description>
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}" />
<tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsCard x:Uid="GeneralPage_EnableViewDiagnosticData" IsEnabled="{x:Bind ViewModel.EnableDataDiagnostics, Mode=TwoWay}">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.EnableViewDataDiagnostics, Mode=TwoWay}" />
<tkcontrols:SettingsCard.Description>
<StackPanel Orientation="Vertical">
<TextBlock
x:Uid="GeneralPage_EnableViewDiagnosticDataText"
Style="{StaticResource SecondaryTextStyle}"
TextWrapping="WrapWholeWords" />
<HyperlinkButton
Margin="0,2,0,0"
Click="ViewDiagnosticData_Click"
Content="View diagnostic data"
FontWeight="SemiBold" />
</StackPanel>
</tkcontrols:SettingsCard.Description>
</tkcontrols:SettingsCard>
</tkcontrols:SettingsExpander.Items>
<tkcontrols:SettingsExpander.ItemsFooter>
<InfoBar
x:Uid="GeneralPage_ViewDiagnosticDataViewerInfo"
BorderThickness="0"
CornerRadius="0,0,4,4"
IsClosable="False"
IsOpen="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
IsTabStop="{x:Bind Mode=OneWay, Path=ViewModel.ViewDiagnosticDataViewerChanged}"
Severity="Informational">
<InfoBar.ActionButton>
<Button x:Uid="GeneralPage_ViewDiagnosticDataViewerInfoButton" Click="Click_ViewDiagnosticDataViewerRestart" />
</InfoBar.ActionButton>
</InfoBar>
</tkcontrols:SettingsExpander.ItemsFooter>
</tkcontrols:SettingsExpander>
<InfoBar
x:Uid="GPO_SettingIsManaged"
IsClosable="False"
IsOpen="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
IsTabStop="{x:Bind ViewModel.IsDataDiagnosticsGPOManaged, Mode=OneWay}"
Severity="Informational">
<InfoBar.IconSource>
<FontIconSource FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE72E;" />
</InfoBar.IconSource>
</InfoBar>
<tkcontrols:SettingsCard x:Uid="GeneralPage_ReportBugPackage" HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}">
<StackPanel Orientation="Horizontal">
<Button
x:Uid="GeneralPageReportBugPackage"
HorizontalAlignment="Right"
Click="BugReportToolClicked"
IsEnabled="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard
x:Uid="GeneralPage_ReportBugPackage"
HeaderIcon="{ui:FontIcon Glyph=&#xEBE8;}"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
Visibility="{x:Bind ViewModel.IsBugReportRunning, Converter={StaticResource ReverseBoolToVisibilityConverter}, Mode=OneWay}" />
<ProgressRing
Width="24"
Height="24"
HorizontalAlignment="Right"
VerticalAlignment="Center" />
</tkcontrols:SettingsCard>
</StackPanel>
VerticalAlignment="Center"
Visibility="{x:Bind ViewModel.IsBugReportRunning, Mode=OneWay}" />
</StackPanel>
</tkcontrols:SettingsCard>
</controls:SettingsGroup>
</StackPanel>

View File

@@ -152,10 +152,10 @@
<comment>"Screen Ruler" is the name of the utility</comment>
</data>
<data name="MeasureTool_DefaultMeasureStyle.Header" xml:space="preserve">
<value>Default measure style</value>
<value>Default mode</value>
</data>
<data name="MeasureTool_DefaultMeasureStyle.Description" xml:space="preserve">
<value>The utility will start having the selected style activated</value>
<value>The measuring mode that is used when activated</value>
</data>
<data name="MeasureTool_DefaultMeasureStyle_None.Content" xml:space="preserve">
<value>None</value>
@@ -234,7 +234,7 @@
<value>Security key</value>
</data>
<data name="MouseWithoutBorders_SecurityKey.Description" xml:space="preserve">
<value>The key must be auto generated in one machine by clicking on New Key, then typed in other machines</value>
<value>To set up, generate the key on one machine, and enter it manually on other machines.</value>
</data>
<data name="MouseWithoutBorders_NewKey.Content" xml:space="preserve">
<value>New key</value>
@@ -274,7 +274,7 @@
<value>Adding a firewall rule might help solve connection issues.</value>
</data>
<data name="MouseWithoutBorders_RunAsAdminText.Title" xml:space="preserve">
<value>You need to run as administrator to modify this setting.</value>
<value>This setting can only be changed when running as administrator</value>
</data>
<data name="MouseWithoutBorders_ServiceUserUninstallWarning.Title" xml:space="preserve">
<value>If PowerToys is installed as a user, uninstalling/upgrading may require the Mouse Without Borders service to be removed manually later.</value>
@@ -361,7 +361,7 @@
<value>Hide the mouse cursor at the top edge of the screen when switching to other machine. This option also steals the focus from any full-screen app to ensure the keyboard input is redirected.</value>
</data>
<data name="MouseWithoutBorders_DrawMouseCursor.Description" xml:space="preserve">
<value>Mouse cursor may not be visible in Windows 10 and later versions of Windows when there is no physical mouse attached.</value>
<value>The mouse cursor may not appear on Windows 10 and later if no physical mouse is connected.</value>
</data>
<data name="MouseWithoutBorders_ValidateRemoteMachineIP.Description" xml:space="preserve">
<value>Reverse DNS lookup to validate machine IP Address.</value>
@@ -804,7 +804,7 @@
<comment>launches the FancyZones layout editor application</comment>
</data>
<data name="FancyZones_MakeDraggedWindowTransparentCheckBoxControl.Content" xml:space="preserve">
<value>Make dragged window transparent</value>
<value>Make the dragged window transparent</value>
</data>
<data name="FancyZones_MouseDragCheckBoxControl_Header.Content" xml:space="preserve">
<value>Use a non-primary mouse button to toggle zone activation</value>
@@ -1304,7 +1304,7 @@
<value>Always run as administrator</value>
</data>
<data name="GeneralSettings_AlwaysRunAsAdminText.Description" xml:space="preserve">
<value>You need to run as administrator to use this setting</value>
<value>This setting can only be changed when running as administrator</value>
</data>
<data name="GeneralSettings_RunningAsUserText" xml:space="preserve">
<value>Running as user</value>
@@ -1465,7 +1465,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<comment>Preview Pane and File Explorer are app/feature names in Windows. 'Alt + P' is a shortcut</comment>
</data>
<data name="FileExplorerPreview_RunAsAdminRequired.Title" xml:space="preserve">
<value>You need to run as administrator to modify these settings.</value>
<value>This setting can only be changed when running as administrator</value>
</data>
<data name="FileExplorerPreview_RebootRequired.Title" xml:space="preserve">
<value>A reboot may be required for changes to these settings to take effect</value>
@@ -1520,7 +1520,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>Mouse actions</value>
</data>
<data name="ColorPicker_MouseActions.Description" xml:space="preserve">
<value>Choose what clicking individual buttons does</value>
<value>Customize the function of each mouse button</value>
</data>
<data name="ColorPicker_PrimaryClick.Header" xml:space="preserve">
<value>Primary click</value>
@@ -1544,7 +1544,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>Picker behavior</value>
</data>
<data name="ColorPicker_CopiedColorRepresentation.Description" xml:space="preserve">
<value>This format will be copied to your clipboard</value>
<value>Format will be copied to your clipboard</value>
</data>
<data name="KBM_KeysCannotBeRemapped.Text" xml:space="preserve">
<value>Learn more about remapping limitations</value>
@@ -1736,7 +1736,7 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>Show color name</value>
</data>
<data name="ColorPicker_ShowColorName.Description" xml:space="preserve">
<value>This will show the name of the color when picking a color</value>
<value>Displays the color name while picking a color</value>
</data>
<data name="ImageResizer_DefaultSize_Large" xml:space="preserve">
<value>Large</value>
@@ -2056,7 +2056,7 @@ Take a moment to preview the various utilities listed or view our comprehensive
<value>Diagnostic data</value>
</data>
<data name="Oobe_Overview_EnableDataDiagnostics.Description" xml:space="preserve">
<value>Helps inform bug fixes, performance, and product decisions</value>
<value>Helps us make PowerToys faster, more stable, and better over time</value>
</data>
<data name="Oobe_WhatsNew_DataDiagnostics_InfoBar.Header" xml:space="preserve">
<value>Turn on diagnostic data to help us improve PowerToys?</value>
@@ -2710,7 +2710,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Spotlight initial zoom</value>
</data>
<data name="MouseUtils_FindMyMouse_SpotlightInitialZoom.Description" xml:space="preserve">
<value>Spotlight zoom factor at animation start</value>
<value>Spotlight zoom factor at the start of the animation</value>
</data>
<data name="MouseUtils_FindMyMouse_AnimationDurationMs.Header" xml:space="preserve">
<value>Animation duration (ms)</value>
@@ -2724,23 +2724,23 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Animations are disabled in your system settings.</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingMinimumDistance.Header" xml:space="preserve">
<value>Shake minimum distance</value>
<value>Minimum shake distance</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingMinimumDistance.Description" xml:space="preserve">
<value>The minimum distance for mouse shaking activation, for adjusting sensitivity</value>
<value>The minimum distance the mouse must move to be considered a shake. Lower values increase sensitivity.</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingIntervalMs.Header" xml:space="preserve">
<value>Shake Interval (ms)</value>
<value>Shake detection interval (ms)</value>
<comment>ms = milliseconds</comment>
</data>
<data name="MouseUtils_FindMyMouse_ShakingIntervalMs.Description" xml:space="preserve">
<value>The span of time during which we track mouse movement to detect shaking, for adjusting sensitivity</value>
<value>Time window used to monitor mouse movement for shake detection. Shorter intervals may detect quicker shakes.</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingFactor.Header" xml:space="preserve">
<value>Shake factor (percent)</value>
<value>Shake sensitivity factor (percent)</value>
</data>
<data name="MouseUtils_FindMyMouse_ShakingFactor.Description" xml:space="preserve">
<value>Mouse shaking is detected by checking how much the mouse pointer has travelled when compared to the diagonal of the movement area. Reducing this factor increases sensitivity.</value>
<value>Determines how far the pointer must move, relative to the screen diagonal, to count as a shake. Lower values make it more sensitive.</value>
</data>
<data name="MouseUtils_MouseHighlighter.Header" xml:space="preserve">
<value>Mouse Highlighter</value>
@@ -2840,7 +2840,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Windows default</value>
</data>
<data name="ColorModeHeader.Header" xml:space="preserve">
<value>App theme</value>
<value>Theme</value>
</data>
<data name="FancyZones_Zone_Appearance.Description" xml:space="preserve">
<value>Customize the way zones look</value>
@@ -2848,9 +2848,6 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<data name="FancyZones_Zone_Appearance.Header" xml:space="preserve">
<value>Zone appearance</value>
</data>
<data name="LearnMore.Content" xml:space="preserve">
<value>Learn more</value>
</data>
<data name="FileExplorerPreview_ToggleSwitch_Thumbnail_GCODE.Header" xml:space="preserve">
<value>Geometric Code</value>
<comment>File type, do not translate</comment>
@@ -2906,13 +2903,13 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Reparent shortcut</value>
</data>
<data name="CropAndLock_ReparentActivation_Shortcut.Description" xml:space="preserve">
<value>Shortcut to crop an application's window into a cropped window. This is experimental and can cause issues with some applications, since the cropped window will contain the original application window.</value>
<value>Creates a cropped version of an applications window by embedding the original app into a new window. This is experimental and may cause issues due to the reparenting behavior.</value>
</data>
<data name="CropAndLock_ThumbnailActivation_Shortcut.Header" xml:space="preserve">
<value>Thumbnail shortcut</value>
</data>
<data name="CropAndLock_ThumbnailActivation_Shortcut.Description" xml:space="preserve">
<value>Shortcut to crop and create a thumbnail of another window. The application isn't controllable through the thumbnail but it'll have less compatibility issues. </value>
<value>Creates a cropped, non-interactive thumbnail of another window. Improves app compatibility.</value>
</data>
<data name="CropAndLock.SecondaryLinksHeader" xml:space="preserve">
<value>Attribution</value>
@@ -2980,12 +2977,6 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Do not activate when Game Mode is on</value>
<comment>Game Mode is a Windows feature</comment>
</data>
<data name="AlwaysOnTop_SoundTitle.Header" xml:space="preserve">
<value>Sound</value>
</data>
<data name="AlwaysOnTop_Sound.Content" xml:space="preserve">
<value>Play a sound when pinning a window</value>
</data>
<data name="AlwaysOnTop_Behavior.Header" xml:space="preserve">
<value>Behavior</value>
</data>
@@ -3061,10 +3052,10 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Behavior</value>
</data>
<data name="Peek_AlwaysRunNotElevated.Header" xml:space="preserve">
<value>Always run not elevated, even when PowerToys is elevated</value>
<value>Always run without elevation (even if PowerToys is elevated)</value>
</data>
<data name="Peek_AlwaysRunNotElevated.Description" xml:space="preserve">
<value>Tries to run Peek without elevated permissions, to fix access to network shares. You need to disable and re-enable Peek for changes to this value to take effect.</value>
<value>Runs Peek without admin permissions to improve access to network shares. To apply this change, you must disable and re-enable Peek.</value>
<comment>Peek is a product name, do not loc</comment>
</data>
<data name="Peek_CloseAfterLosingFocus.Header" xml:space="preserve">
@@ -3072,13 +3063,13 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<comment>Peek is a product name, do not loc</comment>
</data>
<data name="Peek_ConfirmFileDelete.Header" xml:space="preserve">
<value>Ask for confirmation before deleting files</value>
<value>Confirm before deleting files</value>
</data>
<data name="Peek_ConfirmFileDelete.Description" xml:space="preserve">
<value>When enabled, you will be prompted to confirm before moving files to the Recycle Bin.</value>
<value>You'll be asked to confirm before files are moved to the Recycle Bin</value>
</data>
<data name="FancyZones_DisableRoundCornersOnWindowSnap.Content" xml:space="preserve">
<value>Disable round corners when window is snapped</value>
<value>Disable rounded corners when a window is snapped</value>
</data>
<data name="PowerLauncher_SearchQueryTuningEnabled.Description" xml:space="preserve">
<value>Fine tune results ordering</value>
@@ -3111,7 +3102,7 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
<value>Global sort order score modifier</value>
</data>
<data name="AlwaysOnTop_RoundCorners.Content" xml:space="preserve">
<value>Enable round corners</value>
<value>Enable rounded corners</value>
</data>
<data name="LearnMore_QuickAccent.Text" xml:space="preserve">
<value>Learn more about Quick Accent</value>
@@ -3164,9 +3155,6 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="AlwaysOnTop_ShortDescription" xml:space="preserve">
<value>Pin a window</value>
</data>
<data name="Awake_ShortDescription" xml:space="preserve">
<value>Keep your PC awake</value>
</data>
<data name="ColorPicker_ShortDescription" xml:space="preserve">
<value>Pick a color</value>
</data>
@@ -3185,9 +3173,6 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="FindMyMouse_ShortDescription" xml:space="preserve">
<value>Find the mouse</value>
</data>
<data name="ImageResizer_ShortDescription" xml:space="preserve">
<value>Resize images from right-click context menu</value>
</data>
<data name="MouseHighlighter_ShortDescription" xml:space="preserve">
<value>Highlight clicks</value>
</data>
@@ -3197,9 +3182,6 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="MouseCrosshairs_ShortDescription" xml:space="preserve">
<value>Draw crosshairs centered on the mouse pointer</value>
</data>
<data name="MouseWithoutBorders_ShortDescription" xml:space="preserve">
<value>Move your cursor across multiple devices</value>
</data>
<data name="AdvancedPaste_ShortDescription" xml:space="preserve">
<value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value>
</data>
@@ -3212,9 +3194,6 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Peek_ShortDescription" xml:space="preserve">
<value>Quick and easy previewer</value>
</data>
<data name="PowerRename_ShortDescription" xml:space="preserve">
<value>Rename files and folders from right-click context menu</value>
</data>
<data name="Run_ShortDescription" xml:space="preserve">
<value>A quick launcher</value>
</data>
@@ -3315,7 +3294,7 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>ms = milliseconds</comment>
</data>
<data name="QuickAccent_InputTimeMs.Description" xml:space="preserve">
<value>Hold the key down for this much time to make the accent menu appear (ms)</value>
<value>How long a key must be held before the accent menu appears</value>
<comment>ms = milliseconds</comment>
</data>
<data name="QuickAccent_ExcludedApps.Description" xml:space="preserve">
@@ -3646,7 +3625,7 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>"Hosts File Editor" is a product name</comment>
</data>
<data name="Hosts_AdditionalLinesPosition.Header" xml:space="preserve">
<value>Position of additional content</value>
<value>Placement of additional content</value>
</data>
<data name="Hosts_AdditionalLinesPosition_Bottom.Content" xml:space="preserve">
<value>Bottom</value>
@@ -3674,7 +3653,7 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>"Hosts File Editor" is the name of the utility</comment>
</data>
<data name="Hosts_Toggle_LaunchAdministrator.Description" xml:space="preserve">
<value>Needs to be launched as administrator in order to make changes to the hosts file</value>
<value>Required in order to make changes to the hosts file</value>
</data>
<data name="Hosts_Toggle_LaunchAdministrator.Header" xml:space="preserve">
<value>Launch as administrator</value>
@@ -3716,7 +3695,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Environment Variables</value>
</data>
<data name="EnvironmentVariables_Toggle_LaunchAdministrator.Description" xml:space="preserve">
<value>Needs to be launched as administrator in order to make changes to the system environment variables</value>
<value>Required in order to make changes to the system environment variables</value>
</data>
<data name="EnvironmentVariables_Toggle_LaunchAdministrator.Header" xml:space="preserve">
<value>Launch as administrator</value>
@@ -3757,7 +3736,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>This setting is managed by your organization.</value>
</data>
<data name="Hosts_AdditionalLinesPosition.Description" xml:space="preserve">
<value>Additional content includes the file header and lines that can't parse</value>
<value>Includes items like the file header and any lines that cant be parsed</value>
</data>
<data name="TextExtractor_Languages.Header" xml:space="preserve">
<value>Preferred language</value>
@@ -3794,7 +3773,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Experimentation</value>
</data>
<data name="GeneralPage_EnableExperimentation.Description" xml:space="preserve">
<value>Note: Only Windows Insider builds may be selected for experimentation</value>
<value>Only Windows Insider builds may be selected for experimentation</value>
<comment>{Locked="Windows Insider"}</comment>
</data>
<data name="GeneralPage_EnableExperimentation.Header" xml:space="preserve">
@@ -3804,28 +3783,13 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Diagnostics &amp; feedback</value>
</data>
<data name="GeneralPage_DiagnosticsAndFeedback_Link.Content" xml:space="preserve">
<value>Learn more about the information we log &amp; how it gets used</value>
<value>Learn more about the logged information and how it's used</value>
</data>
<data name="GeneralPage_EnableDataDiagnostics.Header" xml:space="preserve">
<value>Diagnostic data</value>
</data>
<data name="GeneralPage_EnableDataDiagnostics.Description" xml:space="preserve">
<value>Helps inform bug fixes, performance, and improvements</value>
</data>
<data name="GeneralPage_ViewDiagnosticData.Header" xml:space="preserve">
<value>View diagnostic data</value>
</data>
<data name="GeneralPage_EnableViewDiagnosticData.Header" xml:space="preserve">
<value>Enable viewing</value>
</data>
<data name="GeneralPage_EnableViewDiagnosticData.Description" xml:space="preserve">
<value>Uses up to 1GB (or more) of hard drive space on your PC</value>
</data>
<data name="GeneralPage_ViewDiagnosticDataViewer.Header" xml:space="preserve">
<value>Diagnostic data viewer</value>
</data>
<data name="GeneralPage_ViewDiagnosticDataViewer.Description" xml:space="preserve">
<value>Generate .xml files containing readable diagnostic data. Folder may include .xml and .etl files</value>
<value>Save logs to this device</value>
</data>
<data name="Shell_AdvancedPaste.Content" xml:space="preserve">
<value>Advanced Paste</value>
@@ -3988,9 +3952,6 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="QuickAccessTxt.Text" xml:space="preserve">
<value>Quick access</value>
</data>
<data name="UpdateAvailable.Title" xml:space="preserve">
<value>Update available</value>
</data>
<data name="FileExplorerPreview_Toggle_Monaco_Max_File_Size.Header" xml:space="preserve">
<value>Maximum file size to preview</value>
<comment>Size refers to the disk space used by a file</comment>
@@ -4055,7 +4016,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Activation shortcut</value>
</data>
<data name="MouseUtils_MouseJump_ThumbnailSize.Header" xml:space="preserve">
<value>Thumbnail Size</value>
<value>Thumbnail size</value>
</data>
<data name="MouseUtils_MouseJump_ThumbnailSize_Description_Prefix.Text" xml:space="preserve">
<value>Constrain thumbnail image size to a maximum of</value>
@@ -4190,11 +4151,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Launch Registry Preview</value>
</data>
<data name="RegistryPreview_DefaultRegApp.Header" xml:space="preserve">
<value>Default app</value>
</data>
<data name="RegistryPreview_DefaultRegApp.Description" xml:space="preserve">
<value>Make Registry Preview default app for opening .reg files</value>
<comment>Registry Preview is app name. Do not localize.</comment>
<value>Make Registry Preview the default app for .reg files</value>
</data>
<data name="AdvancedPaste_ShortcutWarning.Title" xml:space="preserve">
<value>Using this shortcut may prevent non-text paste actions (e.g. images, files) or built-in paste plain text actions in other applications from functioning.</value>
@@ -4272,12 +4229,6 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="Shell_Dashboard.Content" xml:space="preserve">
<value>Dashboard</value>
</data>
<data name="DisabledModules.Text" xml:space="preserve">
<value>Disabled modules</value>
</data>
<data name="EnabledModules.Text" xml:space="preserve">
<value>Enabled modules</value>
</data>
<data name="Peek_Preview_GroupSettings.Header" xml:space="preserve">
<value>Preview</value>
</data>
@@ -4321,7 +4272,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Show the release notes after an update</value>
</data>
<data name="ShowSystemTrayIcon.Description" xml:space="preserve">
<value>This settings page is accessible by running the PowerToys executable again</value>
<value>To access settings, run the PowerToys executable again</value>
</data>
<data name="ShowSystemTrayIcon.Header" xml:space="preserve">
<value>Show system tray icon</value>
@@ -4421,10 +4372,6 @@ Activate by holding the key for the character you want to add an accent to, then
<value>New+</value>
<comment>New+ is the name of the utility. Localize product name in accordance with Windows New</comment>
</data>
<data name="NewPlus_Product_Description.Description" xml:space="preserve">
<value>Create files and folders from a personalized set of templates</value>
<comment>New+ product description</comment>
</data>
<data name="NewPlus_Learn_More.Text" xml:space="preserve">
<value>Learn more about New+</value>
<comment>New+ learn more link. Localize product name in accordance with Windows New</comment>
@@ -4461,15 +4408,15 @@ Activate by holding the key for the character you want to add an accent to, then
<comment>Display options label</comment>
</data>
<data name="NewPlus_Hide_File_Extension_Toggle.Header" xml:space="preserve">
<value>Hide template filename extension</value>
<value>Hide the file extension in template names</value>
<comment>Template file name extension settings toggle</comment>
</data>
<data name="NewPlus_Hide_Starting_Digits_Toggle.Header" xml:space="preserve">
<value>Hide template filename starting digits, spaces, and dots</value>
<value>Hide leading digits, spaces, and dots in template filenames</value>
<comment>Template filename starting digits settings toggle</comment>
</data>
<data name="NewPlus_Hide_Starting_Digits_Description.Text" xml:space="preserve">
<value>This option is useful when using digits, spaces and dots at the beginning of filenames to control the display order of templates</value>
<value>Ignores digits, spaces, and dots at the start of filenames—useful for sorting templates without showing those characters</value>
<comment>Template filename starting digits settings toggle</comment>
</data>
<data name="NewPlus_behavior.Header" xml:space="preserve">
@@ -4856,13 +4803,13 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<value>Open settings</value>
</data>
<data name="LanguageHeader.Header" xml:space="preserve">
<value>Application language</value>
<value>Language</value>
</data>
<data name="LanguageHeader.Description" xml:space="preserve">
<value>PowerToys will use OS language by default.</value>
<value>PowerToys matches your Windows language by default</value>
</data>
<data name="LanguageRestartInfo.Title" xml:space="preserve">
<value>Restart PowerToys for language change to take effect.</value>
<value>Restart PowerToys to apply language changes</value>
</data>
<data name="LanguageRestartInfoButton.Content" xml:space="preserve">
<value>Restart</value>
@@ -5057,7 +5004,7 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<value>Generate bug report package</value>
</data>
<data name="GeneralPage_ReportBugPackage.Description" xml:space="preserve">
<value>Create zip folder with logs on the Desktop</value>
<value>Log files will be zipped to your desktop</value>
</data>
<data name="GeneralPageReportBugPackage.Content" xml:space="preserve">
<value>Generate package</value>
@@ -5080,4 +5027,41 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="HighlightMode_Spotlight_Mode.Content" xml:space="preserve">
<value>Spotlight</value>
</data>
<data name="LearnMore.Content" xml:space="preserve">
<value>Learn more</value>
</data>
<data name="Awake_ShortDescription" xml:space="preserve">
<value>Keep your PC awake</value>
</data>
<data name="ImageResizer_ShortDescription" xml:space="preserve">
<value>Resize images from right-click context menu</value>
</data>
<data name="MouseWithoutBorders_ShortDescription" xml:space="preserve">
<value>Move your cursor across multiple devices</value>
</data>
<data name="PowerRename_ShortDescription" xml:space="preserve">
<value>Rename files and folders from right-click context menu</value>
</data>
<data name="GeneralPage_EnableDataDiagnosticsText.Text" xml:space="preserve">
<value>Helps us make PowerToys faster, more stable, and better over time</value>
</data>
<data name="UpdateAvailable.Title" xml:space="preserve">
<value>Update available</value>
</data>
<data name="DisabledModules.Text" xml:space="preserve">
<value>Disabled modules</value>
</data>
<data name="EnabledModules.Text" xml:space="preserve">
<value>Enabled modules</value>
</data>
<data name="NewPlus_Product_Description.Description" xml:space="preserve">
<value>Create files and folders from a personalized set of templates</value>
<comment>New+ product description</comment>
</data>
<data name="AlwaysOnTop_Sound.Header" xml:space="preserve">
<value>Play a sound when pinning a window</value>
</data>
<data name="GeneralPage_EnableViewDiagnosticDataText.Text" xml:space="preserve">
<value>Stores diagnostic data locally in .xml format; folder may include .etl files as well. May use up 1GB or more of disk space.</value>
</data>
</root>