mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-13 02:39:22 +08:00
Merge branch 'master' of https://github.com/microsoft/PowerToys
This commit is contained in:
commit
1268a463ac
10
.github/pull_request_template.md
vendored
10
.github/pull_request_template.md
vendored
@ -6,11 +6,11 @@
|
||||
|
||||
<!-- Please review the items on the PR checklist before submitting-->
|
||||
## PR Checklist
|
||||
* [ ] Closes #xxx
|
||||
* [ ] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/PowerToys) and sign the CLA
|
||||
* [ ] Tests added/passed
|
||||
* [ ] Requires documentation to be updated
|
||||
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
|
||||
* [] Applies to #xxx
|
||||
* [] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/PowerToys) and sign the CLA
|
||||
* [] Tests added/passed
|
||||
* [] Requires documentation to be updated
|
||||
* [] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
|
||||
|
||||
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
|
||||
## Detailed Description of the Pull Request / Additional comments
|
||||
|
BIN
MTNDWidget.jpg
BIN
MTNDWidget.jpg
Binary file not shown.
Before Width: | Height: | Size: 4.7 KiB |
120
README.md
120
README.md
@ -10,110 +10,121 @@ Inspired by the [Windows 95 era PowerToys project](https://en.wikipedia.org/wiki
|
||||
|
||||
_(Note: in order to run PowerToys, you'll need to be running at least Windows build 17134 or higher. Also, PowerToys does not currently support Windows ARM machines)_
|
||||
|
||||
The latest release of PowerToys can be downloaded currently a few different ways. Our current recommended way is via GitHub.
|
||||
|
||||
### GitHub
|
||||
|
||||
The first preview of these utilities can be installed from the [PowerToys GitHub releases page](https://github.com/Microsoft/powertoys/releases).
|
||||
The preview of these utilities can be installed from the [PowerToys GitHub releases page](https://github.com/Microsoft/powertoys/releases). Click on `Assets` to show the files available in the release and then click on `PowerToysSetup.msi` to download the PowerToys installer. <br />
|
||||
PDB symbols for the release are available in a separate zip file `PDB symbols.zip`.
|
||||
|
||||
### Chocolatey (Unofficial)
|
||||
|
||||
Download and upgrade PowerToys from [Chocolatey](https://chocolatey.org).
|
||||
|
||||
To install PowerToys, run the following command from the command line or from PowerShell:
|
||||
|
||||
```powershell
|
||||
choco install powertoys
|
||||
```
|
||||
|
||||
To upgrade PowerToys, run the following command from the command line or from PowerShell:
|
||||
|
||||
```powershell
|
||||
choco upgrade powertoys
|
||||
```
|
||||
|
||||
If you have any issues when installing/upgrading the package please go to the [package page](https://chocolatey.org/packages/powertoys) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
|
||||
|
||||
### Build Status
|
||||
## Build Status
|
||||
|
||||
[![Build Status](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=master)](https://dev.azure.com/ms/PowerToys/_build?definitionId=35096)
|
||||
|
||||
# What's Happening
|
||||
## PowerToy Utilities
|
||||
|
||||
## October Update
|
||||
A big thanks to everyone who has downloaded and started using the first PowerToys preview. There's been over 150K downloads so far. An even bigger thanks to everyone who has provided feedback and suggestions in the issues. The community engagement has been really awesome to see.
|
||||
### FancyZones
|
||||
|
||||
The team has been hard at working fixing bugs, addressing issues and implementing feature suggestions. If you've downloaded the code and built the latest on your machine recently you've seen many of these improvements (dark mode!). We're hoping to have a new official build out shortly with all these improvements and will also be signed by Microsoft. A new utility is also coming soon: Chris Davis is working to integrate his [SmartRename tool](https://github.com/chrdavis/SmartRename) into PowerToys!
|
||||
[FancyZones](/src/modules/fancyzones/) - FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts. The FancyZones backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/FancyZonesBacklog.md)
|
||||
|
||||
![SmartRename](https://github.com/chrdavis/SmartRename/raw/master/Images/SmartRenameDemo.gif)
|
||||
### Shortcut
|
||||
|
||||
1. [FancyZones](/src/modules/fancyzones/) - FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts. The FancyZones backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/FancyZonesBacklog.md)
|
||||
[Windows key shortcut guide](/src/modules/shortcut_guide) - The shortcut guide appears when a user holds the Windows key down for more than one second and shows the available shortcuts for the current state of the desktop. The shortcut guide backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/ShortcutGuideBacklog.md)
|
||||
|
||||
![FancyZones](src/modules/fancyzones/FancyZones.png)
|
||||
### PowerRename
|
||||
|
||||
FancyZones Video Tutorial
|
||||
[![FancyZones Video Tutorial](doc/images/FZTutorial.jpg)](https://www.youtube.com/watch?v=rTtGzZYAXgY)
|
||||
[PowerRename](/src/modules/powerrename) - PowerRename is a Windows Shell Extension for advanced bulk renaming using search and replace or regular expressions. PowerRename allows simple search and replace or more advanced regular expression matching. While you type in the search and replace input fields, the preview area will show what the items will be renamed to. PowerRename then calls into the Windows Explorer file operations engine to perform the rename. This has the benefit of allowing the rename operation to be undone after PowerRename exits.
|
||||
|
||||
2. [Windows key shortcut guide](/src/modules/shortcut_guide) - The shortcut guide appears when a user holds the Windows key down for more than one second and shows the available shortcuts for the current state of the desktop. The shortcut guide backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/ShortcutGuideBacklog.md)
|
||||
Chris Davis contributed his [SmartRename tool](https://github.com/chrdavis/SmartRename) into PowerToys!
|
||||
|
||||
![Windows key shortcut guide](doc/images/WindowsKeyShortcutGuide.jpg)
|
||||
|
||||
Additional utilities in the pipeline are:
|
||||
### Additional utilities in the pipeline are
|
||||
|
||||
* Maximize to new desktop widget - The MTND widget shows a pop-up button when a user hovers over the maximize / restore button on any window. Clicking it creates a new desktop, sends the app to that desktop and maximizes the app on the new desktop.
|
||||
* [Process terminate tool](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/Terminate%20Spec.md)
|
||||
* [Animated gif screen recorder](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/GIF%20Maker%20Spec.md)
|
||||
|
||||
# Backlog
|
||||
### Backlog
|
||||
|
||||
The full backlog of utilities can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/PowerToysBacklog.md)
|
||||
|
||||
# Where to download PowerToys
|
||||
## What's Happening
|
||||
|
||||
The latest release of PowerToys can be downloaded from https://github.com/microsoft/PowerToys/releases <br />
|
||||
Click on `Assets` to show the files available in the release and then click on `PowerToysSetup.msi` to download the PowerToys installer. <br />
|
||||
PDB symbols for the release are available in a separate zip file `PDB symbols.zip`.
|
||||
### October Update
|
||||
|
||||
# Developer Guidance
|
||||
**Update** - We've shipped 0.12! This release includes bug fixes, addresses issues and implements many feature suggestions. This build and installer is also signed by Microsoft. Last, but not least, this release includes a new utility: Chris Davis has integrated [SmartRename tool](https://github.com/chrdavis/SmartRename) into PowerToys!
|
||||
|
||||
## Build Prerequisites
|
||||
* Windows 10 1803 (build 10.0.17134.0) or above to build and run PowerToys.
|
||||
* Visual Studio 2019 Community edition or higher, with the 'Desktop Development with C++' component and the Windows 10 SDK version 10.0.18362.0 or higher.
|
||||
|
||||
## Building the Code
|
||||
* Open `powertoys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
|
||||
* The PowerToys binaries will be in your repo under `x64\Release`.
|
||||
* If you want to copy the `PowerToys.exe` binary to a different location, you'll also need to copy the `modules` and the `svgs` folders.
|
||||
![SmartRename](https://github.com/microsoft/PowerToys/raw/master/src/modules/powerrename/images/PowerRenameDemo.gif)
|
||||
|
||||
## Prerequisites to Build the Installer
|
||||
* Install the [WiX Toolset Visual Studio 2019 Extension](https://marketplace.visualstudio.com/items?itemName=RobMensching.WiXToolset).
|
||||
* Install the [WiX Toolset build tools](https://wixtoolset.org/releases/).
|
||||
|
||||
## Building the .msi Installer
|
||||
* From the `installer` folder open `PowerToysSetup.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
|
||||
* The resulting `PowerToysSetup.msi` installer will be available in the `installer\PowerToysSetup\x64\Release\` folder.
|
||||
## Developer Guidance
|
||||
|
||||
## Debugging
|
||||
The following configuration issue only applies if the user is a member of the Administrators group.
|
||||
|
||||
Some PowerToys modules require being run with the highest permission level if the current user is a member of the Administrators group. The highest permission level is required to be able to perform some actions when an elevated application (e.g. Task Manager) is in the foreground or is the target of an action. Without elevated privileges some PowerToys modules will still work but with some limitations:
|
||||
- the `FancyZones` module will be not be able to move an elevated window to a zone.
|
||||
- the `Shortcut Guide` module will not appear if the foreground window belongs to an elevated application.
|
||||
|
||||
To run and debug PowerToys from Visual Studio when the user is a member of the Administrators group, Visual Studio has to be started with elevated privileges. If you want to avoid running Visual Studio with elevated privileges and don't mind the limitations described above, you can do the following: open the `runner` project properties and navigate to the `Linker -> Manifest File` settings, edit the `UAC Execution Level` property and change it from `highestAvailable (/level='highestAvailable')` to `asInvoker (/level='asInvoker')`, save the changes.
|
||||
|
||||
## How to create new PowerToys
|
||||
### Build Prerequisites
|
||||
|
||||
* Windows 10 1803 (build 10.0.17134.0) or above to build and run PowerToys.
|
||||
* Visual Studio 2019 Community edition or higher, with the 'Desktop Development with C++' component and the Windows 10 SDK version 10.0.18362.0 or higher.
|
||||
|
||||
### Building the Code
|
||||
|
||||
* Open `powertoys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
|
||||
* The PowerToys binaries will be in your repo under `x64\Release`.
|
||||
* If you want to copy the `PowerToys.exe` binary to a different location, you'll also need to copy the `modules` and the `svgs` folders.
|
||||
|
||||
### Prerequisites to Build the Installer
|
||||
|
||||
* Install the [WiX Toolset Visual Studio 2019 Extension](https://marketplace.visualstudio.com/items?itemName=RobMensching.WiXToolset).
|
||||
* Install the [WiX Toolset build tools](https://wixtoolset.org/releases/).
|
||||
|
||||
### Building the .msi Installer
|
||||
|
||||
* From the `installer` folder open `PowerToysSetup.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
|
||||
* The resulting `PowerToysSetup.msi` installer will be available in the `installer\PowerToysSetup\x64\Release\` folder.
|
||||
|
||||
### Debugging
|
||||
|
||||
The following configuration issue only applies if the user is a member of the Administrators group.
|
||||
|
||||
Some PowerToys modules require being run with the highest permission level if the current user is a member of the Administrators group. The highest permission level is required to be able to perform some actions when an elevated application (e.g. Task Manager) is in the foreground or is the target of an action. Without elevated privileges some PowerToys modules will still work but with some limitations:
|
||||
|
||||
* the `FancyZones` module will be not be able to move an elevated window to a zone.
|
||||
* the `Shortcut Guide` module will not appear if the foreground window belongs to an elevated application.
|
||||
|
||||
To run and debug PowerToys from Visual Studio when the user is a member of the Administrators group, Visual Studio has to be started with elevated privileges. If you want to avoid running Visual Studio with elevated privileges and don't mind the limitations described above, you can do the following: open the `runner` project properties and navigate to the `Linker -> Manifest File` settings, edit the `UAC Execution Level` property and change it from `highestAvailable (level='highestAvailable')` to `asInvoker (/level='asInvoker')`, save the changes.
|
||||
|
||||
### How to create new PowerToys
|
||||
|
||||
See the instructions on [how to install the PowerToys Module project template](tools/project_template). <br />
|
||||
Specifications for the [PowerToys settings API](doc/specs/PowerToys-settings.md).
|
||||
|
||||
## Coding Guidance
|
||||
### Coding Guidance
|
||||
|
||||
Please review these brief docs below relating to our coding standards etc.
|
||||
|
||||
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones\!)
|
||||
|
||||
This is a work in progress as we learn what we'll need to provide people in order to be effective contributors to our project.
|
||||
- [Coding Style](doc/coding/style.md)
|
||||
- [Code Organization](doc/coding/organization.md)
|
||||
|
||||
# Contributing
|
||||
* [Coding Style](doc/coding/style.md)
|
||||
* [Code Organization](doc/coding/organization.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions and we are excited to work with the power user community to build a set of tools for helping you get the most out of Windows.
|
||||
|
||||
We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](contributing.md). We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
|
||||
@ -126,11 +137,14 @@ When you submit a pull request, a CLA-bot will automatically determine whether y
|
||||
a CLA and decorate the PR appropriately (e.g. status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
# Code of Conduct
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. <br />
|
||||
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
|
||||
|
||||
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
|
||||
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
|
||||
[conduct-email]: mailto:opencode@microsoft.com
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has the trends from the telemetry. Please read the [Microsoft privacy statement](http://go.microsoft.com/fwlink/?LinkId=521839) for more information.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 273 KiB |
@ -6,7 +6,6 @@ The list below is the set of utilities we're considering and the rough priority
|
||||
|
||||
* Maximize to new desktop widget - The MTND widget shows a pop-up button when a user hovers over the maximize / restore button on any window. Clicking it creates a new desktop, sends the app to that desktop and maximizes the app on the new desktop.
|
||||
* [Process terminate tool](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/Terminate%20Spec.md)
|
||||
* [Batch file renamer](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/File%20Classification%20Spec.md)
|
||||
* [Animated gif screen recorder](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/GIF%20Maker%20Spec.md)
|
||||
|
||||
## Backlog
|
||||
|
@ -7,7 +7,7 @@
|
||||
<Product Id="*"
|
||||
Name="PowerToys"
|
||||
Language="1033"
|
||||
Version="0.12.0"
|
||||
Version="0.13.0"
|
||||
Manufacturer="Microsoft"
|
||||
UpgradeCode="42B84BF7-5FBF-473B-9C8B-049DC16F7708">
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<Upgrade Id="42B84BF7-5FBF-473B-9C8B-049DC16F7708">
|
||||
<UpgradeVersion
|
||||
Minimum="0.11.0" Maximum="0.12.0"
|
||||
Minimum="0.11.0" Maximum="0.13.0"
|
||||
Property="PREVIOUSVERSIONSINSTALLED"
|
||||
IncludeMinimum="yes" IncludeMaximum="yes" />
|
||||
</Upgrade>
|
||||
|
@ -367,7 +367,7 @@ UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Install::Success",
|
||||
"Install_Success",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -386,7 +386,7 @@ UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Install::Cancel",
|
||||
"Install_Cancel",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -405,7 +405,7 @@ UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Install::Fail",
|
||||
"Install_Fail",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -424,7 +424,7 @@ UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Uninstall::Success",
|
||||
"UnInstall_Success",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -443,7 +443,7 @@ UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Uninstall::Cancel",
|
||||
"UnInstall_Cancel",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -462,7 +462,7 @@ UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Uninstall::Fail",
|
||||
"UnInstall_Fail",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -481,7 +481,7 @@ UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Repair::Cancel",
|
||||
"Repair_Cancel",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
@ -500,7 +500,7 @@ UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall) {
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"Repair::Fail",
|
||||
"Repair_Fail",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
|
@ -98,6 +98,7 @@
|
||||
<ClInclude Include="d2d_window.h" />
|
||||
<ClInclude Include="dpi_aware.h" />
|
||||
<ClInclude Include="monitors.h" />
|
||||
<ClInclude Include="on_thread_executor.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="settings_helpers.h" />
|
||||
<ClInclude Include="settings_objects.h" />
|
||||
@ -117,6 +118,7 @@
|
||||
<ClCompile Include="d2d_window.cpp" />
|
||||
<ClCompile Include="dpi_aware.cpp" />
|
||||
<ClCompile Include="monitors.cpp" />
|
||||
<ClCompile Include="on_thread_executor.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
|
@ -69,6 +69,9 @@
|
||||
<ClInclude Include="version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="on_thread_executor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="d2d_svg.cpp">
|
||||
@ -108,5 +111,8 @@
|
||||
<ClCompile Include="dpi_aware.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="on_thread_executor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -37,4 +37,8 @@ void DPIAware::Convert(HMONITOR monitor_handle, int &width, int &height) {
|
||||
width = width * dpi_x / DEFAULT_DPI;
|
||||
height = height * dpi_y / DEFAULT_DPI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DPIAware::EnableDPIAwarenessForThisProcess() {
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
#include "windef.h"
|
||||
|
||||
class DPIAware {
|
||||
private:
|
||||
static const int DEFAULT_DPI = 96;
|
||||
struct DPIAware {
|
||||
static constexpr int DEFAULT_DPI = 96;
|
||||
|
||||
public:
|
||||
static HRESULT GetScreenDPIForWindow(HWND hwnd, UINT & dpi_x, UINT & dpi_y);
|
||||
static HRESULT GetScreenDPIForPoint(POINT p, UINT& dpi_x, UINT& dpi_y);
|
||||
static void Convert(HMONITOR monitor_handle, int &width, int &height);
|
||||
static void EnableDPIAwarenessForThisProcess();
|
||||
};
|
||||
|
38
src/common/on_thread_executor.cpp
Normal file
38
src/common/on_thread_executor.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "on_thread_executor.h"
|
||||
|
||||
OnThreadExecutor::OnThreadExecutor()
|
||||
:_worker_thread{[this]() { worker_thread(); }}
|
||||
{}
|
||||
|
||||
std::future<void> OnThreadExecutor::submit(task_t task) {
|
||||
auto future = task.get_future();
|
||||
std::lock_guard lock{_task_mutex};
|
||||
_task_queue.emplace(std::move(task));
|
||||
_task_cv.notify_one();
|
||||
return future;
|
||||
}
|
||||
|
||||
void OnThreadExecutor::worker_thread() {
|
||||
while(_active) {
|
||||
task_t task;
|
||||
{
|
||||
std::unique_lock task_lock{_task_mutex};
|
||||
_task_cv.wait(task_lock, [this] { return !_task_queue.empty() || !_active; });
|
||||
if(!_active) {
|
||||
break;
|
||||
}
|
||||
task = std::move(_task_queue.front());
|
||||
_task_queue.pop();
|
||||
}
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
OnThreadExecutor::~OnThreadExecutor() {
|
||||
_active = false;
|
||||
_task_cv.notify_one();
|
||||
_worker_thread.join();
|
||||
}
|
||||
|
30
src/common/on_thread_executor.h
Normal file
30
src/common/on_thread_executor.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
|
||||
// OnThreadExecutor allows its caller to off-load some work to a persistently running background thread.
|
||||
// This might come in handy if you use the API which sets thread-wide global state and the state needs
|
||||
// to be isolated.
|
||||
|
||||
class OnThreadExecutor final {
|
||||
public:
|
||||
using task_t = std::packaged_task<void()>;
|
||||
|
||||
OnThreadExecutor();
|
||||
~OnThreadExecutor();
|
||||
std::future<void> submit(task_t task);
|
||||
|
||||
private:
|
||||
void worker_thread();
|
||||
|
||||
std::thread _worker_thread;
|
||||
|
||||
std::mutex _task_mutex;
|
||||
std::condition_variable _task_cv;
|
||||
std::atomic_bool _active;
|
||||
std::queue<std::packaged_task<void()>> _task_queue;
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
#define STRINGIZE(s) STRINGIZE2(s)
|
||||
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 12
|
||||
#define VERSION_MINOR 13
|
||||
#define VERSION_REVISION 0
|
||||
#define VERSION_BUILD 0
|
||||
|
||||
|
@ -8,7 +8,7 @@ An example PowerToy, that demonstrates how to create new ones.
|
||||
#### [`fancyzones`](./fancyzones)
|
||||
The FancyZones PowerToy that allows users to create custom zones on the screen, to which the windows will snap when moved.
|
||||
|
||||
#### [`FancyZonesEditor`](./FancyZonesEditor)
|
||||
#### [`FancyZonesEditor`](./fancyzones/editor/FancyZonesEditor)
|
||||
Editor for the snap-zones of the FancyZones PowerToy.
|
||||
|
||||
#### [`interface`](./interface)
|
||||
@ -17,5 +17,5 @@ Definition of the interface used by the [`runner`](/src/runner) to manage the Po
|
||||
#### [`powerrename`](./powerrename)
|
||||
PowerRename is a Windows Shell Context Menu Extension for advanced bulk renaming using simple search and replace or more powerful regular expression matching.
|
||||
|
||||
#### [`shortcut_guide`](./shorcut_guide)
|
||||
#### [`shortcut_guide`](./shortcut_guide)
|
||||
The Windows Shortcut Guide, displayed when the WinKey is held for some time.
|
||||
|
@ -220,6 +220,7 @@ private:
|
||||
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
|
||||
void MoveSizeUpdate(POINT const& ptScreen) noexcept;
|
||||
|
||||
HANDLE m_movedWindow = nullptr;
|
||||
winrt::com_ptr<IFancyZones> m_app;
|
||||
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
||||
};
|
||||
@ -298,6 +299,7 @@ void FancyZonesModule::MoveSizeStart(HWND window, POINT const& ptScreen) noexcep
|
||||
{
|
||||
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
|
||||
{
|
||||
m_movedWindow = window;
|
||||
m_app.as<IFancyZonesCallback>()->MoveSizeStart(window, monitor, ptScreen);
|
||||
}
|
||||
}
|
||||
@ -305,8 +307,9 @@ void FancyZonesModule::MoveSizeStart(HWND window, POINT const& ptScreen) noexcep
|
||||
|
||||
void FancyZonesModule::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
|
||||
{
|
||||
if (IsInterestingWindow(window))
|
||||
if (IsInterestingWindow(window) || (window != nullptr && window == m_movedWindow))
|
||||
{
|
||||
m_movedWindow = nullptr;
|
||||
m_app.as<IFancyZonesCallback>()->MoveSizeEnd(window, ptScreen);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ namespace FancyZonesEditor
|
||||
/// </summary>
|
||||
public partial class MainWindow : MetroWindow
|
||||
{
|
||||
// TODO: share the constants b/w C# Editor and FancyZoneLib
|
||||
public static int MAX_ZONES = 40;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
@ -57,7 +60,7 @@ namespace FancyZonesEditor
|
||||
|
||||
private void IncrementZones_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_settings.ZoneCount < 40)
|
||||
if (_settings.ZoneCount < MAX_ZONES)
|
||||
{
|
||||
_settings.ZoneCount++;
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ namespace FancyZonesEditor
|
||||
// 1 = unique key for per-monitor settings
|
||||
// 2 = layoutid used to generate current layout (used to pick the default layout to show)
|
||||
// 3 = handle to monitor (passed back to engine to persist data)
|
||||
// 4 = X_Y_Width_Height (where EditorOverlay shows up)
|
||||
// 4 = X_Y_Width_Height in a dpi-scaled-but-unaware coords (where EditorOverlay shows up)
|
||||
// 5 = resolution key (passed back to engine to persist data)
|
||||
// 6 = monitor DPI (float)
|
||||
|
||||
|
@ -51,5 +51,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.12.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.12.0.0")]
|
||||
[assembly: AssemblyVersion("0.13.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.13.0.0")]
|
||||
|
@ -1,13 +1,14 @@
|
||||
#include "pch.h"
|
||||
#include "common/dpi_aware.h"
|
||||
#include "common/on_thread_executor.h"
|
||||
|
||||
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
||||
{
|
||||
public:
|
||||
FancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept
|
||||
: m_hinstance(hinstance)
|
||||
, m_settings(settings)
|
||||
{
|
||||
m_settings.attach(settings);
|
||||
m_settings->SetCallback(this);
|
||||
}
|
||||
|
||||
@ -84,10 +85,12 @@ private:
|
||||
bool m_dragEnabled{}; // True if we should be showing zone hints while dragging
|
||||
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
|
||||
winrt::com_ptr<IZoneWindow> m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors.
|
||||
winrt::com_ptr<IFancyZonesSettings> m_settings;
|
||||
IFancyZonesSettings* m_settings{};
|
||||
GUID m_currentVirtualDesktopId{};
|
||||
wil::unique_handle m_terminateEditorEvent;
|
||||
|
||||
OnThreadExecutor m_dpiUnawareThread;
|
||||
|
||||
static UINT WM_PRIV_VDCHANGED;
|
||||
static UINT WM_PRIV_EDITOR;
|
||||
|
||||
@ -120,13 +123,18 @@ IFACEMETHODIMP_(void) FancyZones::Run() noexcept
|
||||
|
||||
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
|
||||
VirtualDesktopChanged();
|
||||
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{[]{
|
||||
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
|
||||
SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
|
||||
}}).wait();
|
||||
}
|
||||
|
||||
// IFancyZones
|
||||
IFACEMETHODIMP_(void) FancyZones::Destroy() noexcept
|
||||
{
|
||||
std::unique_lock writeLock(m_lock);
|
||||
|
||||
m_zoneWindowMap.clear();
|
||||
BufferedPaintUnInit();
|
||||
if (m_window)
|
||||
{
|
||||
@ -239,22 +247,22 @@ void FancyZones::ToggleEditor() noexcept
|
||||
}
|
||||
|
||||
HMONITOR monitor{};
|
||||
UINT dpi_x = 96;
|
||||
UINT dpi_y = 96;
|
||||
HWND foregroundWindow{};
|
||||
|
||||
if (m_settings->GetSettings().use_cursorpos_editor_startupscreen)
|
||||
UINT dpi_x = DPIAware::DEFAULT_DPI;
|
||||
UINT dpi_y = DPIAware::DEFAULT_DPI;
|
||||
|
||||
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings().use_cursorpos_editor_startupscreen;
|
||||
POINT currentCursorPos{};
|
||||
if (use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
POINT currentCursorPos{};
|
||||
GetCursorPos(¤tCursorPos);
|
||||
|
||||
monitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
|
||||
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
const HWND foregroundWindow = GetForegroundWindow();
|
||||
foregroundWindow = GetForegroundWindow();
|
||||
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY);
|
||||
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
|
||||
}
|
||||
|
||||
|
||||
@ -272,21 +280,33 @@ void FancyZones::ToggleEditor() noexcept
|
||||
|
||||
MONITORINFOEX mi;
|
||||
mi.cbSize = sizeof(mi);
|
||||
GetMonitorInfo(monitor, &mi);
|
||||
|
||||
// X/Y need to start in unscaled screen coordinates to get to the proper top/left of the monitor
|
||||
// From there, we need to scale the difference between the monitor and workarea rects to get the
|
||||
// appropriate offset where the overlay should appear.
|
||||
// This covers the cases where the taskbar is not at the bottom of the screen.
|
||||
const auto x = mi.rcMonitor.left + MulDiv(mi.rcWork.left - mi.rcMonitor.left, 96, dpi_x);
|
||||
const auto y = mi.rcMonitor.top + MulDiv(mi.rcWork.top - mi.rcMonitor.top, 96, dpi_y);
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{[&]{
|
||||
GetMonitorInfo(monitor, &mi);
|
||||
}}).wait();
|
||||
|
||||
// Location that the editor should occupy, scaled by DPI
|
||||
std::wstring editorLocation =
|
||||
if(use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
|
||||
}
|
||||
|
||||
const auto taskbar_x_offset = MulDiv(mi.rcWork.left - mi.rcMonitor.left, DPIAware::DEFAULT_DPI, dpi_x);
|
||||
const auto taskbar_y_offset = MulDiv(mi.rcWork.top - mi.rcMonitor.top, DPIAware::DEFAULT_DPI, dpi_y);
|
||||
|
||||
// Do not scale window params by the dpi, that will be done in the editor - see LayoutModel.Apply
|
||||
const auto x = mi.rcMonitor.left + taskbar_x_offset;
|
||||
const auto y = mi.rcMonitor.top + taskbar_y_offset;
|
||||
const auto width = mi.rcWork.right - mi.rcWork.left;
|
||||
const auto height = mi.rcWork.bottom - mi.rcWork.top;
|
||||
const std::wstring editorLocation =
|
||||
std::to_wstring(x) + L"_" +
|
||||
std::to_wstring(y) + L"_" +
|
||||
std::to_wstring(MulDiv(mi.rcWork.right - mi.rcWork.left, 96, dpi_x)) + L"_" +
|
||||
std::to_wstring(MulDiv(mi.rcWork.bottom - mi.rcWork.top, 96, dpi_y));
|
||||
std::to_wstring(width) + L"_" +
|
||||
std::to_wstring(height);
|
||||
|
||||
const std::wstring params =
|
||||
iter->second->UniqueId() + L" " +
|
||||
@ -294,7 +314,7 @@ void FancyZones::ToggleEditor() noexcept
|
||||
std::to_wstring(reinterpret_cast<UINT_PTR>(monitor)) + L" " +
|
||||
editorLocation + L" " +
|
||||
iter->second->WorkAreaKey() + L" " +
|
||||
std::to_wstring(static_cast<float>(dpi_x) / 96.0f);
|
||||
std::to_wstring(static_cast<float>(dpi_x) / DPIAware::DEFAULT_DPI);
|
||||
|
||||
SHELLEXECUTEINFO sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
|
||||
@ -652,34 +672,40 @@ void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT cons
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
|
||||
windowRect.top += 6;
|
||||
windowRect.left += 8;
|
||||
windowRect.right -= 8;
|
||||
windowRect.bottom -= 6;
|
||||
const auto padding_x = 8;
|
||||
const auto padding_y = 6;
|
||||
windowRect.top += padding_y;
|
||||
windowRect.left += padding_x;
|
||||
windowRect.right -= padding_x;
|
||||
windowRect.bottom -= padding_y;
|
||||
|
||||
if (PtInRect(&windowRect, ptScreen))
|
||||
if (PtInRect(&windowRect, ptScreen) == FALSE)
|
||||
{
|
||||
m_inMoveSize = true;
|
||||
return;
|
||||
}
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter != m_zoneWindowMap.end())
|
||||
{
|
||||
m_windowMoveSize = window;
|
||||
m_inMoveSize = true;
|
||||
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down.
|
||||
UpdateDragState(writeLock);
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
if (iter == end(m_zoneWindowMap))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dragEnabled)
|
||||
{
|
||||
m_zoneWindowMoveSize = iter->second;
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled);
|
||||
}
|
||||
else if (m_zoneWindowMoveSize)
|
||||
{
|
||||
m_zoneWindowMoveSize->MoveSizeCancel();
|
||||
m_zoneWindowMoveSize = nullptr;
|
||||
}
|
||||
}
|
||||
m_windowMoveSize = window;
|
||||
|
||||
// This updates m_dragEnabled depending on if the shift key is being held down.
|
||||
UpdateDragState(writeLock);
|
||||
|
||||
if (m_dragEnabled)
|
||||
{
|
||||
m_zoneWindowMoveSize = iter->second;
|
||||
m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled);
|
||||
}
|
||||
else if (m_zoneWindowMoveSize)
|
||||
{
|
||||
m_zoneWindowMoveSize->MoveSizeCancel();
|
||||
m_zoneWindowMoveSize = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ public:
|
||||
LoadSettings(name, true /*fromFile*/);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback.attach(callback); }
|
||||
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback = callback; }
|
||||
IFACEMETHODIMP_(bool) GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_sizeg) noexcept;
|
||||
IFACEMETHODIMP_(void) SetConfig(PCWSTR config) noexcept;
|
||||
IFACEMETHODIMP_(void) CallCustomAction(PCWSTR action) noexcept;
|
||||
@ -21,7 +21,7 @@ private:
|
||||
void LoadSettings(PCWSTR config, bool fromFile) noexcept;
|
||||
void SaveSettings() noexcept;
|
||||
|
||||
winrt::com_ptr<IFancyZonesCallback> m_callback;
|
||||
IFancyZonesCallback* m_callback{};
|
||||
const HINSTANCE m_hinstance;
|
||||
PCWSTR m_name{};
|
||||
|
||||
|
@ -34,13 +34,15 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
#define VERSION_PERSISTEDDATA 0x0000F00D
|
||||
struct ZoneSetPersistedData
|
||||
{
|
||||
static constexpr inline size_t MAX_ZONES = 40;
|
||||
|
||||
DWORD Version{VERSION_PERSISTEDDATA};
|
||||
WORD LayoutId{};
|
||||
DWORD ZoneCount{};
|
||||
ZoneSetLayout Layout{};
|
||||
DWORD PaddingInner{};
|
||||
DWORD PaddingOuter{};
|
||||
RECT Zones[40]{};
|
||||
RECT Zones[MAX_ZONES]{};
|
||||
};
|
||||
|
||||
struct ZoneSetConfig
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include <common/dpi_aware.h>
|
||||
|
||||
#include <ShellScalingApi.h>
|
||||
|
||||
struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
|
||||
@ -116,78 +118,79 @@ ZoneWindow::ZoneWindow(
|
||||
|
||||
MONITORINFO mi{};
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (GetMonitorInfoW(m_monitor, &mi))
|
||||
if (!GetMonitorInfoW(m_monitor, &mi))
|
||||
{
|
||||
const UINT dpi = GetDpiForMonitor();
|
||||
const Rect monitorRect(mi.rcMonitor);
|
||||
const Rect workAreaRect(mi.rcWork, dpi);
|
||||
return;
|
||||
}
|
||||
const UINT dpi = GetDpiForMonitor();
|
||||
const Rect monitorRect(mi.rcMonitor);
|
||||
const Rect workAreaRect(mi.rcWork, dpi);
|
||||
|
||||
StringCchPrintf(m_workArea, ARRAYSIZE(m_workArea), L"%d_%d", monitorRect.width(), monitorRect.height());
|
||||
StringCchPrintf(m_workArea, ARRAYSIZE(m_workArea), L"%d_%d", monitorRect.width(), monitorRect.height());
|
||||
|
||||
InitializeId(deviceId, virtualDesktopId);
|
||||
LoadSettings();
|
||||
InitializeZoneSets();
|
||||
InitializeId(deviceId, virtualDesktopId);
|
||||
LoadSettings();
|
||||
InitializeZoneSets();
|
||||
|
||||
WNDCLASSEXW wcex{};
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.lpfnWndProc = s_WndProc;
|
||||
wcex.hInstance = hinstance;
|
||||
wcex.lpszClassName = L"SuperFancyZones_ZoneWindow";
|
||||
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
RegisterClassExW(&wcex);
|
||||
WNDCLASSEXW wcex{};
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.lpfnWndProc = s_WndProc;
|
||||
wcex.hInstance = hinstance;
|
||||
wcex.lpszClassName = L"SuperFancyZones_ZoneWindow";
|
||||
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
RegisterClassExW(&wcex);
|
||||
|
||||
m_window = wil::unique_hwnd {
|
||||
CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP,
|
||||
m_window = wil::unique_hwnd {
|
||||
CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP,
|
||||
workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(),
|
||||
nullptr, nullptr, hinstance, this)
|
||||
};
|
||||
};
|
||||
|
||||
if (m_window)
|
||||
if (m_window)
|
||||
{
|
||||
MakeWindowTransparent(m_window.get());
|
||||
UpdateGrid(0, 0);
|
||||
if (flashZones)
|
||||
{
|
||||
MakeWindowTransparent(m_window.get());
|
||||
UpdateGrid(0, 0);
|
||||
if (flashZones)
|
||||
{
|
||||
FlashZones();
|
||||
}
|
||||
FlashZones();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ZoneWindow::ShowZoneWindow(bool activate, bool fadeIn) noexcept
|
||||
{
|
||||
if (m_window)
|
||||
if (!m_window)
|
||||
{
|
||||
m_flashMode = false;
|
||||
|
||||
UINT flags = SWP_NOSIZE | SWP_NOMOVE;
|
||||
if (!activate)
|
||||
{
|
||||
WI_SetFlag(flags, SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
if (!fadeIn)
|
||||
{
|
||||
WI_SetFlag(flags, SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
HWND windowInsertAfter = m_windowMoveSize;
|
||||
if (windowInsertAfter == nullptr)
|
||||
{
|
||||
windowInsertAfter = HWND_TOPMOST;
|
||||
}
|
||||
|
||||
SetWindowPos(m_window.get(), windowInsertAfter, 0, 0, 0, 0, flags);
|
||||
|
||||
if (fadeIn)
|
||||
{
|
||||
AnimateWindow(m_window.get(), m_showAnimationDuration, AW_BLEND);
|
||||
InvalidateRect(m_window.get(), nullptr, true);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
return E_FAIL;
|
||||
}
|
||||
return E_FAIL;
|
||||
|
||||
m_flashMode = false;
|
||||
|
||||
UINT flags = SWP_NOSIZE | SWP_NOMOVE;
|
||||
if (!activate)
|
||||
{
|
||||
WI_SetFlag(flags, SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
if (!fadeIn)
|
||||
{
|
||||
WI_SetFlag(flags, SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
HWND windowInsertAfter = m_windowMoveSize;
|
||||
if (windowInsertAfter == nullptr)
|
||||
{
|
||||
windowInsertAfter = HWND_TOPMOST;
|
||||
}
|
||||
|
||||
SetWindowPos(m_window.get(), windowInsertAfter, 0, 0, 0, 0, flags);
|
||||
|
||||
if (fadeIn)
|
||||
{
|
||||
AnimateWindow(m_window.get(), m_showAnimationDuration, AW_BLEND);
|
||||
InvalidateRect(m_window.get(), nullptr, true);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ZoneWindow::HideZoneWindow() noexcept
|
||||
@ -356,54 +359,56 @@ void ZoneWindow::InitializeZoneSets() noexcept
|
||||
|
||||
void ZoneWindow::LoadZoneSetsFromRegistry() noexcept
|
||||
{
|
||||
if (wil::unique_hkey key{ RegistryHelpers::OpenKey(m_workArea) })
|
||||
wil::unique_hkey key{RegistryHelpers::OpenKey(m_workArea)};
|
||||
if (!key)
|
||||
{
|
||||
ZoneSetPersistedData data{};
|
||||
DWORD dataSize = sizeof(data);
|
||||
wchar_t value[256]{};
|
||||
DWORD valueLength = ARRAYSIZE(value);
|
||||
DWORD i = 0;
|
||||
while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
||||
return;
|
||||
}
|
||||
ZoneSetPersistedData data{};
|
||||
DWORD dataSize = sizeof(data);
|
||||
wchar_t value[256]{};
|
||||
DWORD valueLength = ARRAYSIZE(value);
|
||||
DWORD i = 0;
|
||||
while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
||||
{
|
||||
if (data.Version == VERSION_PERSISTEDDATA)
|
||||
{
|
||||
if (data.Version == VERSION_PERSISTEDDATA)
|
||||
GUID zoneSetId;
|
||||
if (SUCCEEDED_LOG(CLSIDFromString(value, &zoneSetId)))
|
||||
{
|
||||
GUID zoneSetId;
|
||||
if (SUCCEEDED_LOG(CLSIDFromString(value, &zoneSetId)))
|
||||
auto zoneSet = MakeZoneSet(ZoneSetConfig(
|
||||
zoneSetId,
|
||||
data.LayoutId,
|
||||
m_monitor,
|
||||
m_workArea,
|
||||
data.Layout,
|
||||
0,
|
||||
static_cast<int>(data.PaddingInner),
|
||||
static_cast<int>(data.PaddingOuter)));
|
||||
|
||||
if (zoneSet)
|
||||
{
|
||||
auto zoneSet = MakeZoneSet(ZoneSetConfig(
|
||||
zoneSetId,
|
||||
data.LayoutId,
|
||||
m_monitor,
|
||||
m_workArea,
|
||||
data.Layout,
|
||||
0,
|
||||
static_cast<int>(data.PaddingInner),
|
||||
static_cast<int>(data.PaddingOuter)));
|
||||
|
||||
if (zoneSet)
|
||||
for (UINT j = 0; j < data.ZoneCount; j++)
|
||||
{
|
||||
for (UINT j = 0; j < data.ZoneCount; j++)
|
||||
{
|
||||
zoneSet->AddZone(MakeZone(data.Zones[j]), false);
|
||||
}
|
||||
|
||||
m_zoneSets.emplace_back(zoneSet);
|
||||
|
||||
if (zoneSetId == m_activeZoneSetId)
|
||||
{
|
||||
UpdateActiveZoneSet(zoneSet.get());
|
||||
}
|
||||
zoneSet->AddZone(MakeZone(data.Zones[j]), false);
|
||||
}
|
||||
|
||||
if (zoneSetId == m_activeZoneSetId)
|
||||
{
|
||||
UpdateActiveZoneSet(zoneSet.get());
|
||||
}
|
||||
|
||||
m_zoneSets.emplace_back(std::move(zoneSet));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Migrate from older settings format
|
||||
}
|
||||
|
||||
valueLength = ARRAYSIZE(value);
|
||||
dataSize = sizeof(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Migrate from older settings format
|
||||
}
|
||||
|
||||
valueLength = ARRAYSIZE(value);
|
||||
dataSize = sizeof(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,37 +721,38 @@ void ZoneWindow::DrawZone(wil::unique_hdc& hdc, ColorSetting const& colorSetting
|
||||
}
|
||||
FillRectARGB(hdc, &zoneRect, colorSetting.fillAlpha, colorSetting.fill, false);
|
||||
|
||||
if (!m_flashMode)
|
||||
if (m_flashMode)
|
||||
{
|
||||
COLORREF const colorFill = RGB(255, 255, 255);
|
||||
|
||||
size_t const index = zone->Id();
|
||||
int const padding = 5;
|
||||
int const size = 10;
|
||||
POINT offset = { zoneRect.left + padding, zoneRect.top + padding };
|
||||
if (!IsOccluded(offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, false, false, colorFill); // top left
|
||||
return;
|
||||
}
|
||||
|
||||
offset.x = zoneRect.right - ((padding + size) * 3);
|
||||
if (!IsOccluded(offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, true, false, colorFill); // top right
|
||||
return;
|
||||
}
|
||||
|
||||
offset.y = zoneRect.bottom - ((padding + size) * 3);
|
||||
if (!IsOccluded(offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, true, true, colorFill); // bottom right
|
||||
return;
|
||||
}
|
||||
|
||||
offset.x = zoneRect.left + padding;
|
||||
DrawIndex(hdc, offset, index, padding, size, false, true, colorFill); // bottom left
|
||||
return;
|
||||
}
|
||||
COLORREF const colorFill = RGB(255, 255, 255);
|
||||
|
||||
size_t const index = zone->Id();
|
||||
int const padding = 5;
|
||||
int const size = 10;
|
||||
POINT offset = { zoneRect.left + padding, zoneRect.top + padding };
|
||||
if (!IsOccluded(offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, false, false, colorFill); // top left
|
||||
return;
|
||||
}
|
||||
|
||||
offset.x = zoneRect.right - ((padding + size) * 3);
|
||||
if (!IsOccluded(offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, true, false, colorFill); // top right
|
||||
return;
|
||||
}
|
||||
|
||||
offset.y = zoneRect.bottom - ((padding + size) * 3);
|
||||
if (!IsOccluded(offset, index))
|
||||
{
|
||||
DrawIndex(hdc, offset, index, padding, size, true, true, colorFill); // bottom right
|
||||
return;
|
||||
}
|
||||
|
||||
offset.x = zoneRect.left + padding;
|
||||
DrawIndex(hdc, offset, index, padding, size, false, true, colorFill); // bottom left
|
||||
}
|
||||
|
||||
void ZoneWindow::DrawIndex(wil::unique_hdc& hdc, POINT offset, size_t index, int padding, int size, bool flipX, bool flipY, COLORREF colorFill)
|
||||
@ -817,33 +823,36 @@ void ZoneWindow::DrawActiveZoneSet(wil::unique_hdc& hdc, RECT const& clientRect)
|
||||
ColorSetting const colorFlash { 200, RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
|
||||
|
||||
auto zones = m_activeZoneSet->GetZones();
|
||||
size_t colorIndex = zones.size() - 1;
|
||||
const size_t maxColorIndex = min(size(zones) - 1, size(colors) - 1);
|
||||
size_t colorIndex = maxColorIndex;
|
||||
for (auto iter = zones.rbegin(); iter != zones.rend(); iter++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
|
||||
if (!zone)
|
||||
{
|
||||
if (zone != m_highlightZone)
|
||||
{
|
||||
if (m_flashMode)
|
||||
{
|
||||
DrawZone(hdc, colorFlash, zone);
|
||||
}
|
||||
else if (m_drawHints)
|
||||
{
|
||||
DrawZone(hdc, colorHints, zone);
|
||||
}
|
||||
else if (m_editorMode)
|
||||
{
|
||||
DrawZone(hdc, colorEditorMode, zone);
|
||||
}
|
||||
else
|
||||
{
|
||||
colorViewer.fill = colors[colorIndex];
|
||||
DrawZone(hdc, colorViewer, zone);
|
||||
}
|
||||
}
|
||||
colorIndex--;
|
||||
continue;
|
||||
}
|
||||
if (zone != m_highlightZone)
|
||||
{
|
||||
if (m_flashMode)
|
||||
{
|
||||
DrawZone(hdc, colorFlash, zone);
|
||||
}
|
||||
else if (m_drawHints)
|
||||
{
|
||||
DrawZone(hdc, colorHints, zone);
|
||||
}
|
||||
else if (m_editorMode)
|
||||
{
|
||||
DrawZone(hdc, colorEditorMode, zone);
|
||||
}
|
||||
else
|
||||
{
|
||||
colorViewer.fill = colors[colorIndex];
|
||||
DrawZone(hdc, colorViewer, zone);
|
||||
}
|
||||
}
|
||||
colorIndex = colorIndex != 0 ? colorIndex - 1 : maxColorIndex;
|
||||
}
|
||||
|
||||
if (m_highlightZone)
|
||||
@ -930,7 +939,7 @@ void ZoneWindow::OnPaint(wil::unique_hdc& hdc) noexcept
|
||||
RECT clientRect;
|
||||
GetClientRect(m_window.get(), &clientRect);
|
||||
|
||||
wil::unique_hdc hdcMem;;
|
||||
wil::unique_hdc hdcMem;
|
||||
HPAINTBUFFER bufferedPaint = BeginBufferedPaint(hdc.get(), &clientRect, BPBF_TOPDOWNDIB, nullptr, &hdcMem);
|
||||
if (bufferedPaint)
|
||||
{
|
||||
@ -1284,7 +1293,7 @@ UINT ZoneWindow::GetDpiForMonitor() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
return (dpi == 0) ? 96 : dpi;
|
||||
return (dpi == 0) ? DPIAware::DEFAULT_DPI : dpi;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
|
@ -18,7 +18,7 @@ The text to replace the instance(s) in the item name matched by the Search text
|
||||
## Options
|
||||
|
||||
### Use Regular Expressions
|
||||
If checked, the Search field will be interpreted as a regular expression. If not checked, the Search field will be used as a text to be replaced with the text in the Replace field.
|
||||
If checked, the Search field will be interpreted as a regular expression. The Replace field can also contain regex variables (see examples below). If not checked, the Search field will be used as a text to be replaced with the text in the Replace field.
|
||||
|
||||
### Case Sensitive
|
||||
If checked, the text specified in the Search field will only match text in the items if the text is the same case. By default we match case insensitive.
|
||||
@ -53,9 +53,13 @@ Ex: txt.txt -> txt.NewExtension
|
||||
|
||||
For most use cases, a simple search and replace is sufficient. Other users will need more control over. That is where Regular Expressions come in. Regular Expressions define a search pattern for text. Regular expressions can be used to search, edit and manipulate text. The pattern defined by the regular expression may match one or several times or not at all for a given string. PowerRename uses the ECMAScript grammar, which is common amongst modern programming languages.
|
||||
|
||||
To enable regular expressions, check the "Use Regular Expressions" checkbox. Note: You will likely want to check "Match All Occurrences" while using regular expressions.
|
||||
|
||||
### Examples
|
||||
| RegEx | Description |
|
||||
|
||||
Simple matching examples:
|
||||
|
||||
| Search for | Description |
|
||||
| -------------- | ------------- |
|
||||
| .* | Match all the text in the name |
|
||||
| ^foo | Match text that begins with "foo" |
|
||||
@ -64,7 +68,16 @@ For most use cases, a simple search and replace is sufficient. Other users will
|
||||
| .+?(?=bar) | Match everything up to "bar" |
|
||||
| foo[\s\S]\*bar | Match everything between "foo" and "bar" |
|
||||
|
||||
Note: you will likely want to check Match All Occurrences while using regular expressions
|
||||
|
||||
Matching and variable examples:
|
||||
|
||||
| Search for | Replace With | Description |
|
||||
| ---------- | ------------- |--------------------------------------------|
|
||||
| (.\*).png | foo\_$1.png | Prepends "foo\_" to the existing file name |
|
||||
| (.\*).png | $1\_foo.png | Appends "\_foo" to the existing file name |
|
||||
| (.\*) | $1.txt | Appends ".txt" extension to existing file name |
|
||||
| (^\w+\.$)\|(^\w+$) | $2.txt | Appends ".txt" extension to existing file name only if it does not have an extension |
|
||||
|
||||
|
||||
### External Help
|
||||
There are great examples/cheat sheets available online to help you
|
||||
|
@ -8,8 +8,11 @@
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
|
||||
HWND g_hwndParent = 0;
|
||||
|
||||
struct InvokeStruct
|
||||
{
|
||||
HWND hwndParent;
|
||||
IStream* pstrm;
|
||||
};
|
||||
|
||||
const wchar_t powerRenameRegPath[] = L"Software\\Microsoft\\PowerRename";
|
||||
const wchar_t powerRenameRegEnabledName[] = L"Enabled";
|
||||
@ -73,15 +76,12 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
|
||||
return E_FAIL;
|
||||
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
if (m_spdo)
|
||||
if (m_spdo && !(uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_OPTIMIZEFORINVOKE)))
|
||||
{
|
||||
if ((uFlags & ~CMF_OPTIMIZEFORINVOKE) && (uFlags & ~(CMF_DEFAULTONLY | CMF_VERBSONLY)))
|
||||
{
|
||||
wchar_t menuName[64] = { 0 };
|
||||
LoadString(g_hInst, IDS_POWERRENAME, menuName, ARRAYSIZE(menuName));
|
||||
InsertMenu(hMenu, index, MF_STRING | MF_BYPOSITION, uIDFirst++, menuName);
|
||||
hr = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
|
||||
}
|
||||
wchar_t menuName[64] = { 0 };
|
||||
LoadString(g_hInst, IDS_POWERRENAME, menuName, ARRAYSIZE(menuName));
|
||||
InsertMenu(hMenu, index, MF_STRING | MF_BYPOSITION, uIDFirst++, menuName);
|
||||
hr = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
|
||||
}
|
||||
|
||||
return hr;
|
||||
@ -91,19 +91,29 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
if (IsEnabled() &
|
||||
if (IsEnabled() &&
|
||||
(IS_INTRESOURCE(pici->lpVerb)) &&
|
||||
(LOWORD(pici->lpVerb) == 0))
|
||||
{
|
||||
Trace::Invoked();
|
||||
IStream* pstrm = nullptr;
|
||||
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &pstrm);
|
||||
InvokeStruct* pInvokeData = new InvokeStruct;
|
||||
hr = pInvokeData ? S_OK : E_OUTOFMEMORY;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
if (!SHCreateThread(s_PowerRenameUIThreadProc, pstrm, CTF_COINIT | CTF_PROCESS_REF, nullptr))
|
||||
pInvokeData->hwndParent = pici->hwnd;
|
||||
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &(pInvokeData->pstrm));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
pstrm->Release(); // if we failed to create the thread, then we must release the stream
|
||||
hr = E_FAIL;
|
||||
hr = SHCreateThread(s_PowerRenameUIThreadProc, pInvokeData, CTF_COINIT | CTF_PROCESS_REF, nullptr) ? S_OK : E_FAIL;
|
||||
if (FAILED(hr))
|
||||
{
|
||||
pInvokeData->pstrm->Release(); // if we failed to create the thread, then we must release the stream
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
delete pInvokeData;
|
||||
}
|
||||
}
|
||||
Trace::InvokedRet(hr);
|
||||
@ -114,9 +124,9 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
|
||||
|
||||
DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
|
||||
{
|
||||
IStream* pstrm = static_cast<IStream*>(pData);
|
||||
InvokeStruct* pInvokeData = static_cast<InvokeStruct*>(pData);
|
||||
CComPtr<IDataObject> spdo;
|
||||
HRESULT hr = CoGetInterfaceAndReleaseStream(pstrm, IID_PPV_ARGS(&spdo));
|
||||
HRESULT hr = CoGetInterfaceAndReleaseStream(pInvokeData->pstrm, IID_PPV_ARGS(&spdo));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Create the smart rename manager
|
||||
@ -139,7 +149,7 @@ DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Call blocks until we are done
|
||||
spsrui->Show();
|
||||
spsrui->Show(pInvokeData->hwndParent);
|
||||
spsrui->Close();
|
||||
}
|
||||
}
|
||||
@ -150,6 +160,8 @@ DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
|
||||
}
|
||||
}
|
||||
|
||||
delete pInvokeData;
|
||||
|
||||
Trace::UIShownRet(hr);
|
||||
|
||||
return 0;
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
⼀⼀ഀ<EFBFBD>
|
||||
⌀椀渀挀氀甀搀攀 ∀爀攀猀漀甀爀挀攀⸀栀∀ഀ<EFBFBD>
|
||||
|