Compare commits

...

22 Commits

Author SHA1 Message Date
Zhaopeng Wang (from Dev Box)
8e60559036 merge main branch 2025-02-13 19:37:20 +08:00
Zhaopeng Wang (from Dev Box)
4cd4fc8490 add Session Init in UITestBase.cs 2025-02-13 16:37:09 +08:00
Zhaopeng Wang (from Dev Box)
7913941c04 change UITeseBase code 2025-02-13 16:33:08 +08:00
Zhaopeng Wang (from Dev Box)
7897b8b1a3 Modify the infrastructure so that less structure is exposed. Modify the method of the new module of Attach. 2025-02-12 22:53:40 +08:00
Jerry Xu
306d7ccddb Merge from origin/master 2025-02-12 15:13:14 +08:00
urnotdfs
adb6887563 Add Button, Window and By selector (#37407)
* Add Button, Window and By selector

* Add Button, Window and By selector

e

* Remove extra using

---------

Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
2025-02-12 14:46:49 +08:00
Zhaopeng Wang (from Dev Box)
48e6b7e91d fix keyboardmanager test error 2025-02-11 18:11:42 +08:00
Zhaopeng Wang (from Dev Box)
9e4105afe4 Refactor the UITest structure and modify the corresponding class, add the underlying function of the element, and fix the compilation error caused by the refactoring 2025-02-10 20:57:35 +08:00
Zhaopeng Wang (from Dev Box)
82d7b84490 add element object set 2025-02-08 12:52:11 +08:00
Zhaopeng Wang (from Dev Box)
d51305d7cb Refactor the API code structure 2025-02-07 17:00:45 +08:00
Zhaopeng Wang (from Dev Box)
7f214160f2 change launch module func 2025-02-05 16:45:25 +08:00
Zhaopeng Wang (from Dev Box)
09d4322b7c add PowerToys Module Config 2025-01-26 14:43:33 +08:00
Zhaopeng Wang (from Dev Box)
d43286661b change manager name and func name 2025-01-26 12:15:49 +08:00
urnotdfs
9fccd86223 Add Module Enable/Disable API and update window operation function in UIManager (#37047)
* 1. Enable/Disable API
2. Fix UIManager
3. Fix FancyZone UI test

* Fix spelling error

---------

Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
2025-01-24 17:24:11 +08:00
Zhaopeng Wang (from Dev Box)
300aa9fa9f fix mergeing 2025-01-24 04:23:15 +08:00
Zhaopeng Wang (from Dev Box)
e86226f336 add UIManager 2025-01-24 04:14:48 +08:00
urnotdfs
e801ca2bef Create uitest for keyboardmanager and api for element clicking by hel… (#37007)
Create uitest for keyboardmanager and api for element clicking by helpText

Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
2025-01-22 18:45:16 +08:00
Zhaopeng Wang (from Dev Box)
342d24ccc8 add powertoys setting UI name to fix WinAppDriver Luanch error 2025-01-22 15:41:52 +08:00
Zhaopeng Wang (from Dev Box)
d8feddc9da add UItestAPI Init default val 2025-01-22 14:15:27 +08:00
Zhaopeng Wang (from Dev Box)
7474a54cf2 fix error 2025-01-17 19:04:53 +08:00
Zhaopeng Wang (from Dev Box)
81b5706c09 delete other code 2025-01-17 18:58:35 +08:00
Zhaopeng Wang (from Dev Box)
9da205d2c0 add UI Test framework 2025-01-17 18:58:15 +08:00
50 changed files with 1297 additions and 386 deletions

View File

@@ -993,6 +993,7 @@ NNN
NOACTIVATE
NOAGGREGATION
NOASYNC
NOCHANGEDIR
NOCLIP
NOCLOSEPROCESS
NOCOALESCE

View File

@@ -88,6 +88,8 @@ extends:
akvName: $(SigningAKVName)
authCertName: $(SigningAuthCertName)
signCertName: $(SigningSignCertName)
useManagedIdentity: $(SigningUseManagedIdentity)
clientId: $(SigningOriginalClientId)
# Have msbuild use the release nuget config profile
additionalBuildOptions: /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config"
beforeBuildSteps:

View File

@@ -19,4 +19,6 @@ steps:
AuthAKVName: ${{ parameters.signingIdentity.akvName }}
AuthCertName: ${{ parameters.signingIdentity.authCertName }}
AuthSignCertName: ${{ parameters.signingIdentity.signCertName }}
UseMSIAuthentication: ${{ coalesce(parameters.signingIdentity.useManagedIdentity, 'false') }}
EsrpClientId: ${{ parameters.signingIdentity.clientId }}
${{ insert }}: ${{ parameters.inputs }}

View File

@@ -1,5 +1,7 @@
$LatestVCToolsVersion = (([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest $env:VCWhereExtraVersionTarget -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml)).instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.CRT.Source" }).version;
$VSInstances = ([xml](& 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -include packages -format xml))
$VSPackages = $VSInstances.instances.instance.packages.package
$LatestVCPackage = ($VSInstances.instances.instance.packages.package | ? { $_.id -eq "Microsoft.VisualCpp.Tools.Core" })
$LatestVCToolsVersion = $LatestVCPackage.version;
Write-Output "Latest VCToolsVersion: $LatestVCToolsVersion"
Write-Output "Updating VCToolsVersion environment variable for job"
Write-Output "##vso[task.setvariable variable=VCToolsVersion]$LatestVCToolsVersion"

View File

@@ -636,6 +636,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItModuleInterface", "sr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItSettingsInterop", "src\modules\ZoomIt\ZoomItSettingsInterop\ZoomItSettingsInterop.vcxproj", "{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestAPI", "src\UITestAPI\UITestAPI.csproj", "{6190BD46-AF95-49E6-8B7E-89B923CDE845}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyboardManagerUITests", "src\modules\keyboardmanager\KeyboardManagerUITests\KeyboardManagerUITests.csproj", "{A62BA56B-0A0B-4BBE-B570-F1CF80003274}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2834,6 +2838,30 @@ Global
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x64.Build.0 = Release|x64
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x86.ActiveCfg = Release|x64
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}.Release|x86.Build.0 = Release|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Debug|ARM64.ActiveCfg = Debug|ARM64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Debug|ARM64.Build.0 = Debug|ARM64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Debug|x64.ActiveCfg = Debug|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Debug|x64.Build.0 = Debug|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Debug|x86.ActiveCfg = Debug|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Debug|x86.Build.0 = Debug|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Release|ARM64.ActiveCfg = Release|ARM64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Release|ARM64.Build.0 = Release|ARM64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Release|x64.ActiveCfg = Release|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Release|x64.Build.0 = Release|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Release|x86.ActiveCfg = Release|x64
{6190BD46-AF95-49E6-8B7E-89B923CDE845}.Release|x86.Build.0 = Release|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Debug|ARM64.Build.0 = Debug|ARM64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Debug|x64.ActiveCfg = Debug|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Debug|x64.Build.0 = Debug|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Debug|x86.ActiveCfg = Debug|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Debug|x86.Build.0 = Debug|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Release|ARM64.ActiveCfg = Release|ARM64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Release|ARM64.Build.0 = Release|ARM64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Release|x64.ActiveCfg = Release|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Release|x64.Build.0 = Release|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Release|x86.ActiveCfg = Release|x64
{A62BA56B-0A0B-4BBE-B570-F1CF80003274}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3069,6 +3097,8 @@ Global
{0A84F764-3A88-44CD-AA96-41BDBD48627B} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{E4585179-2AC1-4D5F-A3FF-CFC5392F694C} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{CA7D8106-30B9-4AEC-9D05-B69B31B8C461} = {DD6E12FE-5509-4ABC-ACC2-3D6DC98A238C}
{6190BD46-AF95-49E6-8B7E-89B923CDE845} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{A62BA56B-0A0B-4BBE-B570-F1CF80003274} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

174
README.md
View File

@@ -18,7 +18,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| [New+](https://aka.ms/PowerToysOverview_NewPlus) | [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) |
| [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) |
| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | [ZoomIt](https://aka.ms/PowerToysOverview_PowerToysOverview_ZoomIt) |
| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | [ZoomIt](https://aka.ms/PowerToysOverview_ZoomIt) |
## Installing and running Microsoft PowerToys
@@ -34,19 +34,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.88%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.87%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.1/PowerToysUserSetup-0.87.1-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.1/PowerToysUserSetup-0.87.1-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.1/PowerToysSetup-0.87.1-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.1/PowerToysSetup-0.87.1-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.89%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.88%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.88.0/PowerToysUserSetup-0.88.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.88.0/PowerToysUserSetup-0.88.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.88.0/PowerToysSetup-0.88.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.88.0/PowerToysSetup-0.88.0-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.87.1-x64.exe][ptUserX64] | 8EFAF47ED00BF230D2C2CC3CB6765C903A6A47E0AAED0BBB329CEF918207B486 |
| Per user - ARM64 | [PowerToysUserSetup-0.87.1-arm64.exe][ptUserArm64] | 212FC8055789BD2DC4DE554B9AEE291A9C077907E263A302939266263A9D512B |
| Machine wide - x64 | [PowerToysSetup-0.87.1-x64.exe][ptMachineX64] | 69AD65DDAC6436AEF292D2CC6AB1530021CE98083CB3F5FD3380A52A3B0DBB9A |
| Machine wide - ARM64 | [PowerToysSetup-0.87.1-arm64.exe][ptMachineArm64] | AEC9F1D02F1E23F0C1FCFDF95C337C962902394F44C0568012DF78BEDB45CF19 |
| Per user - x64 | [PowerToysUserSetup-0.88.0-x64.exe][ptUserX64] | 5BBA2E06603CAAE0269DFBC991095C6664FD934130335197C1BA3120E19B7CA3 |
| Per user - ARM64 | [PowerToysUserSetup-0.88.0-arm64.exe][ptUserArm64] | E79723F9F94068C699E01334C8CC0C85F37818EB4664FC772D2B545A1C37C3FA |
| Machine wide - x64 | [PowerToysSetup-0.88.0-x64.exe][ptMachineX64] | C43742DB7AA3F8B01FE7AE1DA591F0342767AFE5BBACB72F2968CE5E8EE1E3AC |
| Machine wide - ARM64 | [PowerToysSetup-0.88.0-arm64.exe][ptMachineArm64] | AEE4A67643C886336F31F86C4117BA5F01BCA5E0E99FF34524217DC91AFA7132 |
This is our preferred method.
@@ -92,119 +92,141 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
### 0.87 - December 2024 Update
### 0.88 - January 2025 Update
In this release, we focused on new features, stability, and improvements.
**Highlights**
- Advanced Paste has a new feature called "Advanced AI" that uses Semantic Kernel to allow setting up the orchestration of sequential clipboard transformations.
- Workspaces supports Progressive Web Applications.
- Workspaces has a new feature to move existing windows instead of creating new ones.
- Mouse Jump added new settings to allow customization of screens pop-up. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- New+ now works on Windows 10. Thanks [@cgaarden](https://github.com/cgaarden)!
- Quick Accent allows selecting the character sets that should appear on the UI. Thanks [@Sirozha1337](https://github.com/Sirozha1337)!
- New utility: ZoomIt - a screen zoom, annotation, and recording tool for technical presentations and demos. This utility from Sysinternals has had its source code released and included in PowerToys. ZoomIt will still continue to be updated and shipped by Sysinternals for users who prefer to have it as a standalone utility outside of PowerToys. Thanks [@markrussinovich](https://github.com/markrussinovich), [@foxmsft](https://github.com/foxmsft) and [@johnstep](https://github.com/johnstep) for contributing the original code and reviewing the PowerToys integration!
- Video Conference Mute has been deprecated and was removed from PowerToys.
- .Net 9.0.1 fixed many issue in WPF, improving stability for PowerToys Run.
### General
- Applied a workaround for the Windows App SDK applications title bar override that was causing accent color to not be shown on the top bar of applications on Windows 10. Thanks [@pingzing](https://github.com/pingzing)!
- Improved the "admin application running" notification checking logic to be less demanding on resources. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an issue causing many utilities to crash when the GPO to disable data diagnostics was applied.
### Advanced Paste
- Added a new optional feature allowing using AI to set up the orchestration of sequential clipboard transformations.
- Fixed a crash when the application was exiting. (This was a hotfix for 0.87)
- Added a Json format validation step to verify if a conversion to Json should be applied.
- Fixed accessibility issues when using a screen reader.
- Added support for all BitmapDecoder supported image file types to the Image to Text functionality. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed an issue causing Advanced Paste initialization errors to hang the PowerToys main process.
### Awake
### FancyZones
- Initialization, logging and tray icon setup improvements. Thanks [@dend](https://github.com/dend)!
- Removed Workspaces Editor from the exclusions list so it can be snapped by FancyZones.
### File Explorer add-ons
### Keyboard Manager
- Preview Pane extensions now use the PerMonitorV2 DPI mode to fix errors on different scales. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Keyboard Manager.
- Added labels to the IME On, IME Off keys. Thanks [@kit494way](https://github.com/kit494way)!
- Fixed an issue that caused the Shift key to remain stuck if a numpad key was mapped to the Shift key.
- Added an option to make a shortcut remapping only trigger with exact modifiers.
### Monaco Preview
- Added support for .ahk files to be shown as a plaintext file in Peek and File Explorer add-ons. Thanks [@daverayment](https://github.com/daverayment)!
- Added support for .ion files to be shown as a plaintext file in Peek and File Explorer add-ons. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
- Added support for syntax highlighting for .srt files in Peek and File Explorer add-ons. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added support for .resx and .resw files in Peek and File Explorer add-ons. Thanks [@asif4318](https://github.com/asif4318)!
- Added a setting to make the code minimap toggle-able in Peek and File Explorer add-ons. Thanks [@PesBandi](https://github.com/PesBandi)!
- Fixed an issue causing Json format preview setting to not be applied correctly.
- Fixed an issue causing the wrong Monaco assets to be used at runtime.
### Mouse Jump
### Mouse Without Borders
- Allow customizing the appearance of the UI of the Mouse Jump pop-up. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- Fixed an issue causing clipboard to stop working after going through a UAC screen when using the Service mode. Thanks [@YDKK](https://github.com/YDKK)!
### New+
- Added support for Windows 10. Thanks [@cgaarden](https://github.com/cgaarden)!
- Fixed an issue causing the renaming of new files to not trigger some times. Thanks [@cgaarden](https://github.com/cgaarden)!
- Updated the New+ icons. Thanks [@niels9001](https://github.com/niels9001)!
- Fixed an issue causing New+ to override the New file or folder creation from the File Explorer Ribbon buttons or keyboard shortcuts on Windows 10.
- When creating file or folders through a template, they should now have the current time as the last modified date. Thanks [@cgaarden](https://github.com/cgaarden)!
### Peek
- Peek now checks local capabilities to decide what image formats Image Previewer is able to support. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed an issue causing the Code Files Previewer to not load correctly under certain conditions. Thanks [@daverayment](https://github.com/daverayment)!
- Refactored, improved and fixed logging when loading the user settings file. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed an issue causing Peek to not appear if it was previously minimized. Thanks [@asif4318](https://github.com/asif4318)!
### PowerToys Run
- Added a scoring function for proper ordering of the WindowWalker plugin results. Thanks [@andbartol](https://github.com/andbartol)!
- Added UUIDv7 support to the ValueGenerator plugin. Thanks [@frederik-hoeft](https://github.com/frederik-hoeft)!
- The calculator plugin now allows scientific notation numbers with a lowercase 'e'. Thanks [@PesBandi](https://github.com/PesBandi)!
- Ported the UI from WPF-UI to .NET 9 WPF, to fix "Desktop composition is disabled" crashes.
- Fixed a transparent border issue on Windows 10. (This was a hotfix for 0.87)
- Fixed a crash in the OneNote plugin after the .Net 9 update. (This was a hotfix for 0.87)
- Fixed an issue causing the Calculator plugin to return division by zero errors when dividing by hexadecimal numbers. Thanks [@plante-msft](https://github.com/plante-msft)!
- Updated the Calculator plugin Mages library to 3.0.0 and added support for the random integer function. Thanks [@htcfreek](https://github.com/htcfreek)!
- Improved handling of non-base 10 numbers to add support for binary and octal numbers in the Calculator plugin. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added a setting to enable selection of which units to use for trigonometric functions. Thanks [@OldUser101](https://github.com/OldUser101)!
- Fixed a .NET 9 regression causing the PowerToys Run dialog to not be draggable. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Added context menu buttons for the VS Code Workspaces plugin, for copying the path, opening in File Explorer or in Console. Thanks [@programming-with-ia](https://github.com/programming-with-ia)!
- Added some telemetry to gather data on which hotkey is used to trigger PowerToys Run.
- Removed the workarounds that were in place to fix some WPF issues that were fixed in .NET 9.0.1.
- Fixed a typo in the Value Generator plugin messages. Thanks [@OldUser101](https://github.com/OldUser101)!
### Quick Accent
- Added a setting to allow selecting which character sets to show. Thanks [@Sirozha1337](https://github.com/Sirozha1337)!
- Added the ć character to the Slovenian character set. Thanks [@dsoklic](https://github.com/dsoklic)!
- Added the Proto-Indo-European character set.
### Screen Ruler
### Registry Preview
- Added a Setting to also allow showing measurements in inches, centimeters or millimeters. Thanks [@Sophanatprime](https://github.com/Sophanatprime)!
- Fixed an issue causing line breaks to not be parsed correctly for REG_MULTI_SZ values. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added a tooltip to values to show multiple lines of data. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added a context menu to enable copying type, value and key paths. Thanks [@htcfreek](https://github.com/htcfreek)!
### Settings
- Fixed an issue causing all the links to milestones in the "What's new?" OOBE page to point to the same milestone.
- Removed extra space from the Welcome page. Thanks [@agarwalishita](https://github.com/agarwalishita)!
- Updated left navigation bar icons. Thanks [@niels9001](https://github.com/niels9001)!
- Fixed accessibility issues in the dashboard page. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Made the Advanced Paste paste OpenAI configuration modal scrollable.
- Fixed the text on the Quick Accent page to refer to "character sets" instead of "character set". Thanks [@PesBandi](https://github.com/PesBandi)!
- Added the plugin's dll file version and website to the PowerToys Run plugin settings. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added the Workspaces file to the list of files that gets backed up by the Back up / Restore functionality.
- Fixed an issue causing some of the selected character sets to be unselected when opening the character set expander in the Quick Accent page.
- Improved GPO logic, icons, info bar layout and enabled state of all modules settings pages. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed some accessibility issues and refactored and improved quality of the code related to image sizes in the Image Resizer page. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed mentions of "Backup" to "Back up" when it should be used as a verb. Thanks [@JackStuart](https://github.com/JackStuart)!
- Added a "New" label to Settings to better highlight new utilities that get released. Thanks [@niels9001](https://github.com/niels9001) for the UI tweaks!
### Text Extractor
- Fixed many accessibility and UI issues on the overlay UI. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Workspaces
- Added support for Progressive Web Applications to Workspaces.
- Implemented a feature to move existing windows instead of creating new ones.
- Fixed a crash when opening the workspaces editor that was caused by passing incorrect encoder parameters when saving Bitmap files.
- Workspaces editor position is now saved so that we can start it at the same position when we open it again.
- Fixed an issue causing many instances of the same application to be put in the same position instead of the intended position due to timer issues.
- Fixed detection of exact application version when many versions of the same application are installed.
- Fixed an issue causing the Workspaces Editor to start outside of visible desktop area.
- Fixed an issue to maintain command line arguments for applications when trying using the "Launch and Edit" feature.
### Video Conference Mute
- The module has been deprecated in 0.88.0, being removed from PowerToys.
### ZoomIt
- New utility: Zoom It - a screen zoom, annotation, and recording tool for technical presentations and demos. This utility from Sysinternals has had its source code released and included in PowerToys. ZoomIt will still continue to be updated and shipped by Sysinternals for users who prefer to have it as a standalone utility outside of PowerToys. Thanks [@markrussinovich](https://github.com/markrussinovich), [@foxmsft](https://github.com/foxmsft) and [@johnstep](https://github.com/johnstep) for contributing the original code and reviewing the PowerToys integration!
### Documentation
- Improved language in CONTRIBUTE.md. Thanks [@sanskaarz](https://github.com/sanskaarz)!
- Added Bilibili plugin mention to thirdPartyRunPlugins.md. Thanks [@Whuihuan](https://github.com/Whuihuan)!
- Added CanIUse and TailwindCSS plugins mention to thirdPartyRunPlugins.md. Thanks [@skttl](https://github.com/skttl)!
- Added HttpStatusCodes plugin mention to thirdPartyRunPlugins.md. Thanks [@grzhan](https://github.com/grzhan)!
- Updated COMMUNITY.md with more contributors.
- Updated the PowerToys Run documentation to reflect documentation pages for new plugins.
- Added YubicoOauthOTP plugin mention to thirdPartyRunPlugins.md. Thanks [@dlnilsson](https://github.com/dlnilsson)!
### Development
- Upgraded to .NET 9. Thanks [@snickler](https://github.com/snickler)!
- Fixed building on Visual Studio 17.12.
- Upgraded the System.IO.Abstractions dependency to 21.0.29. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Upgraded the WindowsAppSDK dependency to 1.6.241114003. Thanks [@shuaiyuanxx](https://github.com/shuaiyuanxx)!
- Upgraded the MSTest dependency to 3.6.3. Thanks [@Youssef1313](https://github.com/Youssef1313)!
- Upgraded the check-spelling CI dependency to 0.0.24 and fixed related spell checking issues. Thanks [@jsoref](https://github.com/jsoref)!
- Removed duplicate names from the spellcheck allowed names file. Thanks [@htcfreek](https://github.com/htcfreek)!
- Improved logging of asynchronous methods call stacks when logging an error.
- Created a MSBuild props file to be imported by other projects to enable AOT support.
- Made the Peek utility source code AOT compatible.
- Updated .editorconfig rules to relax squiggly IDE errors in Visual Studio 17.12. Thanks [@snickler](https://github.com/snickler)!
- Moved Xaml.Styler from the root to the src folder.
- Added fuzz testing for AdvancedPaste, with a new pipeline for OneFuzz.
- Added a new CI pipeline to build with the latest WindowsAppSDK.
- Added a new CI pipeline to build with the latest webview2 from Edge Canary.
- Made the HostsUILib project AOT compatible. Thanks [@snickler](https://github.com/snickler) for your help reviewing this!
- Made FilePreviewCommon and MarkdownPreviewHandler AOT compatible. Thanks [@snickler](https://github.com/snickler) for your help reviewing this!
- Made the PowerAccent.Core project AOT compatible. Thanks [@snickler](https://github.com/snickler) for your help reviewing this!
- Cleaned up some code for AOT compatibility in the Advanced Paste module. Thanks [@snickler](https://github.com/snickler) for your help reviewing this!
- Removed the prerelease flag from the PowerToys development DSC configurations. Thanks [@denelon](https://github.com/denelon)!
- Improved Dart CI reliability by improving error messages and retrying to the step that installs the correct dotnet version.
- Improved Dart CI reliability by fixing retries when downloading the localization files.
- Improved Dart CI build times by removing the steps to build the no longer needed abstracted utility nuget packages.
- Removed the solution.props file from the solution root.
- Fixed PowerToys Run Calculator plugin tests when running in systems with different number formats. Thanks [@htcfreek](https://github.com/htcfreek)!
- Updated many .NET packages from .NET 9.0.0 to 9.0.1 for security fixes. Thanks [@snickler](https://github.com/snickler)!
- Refactored the Mouse Without Borders Common.Log.cs and Common.Receiver.cs files. Thanks [@mikeclayton](https://github.com/mikeclayton)!
#### What is being planned for version 0.88
#### What is being planned for version 0.89
For [v0.88][github-next-release-work], we'll work on the items below:
For [v0.89][github-next-release-work], we'll work on the items below:
- Stability / bug fixes
- New module: File Actions Menu
- Integrate Sysinternals ZoomIt
- PowerToys Run v2 development work
## PowerToys Community

View File

@@ -3,8 +3,8 @@
The Settings v2 process uses two way IPC to communicate with the runner process.
## Initialization
- On the settings' side, the two way IPC delegates are contained with the [`ShellPage.xaml.cs`](/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs) file. The delegates are static and the views for all the powerToys send the ipc information to the viewmodels as `ShellPage.DefaultSndMSGCallBack`.
- These delegates are initialized within the [`MainWindow.xaml.cs`](/src/settings-ui/Settings.UI/MainWindow.xaml.cs) file in the `Settings.Runner` project.
- On the settings' side, the two way IPC delegates are contained with the [`ShellPage.xaml.cs`](/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs) file. The delegates are static and the views for all the powerToys send the ipc information to the viewmodels as `ShellPage.DefaultSndMSGCallBack`.
- These delegates are initialized within the [`MainWindow.xaml.cs`](/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs) file in the `Settings.Runner` project.
## Types of IPC delegates
@@ -14,12 +14,12 @@ The Settings v2 process uses two way IPC to communicate with the runner process.
3. `CheckForUpdates`
## Sending information to runner
- The settings process communicates with the runner by using the delegates defined within the [`ShellPage.xaml.cs`](/src/settings-ui/Settings.UI/Views/ShellPage.xaml.cs) file.
- The settings process communicates with the runner by using the delegates defined within the [`ShellPage.xaml.cs`](/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs) file.
- Depending on the type of object sending the information, the json is created accordingly.
- If any information has been modified by the user in the GeneralSettings page, then the json file sent to the runner has the name set to `general`, whereas if any information has been modified by the user in any powertoy related settings page, the name of the json file being communicated with the runner is set to `powertoy`.
## Receiving information from runner
- The `ShellPage`object has a `IPCResponseHandleList` which is a list of functions which handle IPC responses.
- The `ShellPage` object has a `IPCResponseHandleList` which is a list of functions which handle IPC responses.
```csharp
// receive IPC Message
@@ -43,4 +43,4 @@ Program.IPCMessageReceivedCallback = (string msg) =>
```
- Whenever any information is sent from the runner each of the functions in the handle list perform their action on that json object.
- One example of where information sent from the runner is being processed by the settings is in [`GeneralPage.xaml.cs`](/src/settings-ui/Settings.UI/Views/GeneralPage.xaml.cs) when the user clicks the check for updates button. The information displayed after, such as the user has the latest version installed is a result of this handle.
- One example of where information sent from the runner is being processed by the settings is in [`GeneralPage.xaml.cs`](/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs) when the user clicks the check for updates button. The information displayed after, such as the user has the latest version installed is a result of this handle.

View File

@@ -0,0 +1,27 @@
// 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.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.Events;
namespace Microsoft.PowerToys.UITest
{
public class Button : Element
{
public Button()
: base()
{
}
public string GetButtonType()
{
Assert.IsNotNull(WindowsElement, "WindowsElement should not be null");
return WindowsElement.GetAttribute("ControlType");
}
}
}

View File

@@ -0,0 +1,54 @@
// 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 OpenQA.Selenium;
namespace Microsoft.PowerToys.UITest
{
#pragma warning disable SA1649 // File name should match first type name
public class By
{
private readonly OpenQA.Selenium.By by;
private By(OpenQA.Selenium.By by)
{
this.by = by;
}
public static By Name(string name)
{
return new By(OpenQA.Selenium.By.Name(name));
}
public static By Id(string id)
{
return new By(OpenQA.Selenium.By.Id(id));
}
public static By XPath(string xpath)
{
return new By(OpenQA.Selenium.By.XPath(xpath));
}
public static By CssSelector(string xpath)
{
return new By(OpenQA.Selenium.By.CssSelector(xpath));
}
public static By LinkText(string linkText)
{
return new By(OpenQA.Selenium.By.LinkText(linkText));
}
public static By TagName(string tagName)
{
return new By(OpenQA.Selenium.By.TagName(tagName));
}
public OpenQA.Selenium.By ToSeleniumBy()
{
return by;
}
}
}

View File

@@ -0,0 +1,188 @@
// 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.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.Events;
using static Microsoft.PowerToys.UITest.UITestBase;
namespace Microsoft.PowerToys.UITest
{
public class Element
{
public WindowsElement? WindowsElement { get; set; }
public Element()
{
WindowsElement = null;
}
public void SetWindowsElement(WindowsElement windowsElement)
{
WindowsElement = windowsElement;
}
public string GetName()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
return " ";
}
return WindowsElement.GetAttribute("Name");
}
public string GetText()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
return " ";
}
return WindowsElement.GetAttribute("Value");
}
public string GetAutomationId()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
return " ";
}
return WindowsElement.GetAttribute("AutomationId");
}
public string GetClassName()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
return " ";
}
return WindowsElement.GetAttribute("ClassName");
}
public bool IsEnable()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
}
return WindowsElement?.GetAttribute("IsEnabled") == "True" ? true : false;
}
public bool IsSelected()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
}
return WindowsElement?.GetAttribute("IsSelected") == "True" ? true : false;
}
public void Click()
{
Session? session = SessionManager.Current;
var element = WindowsElement;
Actions actions = new Actions(session);
actions.MoveToElement(element);
actions.Click();
actions.Build().Perform();
}
public void RightClick()
{
Session? session = SessionManager.Current;
var element = WindowsElement;
Actions actions = new Actions(session);
actions.MoveToElement(element);
actions.MoveByOffset(5, 5);
actions.ContextClick();
actions.Build().Perform();
}
public void ClickCheckAttribute(string attributeKey, string attributeValue)
{
Session? session = SessionManager.Current;
var elements = WindowsElement;
Actions actions = new Actions(session);
if (elements?.GetAttribute(attributeKey) == attributeValue)
{
actions.MoveToElement(elements);
actions.Click();
actions.Build().Perform();
actions.MoveByOffset(5, 5);
}
}
public bool CheckAttribute(string attributeKey, string attributeValue)
{
var elements = WindowsElement;
return elements?.GetAttribute(attributeKey) == attributeValue;
}
public T FindElementByName<T>(string name)
where T : Element, new()
{
var item = WindowsElement?.FindElementByName(name) as WindowsElement;
Assert.IsNotNull(item, "Can`t find this element");
T element = new T();
element.SetWindowsElement(item);
return element;
}
public T? FindElementByAccessibilityId<T>(string name)
where T : Element, new()
{
var item = WindowsElement?.FindElementByAccessibilityId(name) as WindowsElement;
Assert.IsNotNull(item, "Can`t find this element");
T element = new T();
element.SetWindowsElement(item);
return element;
}
public ReadOnlyCollection<T>? FindElementsByName<T>(string name)
where T : Element, new()
{
var items = WindowsElement?.FindElementsByName(name);
Assert.IsNotNull(items, "Can`t find this element");
List<T> res = new List<T>();
foreach (var item in items)
{
T element = new T();
var itemTemp = item as WindowsElement;
if (itemTemp != null)
{
element.SetWindowsElement(itemTemp);
}
res.Add(element);
}
var resReadOnlyCollection = new ReadOnlyCollection<T>(res);
return resReadOnlyCollection;
}
public Screenshot? GetScreenShot()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
return null;
}
return WindowsElement?.GetScreenshot();
}
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.Events;
namespace Microsoft.PowerToys.UITest
{
public class Window : Element
{
public Window()
: base()
{
}
public string GetHelpText()
{
if (WindowsElement == null)
{
Assert.IsNotNull(null);
return " ";
}
return WindowsElement.GetAttribute("HelpText");
}
public bool IsVisible()
{
Assert.IsNotNull(WindowsElement, "WindowsElement should not be null");
return WindowsElement.Displayed;
}
public Window Maximize()
{
Assert.IsNotNull(WindowsElement, "WindowsElement should not be null");
Assert.IsTrue(IsVisible(), "Window is not visible");
FindElementByName<Button>("Maximize").Click();
return this;
}
public Window Restore()
{
Assert.IsNotNull(WindowsElement, "WindowsElement should not be null");
Assert.IsTrue(IsVisible(), "Window is not visible");
FindElementByName<Button>("Restore").Click();
return this;
}
public Window Minimize()
{
Assert.IsNotNull(WindowsElement, "WindowsElement should not be null");
Assert.IsTrue(IsVisible(), "Window is not visible");
FindElementByName<Button>("Minimize").Click();
return this;
}
}
}

View File

@@ -0,0 +1,77 @@
// 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;
namespace Microsoft.PowerToys.UITest
{
public enum PowerToysModule
{
None,
Fancyzone,
KeyboardManager,
Hosts,
}
public enum PowerToysModuleWindow
{
None,
PowerToys,
Fancyzone,
KeyboardManagerKeys,
KeyboardManagerShortcuts,
Hosts,
}
public class ModuleConfigData
{
private static readonly Lazy<ModuleConfigData> MInstance = new Lazy<ModuleConfigData>(() => new ModuleConfigData());
public static ModuleConfigData Instance
{
get
{
return MInstance.Value;
}
}
public Dictionary<PowerToysModuleWindow, ModuleWindowData> ModuleWindowName { get; private set; }
private Dictionary<PowerToysModule, string> ModulePath { get; set; }
private ModuleConfigData()
{
ModuleWindowName = new Dictionary<PowerToysModuleWindow, ModuleWindowData>();
ModuleWindowName[PowerToysModuleWindow.Fancyzone] = new ModuleWindowData("PowerToys", "PowerToys Settings");
ModuleWindowName[PowerToysModuleWindow.Fancyzone] = new ModuleWindowData("Fancyzone", "FancyZones Layout");
ModuleWindowName[PowerToysModuleWindow.KeyboardManagerKeys] = new ModuleWindowData("KeyboardManagerKeys", "Remap keys");
ModuleWindowName[PowerToysModuleWindow.KeyboardManagerShortcuts] = new ModuleWindowData("KeyboardManagerShortcuts", "Remap shortcuts");
ModuleWindowName[PowerToysModuleWindow.Hosts] = new ModuleWindowData("Hosts", "Hosts File Editor");
ModulePath = new Dictionary<PowerToysModule, string>();
ModulePath[PowerToysModule.Fancyzone] = @"\..\..\..\PowerToys.FancyZones.exe";
ModulePath[PowerToysModule.Hosts] = @"\..\..\..\WinUI3Apps\PowerToys.Hosts.exe";
}
public string GetModulePath(PowerToysModule scope)
{
return ModulePath[scope];
}
public ModuleWindowData GetModuleWindowData(PowerToysModuleWindow scope)
{
return ModuleWindowName[scope];
}
}
public struct ModuleWindowData(string moduleName, string windowName)
{
public string ModuleName = moduleName;
public string WindowName = windowName;
}
}

62
src/UITestAPI/Session.cs Normal file
View File

@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Interfaces;
using OpenQA.Selenium.Appium.Service;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;
namespace Microsoft.PowerToys.UITest
{
public class Session : WindowsDriver<WindowsElement>
{
public Session(Uri remoteAddress, AppiumOptions appiumOptions)
: base(remoteAddress, appiumOptions)
{
}
public T FindElement<T>(By by)
where T : Element, new()
{
var item = FindElement(by.ToSeleniumBy());
Assert.IsNotNull(item, "Can`t find this element");
T element = new T();
element.SetWindowsElement(item);
return element;
}
public T FindElementByName<T>(string name)
where T : Element, new()
{
var item = FindElementByName(name);
Assert.IsNotNull(item, "Can`t find this element");
T element = new T();
element.SetWindowsElement(item);
return element;
}
public ReadOnlyCollection<T>? FindElementsByName<T>(string name)
where T : Element, new()
{
var items = FindElementsByName(name);
Assert.IsNotNull(items, "Can`t find this element");
List<T> res = new List<T>();
foreach (var item in items)
{
T element = new T();
element.SetWindowsElement(item);
res.Add(element);
}
var resReadOnlyCollection = new ReadOnlyCollection<T>(res);
return resReadOnlyCollection;
}
}
}

View File

@@ -0,0 +1,228 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
namespace Microsoft.PowerToys.UITest
{
public class SessionManager
{
public static Session? Current { get; private set; }
private static string sessionPath = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe";
private static Process? appDriver;
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(nint hWnd);
private struct WinDriver
{
public Session Session { get; set; }
public string AppName;
public string WindowName;
}
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
private static Session? Root { get; set; }
private static WinDriver CurrentDriver { get; set; }
private static Stack<WinDriver> mWindowList = new Stack<WinDriver>();
private static Stack<WinDriver> mWindowListTemp = new Stack<WinDriver>();
protected SessionManager()
{
}
static SessionManager()
{
if (mWindowList == null)
{
mWindowList = new Stack<WinDriver>();
}
if (mWindowListTemp == null)
{
mWindowListTemp = new Stack<WinDriver>();
}
var desktopCapabilities = new AppiumOptions();
desktopCapabilities.AddAdditionalCapability("app", "Root");
Root = new Session(new Uri(WindowsApplicationDriverUrl), desktopCapabilities);
Current = Root;
}
public static void SetScope(PowerToysModule scope)
{
sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
}
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public static void Init()
{
appDriver = Process.Start("C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe");
// Launch Exe
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
path += sessionPath;
StartExe("PowerToys", "PowerToys Settings", path);
var session = Current;
Assert.IsNotNull(session, "Session not initialized");
// Set implicit timeout to make element search to retry every 500 ms
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3);
}
public static void UnInit()
{
sessionPath = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe";
var session = Current;
// Close the session
if (session != null)
{
session.Quit();
session.Dispose();
}
try
{
appDriver?.Kill();
}
catch
{
}
}
// Take control of an application that already exists
public static Session? AttachSession(PowerToysModuleWindow module)
{
string windowName = ModuleConfigData.Instance.GetModuleWindowData(module).WindowName;
string appName = ModuleConfigData.Instance.GetModuleWindowData(module).ModuleName;
if (Root != null)
{
if (SwitchApp(appName) == true)
{
return Current;
}
var window = Root.FindElementByName(windowName);
if (window == null)
{
Assert.IsNotNull(null, windowName + " not found");
return null;
}
var windowHandle = new nint(int.Parse(window.GetAttribute("NativeWindowHandle")));
SetForegroundWindow(windowHandle);
var hexWindowHandle = windowHandle.ToString("x");
var appCapabilities = new AppiumOptions();
appCapabilities.AddAdditionalCapability("appTopLevelWindow", hexWindowHandle);
appCapabilities.AddAdditionalCapability("deviceName", "WindowsPC");
var appSession = new Session(new Uri(WindowsApplicationDriverUrl), appCapabilities);
WinDriver winDriver = default;
winDriver.Session = appSession;
winDriver.AppName = appName;
winDriver.WindowName = windowName;
if (CurrentDriver.Session != null)
{
mWindowList.Push(CurrentDriver);
}
CurrentDriver = winDriver;
Current = appSession;
}
else
{
Assert.IsNotNull(Root, "Root driver is null");
}
return null;
}
// Create a new application and take control of it
private static void StartExe(string appName, string windowName, string appPath)
{
AppiumOptions opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
var session = new Session(new Uri(WindowsApplicationDriverUrl), opts);
WinDriver winDriver = default;
winDriver.Session = session;
winDriver.AppName = appName;
winDriver.WindowName = windowName;
if (CurrentDriver.Session != null)
{
mWindowList.Push(CurrentDriver);
}
CurrentDriver = winDriver;
Current = session;
}
// Use the name to switch the current driver
private static bool SwitchApp(string appName)
{
while (mWindowList.Count > 0)
{
var driver = mWindowList.Peek();
if (driver.AppName == appName)
{
WinDriver driverTemp = mWindowList.Pop();
while (mWindowListTemp.Count > 0)
{
mWindowList.Push(mWindowListTemp.Pop());
}
// Check session is live
var elements = driverTemp.Session.FindElementsByAccessibilityId("elementId");
if (elements.Count <= 0)
{
return false;
}
mWindowList.Push(CurrentDriver);
CurrentDriver = driverTemp;
Current = CurrentDriver.Session;
if (CurrentDriver.Session != null)
{
var windowHandle = new nint(int.Parse(CurrentDriver.Session.FindElementByName(CurrentDriver.WindowName).GetAttribute("NativeWindowHandle")));
SetForegroundWindow(windowHandle);
}
return true;
}
else
{
mWindowListTemp.Push(driver);
mWindowList.Pop();
}
}
while (mWindowListTemp.Count > 0)
{
mWindowList.Push(mWindowListTemp.Pop());
}
return false;
}
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
<RootNamespace>Microsoft.PowerToys.UITest</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Appium.WebDriver" />
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,98 @@
// 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.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
using static Microsoft.PowerToys.UITest.ModuleConfigData;
using static Microsoft.PowerToys.UITest.UITestBase;
namespace Microsoft.PowerToys.UITest
{
public class UITestBase
{
public Session? Session { get; set; }
public UITestBase()
{
SessionManager.Init();
Session = SessionManager.Current;
}
public UITestBase(PowerToysModule scope)
{
SessionManager.SetScope(scope);
SessionManager.Init();
Session = SessionManager.Current;
}
~UITestBase()
{
SessionManager.UnInit();
Session = null;
}
public static void Enable_Module_from_Dashboard(string moduleName, PowerToysModuleWindow module = PowerToysModuleWindow.None)
{
Session? session = SessionManager.Current;
var elements = session?.FindElementsByName<Element>("Enable module");
Actions actions = new Actions(session);
bool buttonFound = false;
if (elements != null)
{
foreach (var element in elements)
{
if (element.CheckAttribute("HelpText", moduleName))
{
if (element.CheckAttribute("Toggle.ToggleState", "0"))
{
element.Click();
}
buttonFound = true;
break;
}
}
}
Assert.IsTrue(buttonFound, $"No button with elementName '{moduleName}' and HelpText '{moduleName}' was found.");
}
public static void Disable_Module_from_Dashboard(string moduleName, PowerToysModuleWindow module = PowerToysModuleWindow.None)
{
Session? session = SessionManager.Current;
var elements = session?.FindElementsByName<Element>("Enable module");
Actions actions = new Actions(session);
bool buttonFound = false;
if (elements != null)
{
foreach (var element in elements)
{
if (element.CheckAttribute("HelpText", moduleName))
{
if (element.CheckAttribute("Toggle.ToggleState", "1"))
{
element.Click();
}
buttonFound = true;
break;
}
}
}
Assert.IsTrue(buttonFound, $"No button with elementName '{moduleName}' and HelpText '{moduleName}' was found.");
}
}
}

View File

@@ -20,16 +20,16 @@ namespace Microsoft.PowerToys.Telemetry
try
{
registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsRegistryValueName, 0);
if (registryValue is not null)
{
return (int)registryValue == 1 ? true : false;
}
}
catch
{
}
if (registryValue is not null)
{
return (int)registryValue == 1 ? true : false;
}
return false;
}

View File

@@ -174,9 +174,8 @@ namespace PTSettingsHelper
return;
}
const bool value = enabled;
const size_t buf_size = sizeof(bool);
if (RegSetValueExW(key, DataDiagnosticsRegValueName, 0, REG_QWORD, reinterpret_cast<const BYTE*>(&value), buf_size) != ERROR_SUCCESS)
const DWORD value = enabled ? 1 : 0;
if (RegSetValueExW(key, DataDiagnosticsRegValueName, 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value)) != ERROR_SUCCESS)
{
RegCloseKey(key);
return;

View File

@@ -82,12 +82,20 @@ namespace updating
// prevent the warning that may show up depend on the value of the constants (#defines)
#pragma warning(push)
#pragma warning(disable : 4702)
#if USE_STD_EXPECTED
std::future<std::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease)
#else
std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease)
#endif
{
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
if constexpr (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
{
#if USE_STD_EXPECTED
co_return std::unexpected(LOCAL_BUILD_ERROR);
#else
co_return nonstd::make_unexpected(LOCAL_BUILD_ERROR);
#endif
}
try
@@ -139,7 +147,11 @@ namespace updating
catch (...)
{
}
#if USE_STD_EXPECTED
co_return std::unexpected(NETWORK_ERROR);
#else
co_return nonstd::make_unexpected(NETWORK_ERROR);
#endif
}
#pragma warning(pop)

View File

@@ -6,7 +6,14 @@
#include <filesystem>
#include <variant>
#include <winrt/Windows.Foundation.h>
//#if __MSVC_VERSION__ >= 1933 // MSVC begin to support std::unexpected in 19.33
#if __has_include(<expected> ) // use the same way with excepted-lite to detect std::unexcepted, as using it as backup
#include <expected>
#define USE_STD_EXPECTED 1
#else
#include <expected.hpp>
#define USE_STD_EXPECTED 0
#endif
#include <common/version/helper.h>
@@ -27,7 +34,11 @@ namespace updating
std::future<std::optional<std::filesystem::path>> download_new_version(const new_version_download_info& new_version);
std::filesystem::path get_pending_updates_path();
#if USE_STD_EXPECTED
std::future<std::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease = false);
#else
std::future<nonstd::expected<github_version_info, std::wstring>> get_github_version_info_async(const bool prerelease = false);
#endif
void cleanup_updates();
// non-localized

View File

@@ -10,6 +10,7 @@
#include <winrt/Windows.Management.Deployment.h>
#include "../logger/logger.h"
#include "../version/version.h"
namespace package {
inline BOOL IsWin11OrGreater()
@@ -47,10 +48,14 @@ namespace package {
for (auto const& package : packageManager.FindPackagesForUser({}))
{
const auto& packageFullName = std::wstring{ package.Id().FullName() };
const auto& packageVersion = package.Id().Version();
if (packageFullName.contains(packageDisplayName))
{
return true;
if (packageVersion.Major == VERSION_MAJOR && packageVersion.Minor == VERSION_MINOR && packageVersion.Revision == VERSION_REVISION)
{
return true;
}
}
}
@@ -72,6 +77,7 @@ namespace package {
// Declare use of an external location
AddPackageOptions options;
options.ExternalLocationUri(externalUri);
options.ForceUpdateFromAnyVersion(true);
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.AddPackageByUriAsync(packageUri, options);
deploymentOperation.get();

View File

@@ -262,6 +262,10 @@ namespace MouseWithoutBorders
new Task(() =>
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
System.Threading.Thread thread = Thread.CurrentThread;
thread.Name = $"{nameof(SendClipboardDataUsingTCP)}.{thread.ManagedThreadId}";
Thread.UpdateThreads(thread);
@@ -386,6 +390,10 @@ namespace MouseWithoutBorders
new Task(() =>
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
System.Threading.Thread thread = Thread.CurrentThread;
thread.Name = $"{nameof(ConnectAndGetData)}.{thread.ManagedThreadId}";
Thread.UpdateThreads(thread);

View File

@@ -72,6 +72,10 @@ namespace MouseWithoutBorders
private static void HelperThread()
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = System.Threading.ExecutionContext.SuppressFlow();
try
{
while (true)

View File

@@ -379,6 +379,10 @@ namespace MouseWithoutBorders.Class
private static void InputCallbackThread()
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
Common.InputCallbackThreadID = Thread.CurrentThread.ManagedThreadId;
while (!Common.InitDone)
{

View File

@@ -681,6 +681,10 @@ namespace MouseWithoutBorders.Class
private void TCPServerThread(object param)
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
try
{
TcpListener server = param as TcpListener;
@@ -768,6 +772,10 @@ namespace MouseWithoutBorders.Class
{
void ServerThread()
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
try
{
// Receiving packages
@@ -876,6 +884,10 @@ namespace MouseWithoutBorders.Class
{
void ClientThread(object obj)
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
IPHostEntry host;
bool useName2IP = false;
List<IPAddress> validAddresses = new();
@@ -1117,6 +1129,10 @@ namespace MouseWithoutBorders.Class
{
void NewTcpClient()
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
TcpClient tcpClient = null;
try
@@ -1549,6 +1565,10 @@ namespace MouseWithoutBorders.Class
private static void AcceptConnectionAndSendClipboardData(object param)
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
TcpListener server = param as TcpListener;
do
@@ -1590,6 +1610,10 @@ namespace MouseWithoutBorders.Class
{
new Task(() =>
{
// SuppressFlow fixes an issue on service mode, where the helper process can't get enough permissions to be started again.
// More details can be found on: https://github.com/microsoft/PowerToys/pull/36892
using var asyncFlowControl = ExecutionContext.SuppressFlow();
System.Threading.Thread thread = Thread.CurrentThread;
thread.Name = $"{nameof(SendOrReceiveClipboardData)}.{thread.ManagedThreadId}";
Thread.UpdateThreads(thread);

View File

@@ -6023,6 +6023,12 @@ LRESULT APIENTRY MainWndProc(
// Apply tray icon setting
EnableDisableTrayIcon(hWnd, g_ShowTrayIcon);
// This is also called by ZoomIt when it starts and loads the Settings. Opacity is added after loading from registry, so we use the same pattern.
if ((g_PenColor >> 24) == 0)
{
g_PenColor |= 0xFF << 24;
}
// Apply hotkey settings
UnregisterAllHotkeys(hWnd);
g_ToggleMod = GetKeyMod(g_ToggleKey);

View File

@@ -105,9 +105,12 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
}
else if (special_semantics->second == SPECIAL_SEMANTICS_COLOR)
{
// PowerToys settings likes colors as #FFFFFF strings.
/* PowerToys settings likes colors as #FFFFFF strings.
But currently these Settings are internal state for ZoomIt, not something that we really need to send Settings.
Code is kept here as a reference if a future color Setting ends up being configured.
hstring s = winrt::to_hstring(std::format("#{:02x}{:02x}{:02x}", value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF));
_settings.add_property(curSetting->ValueName, s);
*/
}
}
break;
@@ -211,6 +214,9 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
}
else if (special_semantics->second == SPECIAL_SEMANTICS_COLOR)
{
/* PowerToys settings likes colors as #FFFFFF strings.
But currently these Settings are internal state for ZoomIt, not something that we really need to save from Settings.
Code is kept here as a reference if a future color Setting ends up being configured.
auto possibleValue = valuesFromSettings.get_string_value(curSetting->ValueName);
if (possibleValue.has_value())
{
@@ -219,8 +225,8 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
{
*static_cast<PDWORD>(curSetting->Setting) = RGB(r, g, b);
}
}
*/
}
}
break;

View File

@@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.FancyZonesEditor.UITests
{
[TestClass]
public class Init
{
private static Process? appDriver;
[AssemblyInitialize]
public static void SetupAll(TestContext context)
{
string winAppDriverPath = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe";
context.WriteLine($"Attempting to launch WinAppDriver at: {winAppDriverPath}");
appDriver = Process.Start(winAppDriverPath);
}
[AssemblyCleanup]
public static void CleanupAll()
{
try
{
appDriver?.Kill();
}
catch
{
}
}
}
}

View File

@@ -2,32 +2,27 @@
// 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.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.FancyZones.UnitTests.Utils;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_FancyZones
{
[TestClass]
public class RunFancyZonesTest
public class RunFancyZonesTest : UITestBase
{
private static FancyZonesSession? _session;
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
_session = new FancyZonesSession(testContext);
}
[ClassCleanup]
public static void ClassCleanup()
{
_session?.Close();
}
[TestMethod]
public void RunFancyZones()
{
Assert.IsNotNull(_session?.FancyZonesProcess);
Thread.Sleep(2000);
Session?.FindElementByName<Element>("Launch layout editor").Click();
Thread.Sleep(4000);
Session = SessionManager.AttachSession(PowerToysModuleWindow.Fancyzone);
}
}
}

View File

@@ -23,6 +23,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\UITestAPI\UITestAPI.csproj" />
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.FancyZonesEditor.UITests
{
[TestClass]
public class Init
{
private static Process? appDriver;
[AssemblyInitialize]
public static void SetupAll(TestContext context)
{
string winAppDriverPath = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe";
context.WriteLine($"Attempting to launch WinAppDriver at: {winAppDriverPath}");
appDriver = Process.Start(winAppDriverPath);
}
[AssemblyCleanup]
public static void CleanupAll()
{
try
{
appDriver?.Kill();
}
catch
{
}
}
}
}

View File

@@ -3,22 +3,35 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading;
using FancyZonesEditorCommon.Data;
using Microsoft.FancyZonesEditor.UITests;
using Microsoft.FancyZonesEditor.UnitTests.Utils;
using Microsoft.FancyZonesEditor.UITests.Utils;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_FancyZonesEditor
{
[TestClass]
public class RunFancyZonesEditorTest
public class RunFancyZonesEditorTest : UITestBase
{
private static FancyZonesEditorSession? _session;
private static FancyZonesEditorFiles? _files;
private static TestContext? _context;
[AssemblyInitialize]
public static void SetupAll(TestContext context)
{
}
[AssemblyCleanup]
public static void CleanupAll()
{
}
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
public static void ClassInit(TestContext testContext)
{
_context = testContext;
@@ -48,7 +61,8 @@ namespace UITests_FancyZonesEditor
},
},
};
FancyZonesEditorSession.Files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
_files = new FancyZonesEditorFiles();
_files.ParamsIOHelper.WriteData(editorParameters.Serialize(parameters));
LayoutTemplates layoutTemplates = new LayoutTemplates();
LayoutTemplates.TemplateLayoutsListWrapper templateLayoutsListWrapper = new LayoutTemplates.TemplateLayoutsListWrapper
@@ -98,81 +112,75 @@ namespace UITests_FancyZonesEditor
},
},
};
FancyZonesEditorSession.Files.LayoutTemplatesIOHelper.WriteData(layoutTemplates.Serialize(templateLayoutsListWrapper));
_files.LayoutTemplatesIOHelper.WriteData(layoutTemplates.Serialize(templateLayoutsListWrapper));
CustomLayouts customLayouts = new CustomLayouts();
CustomLayouts.CustomLayoutListWrapper customLayoutListWrapper = new CustomLayouts.CustomLayoutListWrapper
{
CustomLayouts = new List<CustomLayouts.CustomLayoutWrapper> { },
};
FancyZonesEditorSession.Files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(customLayoutListWrapper));
_files.CustomLayoutsIOHelper.WriteData(customLayouts.Serialize(customLayoutListWrapper));
DefaultLayouts defaultLayouts = new DefaultLayouts();
DefaultLayouts.DefaultLayoutsListWrapper defaultLayoutsListWrapper = new DefaultLayouts.DefaultLayoutsListWrapper
{
DefaultLayouts = new List<DefaultLayouts.DefaultLayoutWrapper> { },
};
FancyZonesEditorSession.Files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(defaultLayoutsListWrapper));
_files.DefaultLayoutsIOHelper.WriteData(defaultLayouts.Serialize(defaultLayoutsListWrapper));
LayoutHotkeys layoutHotkeys = new LayoutHotkeys();
LayoutHotkeys.LayoutHotkeysWrapper layoutHotkeysWrapper = new LayoutHotkeys.LayoutHotkeysWrapper
{
LayoutHotkeys = new List<LayoutHotkeys.LayoutHotkeyWrapper> { },
};
FancyZonesEditorSession.Files.LayoutHotkeysIOHelper.WriteData(layoutHotkeys.Serialize(layoutHotkeysWrapper));
_files.LayoutHotkeysIOHelper.WriteData(layoutHotkeys.Serialize(layoutHotkeysWrapper));
AppliedLayouts appliedLayouts = new AppliedLayouts();
AppliedLayouts.AppliedLayoutsListWrapper appliedLayoutsWrapper = new AppliedLayouts.AppliedLayoutsListWrapper
{
AppliedLayouts = new List<AppliedLayouts.AppliedLayoutWrapper> { },
};
FancyZonesEditorSession.Files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
_files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));
}
[ClassCleanup]
public static void ClassCleanup()
{
FancyZonesEditorSession.Files.Restore();
_context = null;
}
if (_files != null)
{
_files.Restore();
}
[TestInitialize]
public void TestInitialize()
{
_session = new FancyZonesEditorSession(_context!);
}
[TestCleanup]
public void TestCleanup()
{
_session?.Close(_context!);
if (_context != null)
{
_context = null;
}
}
[TestMethod]
public void OpenEditorWindow() // verify the session is initialized
{
Assert.IsNotNull(_session?.Session);
Assert.IsNotNull(SessionManager.Current);
}
[TestMethod]
public void OpenNewLayoutDialog() // verify the new layout dialog is opened
{
_session?.Click_CreateNewLayout();
Assert.IsNotNull(_session?.Session?.FindElementsByName("Choose layout type")); // check the pane header
Session = SessionManager.Current;
var button = Session?.FindElementByAccessibilityId("NewLayoutButton");
button?.Click();
Assert.IsNotNull(Session?.FindElementsByName("Choose layout type")); // check the pane header
}
[TestMethod]
public void OpenEditLayoutDialog() // verify the edit layout dialog is opened
{
_session?.Click_EditLayout(TestConstants.TemplateLayoutNames[Constants.TemplateLayout.Grid]);
Assert.IsNotNull(_session?.Session?.FindElementByAccessibilityId("EditLayoutDialogTitle")); // check the pane header
Assert.IsNotNull(_session?.Session?.FindElementsByName("Edit 'Grid'")); // verify it's opened for the correct layout
}
[TestMethod]
public void OpenContextMenu() // verify the context menu is opened
{
Assert.IsNotNull(_session?.OpenContextMenu(TestConstants.TemplateLayoutNames[Constants.TemplateLayout.Columns]));
Session = SessionManager.Current;
var layout = Session?.FindElementByName<Element>(TestConstants.TemplateLayoutNames[Constants.TemplateLayout.Grid]);
var button = layout?.FindElementByAccessibilityId<Element>("EditLayoutButton");
button?.Click();
Assert.IsNotNull(Session?.FindElementByAccessibilityId("EditLayoutDialogTitle")); // check the pane header
Assert.IsNotNull(Session?.FindElementsByName("Edit 'Grid'")); // verify it's opened for the correct layout
}
}
}

View File

@@ -18,7 +18,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Appium.WebDriver" />
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
@@ -28,6 +27,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\UITestAPI\UITestAPI.csproj" />
<ProjectReference Include="..\FancyZonesEditorCommon\FancyZonesEditorCommon.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,138 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Reflection;
using Microsoft.FancyZonesEditor.UITests.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.FancyZonesEditor.UnitTests.Utils
{
public class FancyZonesEditorSession
{
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
private const string FancyZonesEditorPath = @"\..\..\..\PowerToys.FancyZonesEditor.exe";
private static FancyZonesEditorFiles? _files;
public static FancyZonesEditorFiles Files
{
get
{
if (_files == null)
{
_files = new FancyZonesEditorFiles();
}
return _files;
}
}
public WindowsDriver<WindowsElement>? Session { get; }
public WindowsElement? MainEditorWindow { get; }
public FancyZonesEditorSession(TestContext testContext)
{
try
{
// Launch FancyZonesEditor
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
path += FancyZonesEditorPath;
AppiumOptions opts = new AppiumOptions();
opts.AddAdditionalCapability("app", path);
Session = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), opts);
}
catch (Exception ex)
{
testContext.WriteLine(ex.Message);
}
Assert.IsNotNull(Session, "Session not initialized");
testContext.WriteLine("Session: " + Session.SessionId.ToString());
testContext.WriteLine("Title: " + Session.Title);
// Set implicit timeout to make element search to retry every 500 ms
Session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(3);
// Find main editor window
try
{
MainEditorWindow = Session.FindElementByAccessibilityId("MainWindow1");
}
catch
{
Assert.IsNotNull(MainEditorWindow, "Main editor window not found");
}
}
public void Close(TestContext testContext)
{
// Close the session
if (Session != null)
{
try
{
// FZEditor application can be closed by explicitly closing main editor window
MainEditorWindow?.SendKeys(Keys.Alt + Keys.F4);
}
catch (Exception ex)
{
testContext.WriteLine(ex.Message);
}
Session.Quit();
Session.Dispose();
}
}
private WindowsElement? GetLayout(string layoutName)
{
var listItem = Session?.FindElementByName(layoutName);
Assert.IsNotNull(listItem, "Layout " + layoutName + " not found");
return listItem;
}
public WindowsElement? OpenContextMenu(string layoutName)
{
RightClick_Layout(layoutName);
var menu = Session?.FindElementByClassName("ContextMenu");
Assert.IsNotNull(menu, "Context menu not found");
return menu;
}
public void Click_CreateNewLayout()
{
var button = Session?.FindElementByAccessibilityId("NewLayoutButton");
Assert.IsNotNull(button, "Create new layout button not found");
button?.Click();
}
public void Click_EditLayout(string layoutName)
{
var layout = GetLayout(layoutName);
var editButton = layout?.FindElementByAccessibilityId("EditLayoutButton");
Assert.IsNotNull(editButton, "Edit button not found");
editButton.Click();
}
public void RightClick_Layout(string layoutName)
{
var layout = GetLayout(layoutName);
Actions actions = new Actions(Session);
actions.MoveToElement(layout);
actions.MoveByOffset(30, 30);
actions.ContextClick();
actions.Build().Perform();
}
}
}

View File

@@ -12,7 +12,7 @@
xmlns:props="clr-namespace:FancyZonesEditor.Properties"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ui="http://schemas.modernwpf.com/2019"
x:Name="MainWindow1"
x:Name="FancyZoneMainWindow"
Title="{x:Static props:Resources.Fancy_Zones_Editor_App_Title}"
MinWidth="360"
ui:TitleBar.Background="{DynamicResource TertiaryBackgroundBrush}"

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<ProjectGuid>{6F8C7C5A-130A-48C2-A245-3132A4121E24}</ProjectGuid>
<RootNamespace>Microsoft.KeyboardManager.UITests</RootNamespace>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<!-- This is a UI test, so don't run as part of MSBuild -->
<RunVSTest>false</RunVSTest>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\KeyboardManagerUITests\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\UITestAPI\UITestAPI.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,75 @@
// 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.Diagnostics;
using System.Threading;
using Microsoft.PowerToys.UITest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UITests_KeyboardManager
{
[TestClass]
public class RunKeyboardManagerUITests : UITestBase
{
private static TestContext? _context;
[AssemblyInitialize]
public static void SetupAll(TestContext context)
{
Debug.WriteLine("AssemblyInitialize executed");
}
[AssemblyCleanup]
public static void CleanupAll()
{
Debug.WriteLine("AssemblyCleanup executed");
}
[ClassInitialize]
public static void ClassInit(TestContext testContext)
{
Debug.WriteLine("ClassInitialize executed");
}
[ClassCleanup]
public static void ClassCleanup()
{
if (_context != null)
{
_context = null;
}
Debug.WriteLine("ClassCleanup executed");
}
[TestMethod]
public void EnableKeyboardManager() // verify the session is initialized
{
Debug.WriteLine("Test method executed");
Session = SessionManager.Current;
UITestBase.Enable_Module_from_Dashboard("Keyboard Manager");
// Launch to KeyboardManagerEditor
// session.FindElementByName<Button>("Remap a key").Click();
Session?.FindElement<Button>(By.Name("Remap a key")).Click();
Thread.Sleep(3000);
Session = SessionManager.AttachSession(PowerToysModuleWindow.KeyboardManagerKeys);
// Maximize window
Session?.FindElementByName<Window>("Remap keys").Maximize();
// Add Key Remapping
Session?.FindElementByName<Button>("Add key remapping").Click();
Session?.FindElementByName<Button>("Select").Click();
Thread.Sleep(3000);
Session?.FindElementByName<Button>("Cancel").Click();
Session?.FindElement<Button>(By.Name("Cancel")).Click();
UITestBase.Disable_Module_from_Dashboard("Keyboard Manager");
}
}
}

View File

@@ -13,6 +13,7 @@ using ManagedCommon;
using Microsoft.PowerToys.Run.Plugin.Calculator.Properties;
using Microsoft.PowerToys.Settings.UI.Library;
using Wox.Plugin;
using Wox.Plugin.Logger;
namespace Microsoft.PowerToys.Run.Plugin.Calculator
{

View File

@@ -44,12 +44,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public KeyboardKeysProperty SnipToggleKey { get; set; }
public StringProperty PenColor { get; set; }
public IntProperty PenWidth { get; set; }
public StringProperty BreakPenColor { get; set; }
public KeyboardKeysProperty BreakTimerKey { get; set; }
public StringProperty Font { get; set; }
@@ -80,24 +74,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public BoolProperty BreakShowDesktop { get; set; }
public BoolProperty BreakOnSecondary { get; set; }
public IntProperty FontScale { get; set; }
public BoolProperty ShowExpiredTime { get; set; }
public BoolProperty ShowTrayIcon { get; set; }
public BoolProperty AnimnateZoom { get; set; }
public BoolProperty TelescopeZoomOut { get; set; }
public BoolProperty SnapToGrid { get; set; }
public IntProperty ZoominSliderLevel { get; set; }
public IntProperty RecordFrameRate { get; set; }
public IntProperty RecordScaling { get; set; }
public BoolProperty CaptureAudio { get; set; }

View File

@@ -33,4 +33,10 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers
public IntPtr Hook = IntPtr.Zero;
public string Template;
}
// https://learn.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamea
public enum OpenFileNameFlags
{
OFN_NOCHANGEDIR = 0x00000008,
}
}

View File

@@ -11,6 +11,7 @@
<ResourceDictionary Source="/SettingsXAML/Controls/KeyVisual/KeyVisual.xaml" />
<ResourceDictionary Source="/SettingsXAML/Styles/TextBlock.xaml" />
<ResourceDictionary Source="/SettingsXAML/Styles/Button.xaml" />
<ResourceDictionary Source="/SettingsXAML/Styles/InfoBadge.xaml" />
<ResourceDictionary Source="/SettingsXAML/Themes/Colors.xaml" />
<ResourceDictionary Source="/SettingsXAML/Themes/Generic.xaml" />
<!-- Other merged dictionaries here -->

View File

@@ -0,0 +1,23 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="NewInfoBadge" TargetType="InfoBadge">
<Setter Property="Padding" Value="5,1,5,2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="InfoBadge">
<Border
x:Name="RootGrid"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
CornerRadius="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.InfoBadgeCornerRadius}">
<TextBlock
x:Uid="SettingsPage_NewInfoBadge"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="10" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -12,7 +12,8 @@
xmlns:viewmodels="using:Microsoft.PowerToys.Settings.UI.ViewModels"
AutomationProperties.LandmarkType="Main"
DataContext="DashboardViewModel"
mc:Ignorable="d">
mc:Ignorable="d"
x:Name="DashboardMainWindow">
<Page.Resources>
<DataTemplate x:Key="KeyVisualTemplate">
@@ -348,17 +349,28 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Width="20" Margin="0,0,0,0">
<Image
Grid.Column="0"
Width="20"
Margin="0,0,0,0">
<Image.Source>
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
</Image.Source>
</Image>
<TextBlock
<StackPanel
Grid.Column="1"
VerticalAlignment="Center"
FontWeight="SemiBold"
Text="{x:Bind Label, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
FontWeight="SemiBold"
Text="{x:Bind Label, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<InfoBadge
Margin="4,0,0,0"
Style="{StaticResource NewInfoBadge}"
Visibility="{x:Bind IsNew, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</StackPanel>
<FontIcon
Grid.Column="2"
Width="20"
@@ -440,17 +452,25 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Width="20">
<Image Grid.Column="0" Width="20">
<Image.Source>
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
</Image.Source>
</Image>
<TextBlock
<StackPanel
Grid.Column="1"
VerticalAlignment="Center"
FontWeight="SemiBold"
Text="{x:Bind Label, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
FontWeight="SemiBold"
Text="{x:Bind Label, Mode=OneWay}"
TextTrimming="CharacterEllipsis" />
<InfoBadge
Margin="4,0,0,0"
Style="{StaticResource NewInfoBadge}"
Visibility="{x:Bind IsNew, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</StackPanel>
<FontIcon
Grid.Column="2"
Width="20"

View File

@@ -108,6 +108,8 @@
x:Uid="ImageResizer_EditButton"
Width="40"
Height="36"
AutomationProperties.FullDescription="{x:Bind AccessibleTextHelper, Mode=OneWay, Converter={StaticResource ImageResizerSizeToAccessibleTextConverter}, ConverterParameter='Edit'}"
AutomationProperties.Name="{x:Bind Name, Mode=OneWay, Converter={StaticResource ImageResizerSizeToAccessibleTextConverter}, ConverterParameter='Edit'}"
Content="&#xE70F;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
Style="{StaticResource SubtleButtonStyle}"
@@ -176,6 +178,8 @@
x:Uid="ImageResizer_RemoveButton"
Width="40"
Height="36"
AutomationProperties.FullDescription="{x:Bind AccessibleTextHelper, Mode=OneWay, Converter={StaticResource ImageResizerSizeToAccessibleTextConverter}, ConverterParameter='Remove'}"
AutomationProperties.Name="{x:Bind Name, Mode=OneWay, Converter={StaticResource ImageResizerSizeToAccessibleTextConverter}, ConverterParameter='Remove'}"
Click="DeleteCustomSize"
CommandParameter="{Binding Id}"
Content="&#xE74D;"

View File

@@ -110,6 +110,9 @@
x:Uid="Shell_TopLevelSystemTools"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/SystemTools.png}"
SelectsOnInvoked="False">
<NavigationViewItem.InfoBadge>
<InfoBadge Style="{StaticResource NewInfoBadge}" />
</NavigationViewItem.InfoBadge>
<NavigationViewItem.MenuItems>
<NavigationViewItem
x:Uid="Shell_AdvancedPaste"
@@ -142,7 +145,11 @@
<NavigationViewItem
x:Uid="Shell_ZoomIt"
helpers:NavHelper.NavigateTo="views:ZoomItPage"
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ZoomIt.png}" />
Icon="{ui:BitmapIcon Source=/Assets/Settings/Icons/ZoomIt.png}">
<NavigationViewItem.InfoBadge>
<InfoBadge Style="{StaticResource NewInfoBadge}" />
</NavigationViewItem.InfoBadge>
</NavigationViewItem>
</NavigationViewItem.MenuItems>
</NavigationViewItem>

View File

@@ -35,6 +35,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
openFileName.Title = title;
openFileName.FilterIndex = initialFilter;
openFileName.DefExt = null;
openFileName.Flags = (int)OpenFileNameFlags.OFN_NOCHANGEDIR; // OFN_NOCHANGEDIR flag is needed, because otherwise GetOpenFileName overwrites the process working directory.
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow());
openFileName.Hwnd = windowHandle;

View File

@@ -4880,4 +4880,8 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="PowerLauncher_PluginWebsite.Text" xml:space="preserve">
<value>Project website</value>
</data>
<data name="SettingsPage_NewInfoBadge.Text" xml:space="preserve">
<value>NEW</value>
<comment>Must be all caps</comment>
</data>
</root>

View File

@@ -20,6 +20,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
public string Label { get; set; }
public bool IsNew { get; set; }
public string Icon { get; set; }
public string ToolTip { get; set; }

View File

@@ -74,6 +74,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
Tag = moduleType,
Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
IsNew = moduleType == ModuleType.ZoomIt,
IsEnabled = gpo == GpoRuleConfigured.Enabled || (gpo != GpoRuleConfigured.Disabled && ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType)),
IsLocked = gpo == GpoRuleConfigured.Enabled || gpo == GpoRuleConfigured.Disabled,
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),

View File

@@ -6,7 +6,6 @@ using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text.Json;
using global::PowerToys.GPOWrapper;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;