mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-29 08:29:10 +01:00
Compare commits
22 Commits
dev/vanzue
...
zhaopengwa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e60559036 | ||
|
|
4cd4fc8490 | ||
|
|
7913941c04 | ||
|
|
7897b8b1a3 | ||
|
|
306d7ccddb | ||
|
|
adb6887563 | ||
|
|
48e6b7e91d | ||
|
|
9e4105afe4 | ||
|
|
82d7b84490 | ||
|
|
d51305d7cb | ||
|
|
7f214160f2 | ||
|
|
09d4322b7c | ||
|
|
d43286661b | ||
|
|
9fccd86223 | ||
|
|
300aa9fa9f | ||
|
|
e86226f336 | ||
|
|
e801ca2bef | ||
|
|
342d24ccc8 | ||
|
|
d8feddc9da | ||
|
|
7474a54cf2 | ||
|
|
81b5706c09 | ||
|
|
9da205d2c0 |
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -993,6 +993,7 @@ NNN
|
||||
NOACTIVATE
|
||||
NOAGGREGATION
|
||||
NOASYNC
|
||||
NOCHANGEDIR
|
||||
NOCLIP
|
||||
NOCLOSEPROCESS
|
||||
NOCOALESCE
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
174
README.md
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
27
src/UITestAPI/Element/Button.cs
Normal file
27
src/UITestAPI/Element/Button.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/UITestAPI/Element/By.cs
Normal file
54
src/UITestAPI/Element/By.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
188
src/UITestAPI/Element/Element.cs
Normal file
188
src/UITestAPI/Element/Element.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/UITestAPI/Element/Window.cs
Normal file
62
src/UITestAPI/Element/Window.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/UITestAPI/ModuleConfigData.cs
Normal file
77
src/UITestAPI/ModuleConfigData.cs
Normal 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
62
src/UITestAPI/Session.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
228
src/UITestAPI/SessionManager.cs
Normal file
228
src/UITestAPI/SessionManager.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/UITestAPI/UITestAPI.csproj
Normal file
22
src/UITestAPI/UITestAPI.csproj
Normal 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>
|
||||
98
src/UITestAPI/UITestBase.cs
Normal file
98
src/UITestAPI/UITestBase.cs
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\UITestAPI\UITestAPI.csproj" />
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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}"
|
||||
|
||||
@@ -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>
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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=""
|
||||
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=""
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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; }
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user