Merge Master Latest: 4/15/20

This commit is contained in:
Udit Singh
2020-04-15 07:55:17 -07:00
58 changed files with 2618 additions and 1337 deletions

100
NOTICE.md
View File

@@ -18,11 +18,12 @@ Notwithstanding any other terms, you may reverse engineer this software to the
extent required to debug changes to any libraries licensed under the GNU Lesser extent required to debug changes to any libraries licensed under the GNU Lesser
General Public License. General Public License.
## ImageResizer ## PowerToy: ImageResizer
### Brice Lams's Image Resizer License
**Source**: https://github.com/bricelam/ImageResizer/ **Source**: https://github.com/bricelam/ImageResizer/
### License
The MIT License (MIT) The MIT License (MIT)
Copyright (c) Brice Lambson. All rights reserved. Copyright (c) Brice Lambson. All rights reserved.
@@ -44,3 +45,98 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
## PowerToy: Launcher
### Wox License
**Source**: https://github.com/Wox-launcher/Wox
The MIT License (MIT)
Copyright (c) 2015 Wox
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### Beta Tadele's Window Walker License
**Source**: https://github.com/betsegaw/windowwalker
The MIT License (MIT)
Copyright 2020 Betsegaw Tadele
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### Squirrel.Windows License
**Source**: https://github.com/Squirrel/Squirrel.Windows/
The MIT License (MIT)
Copyright (c) 2012 GitHub, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
## PowerToy: PowerRename
### Chris Davis's SmartRename License
**Source**: https://github.com/chrdavis/SmartRename
MIT License
Copyright (c) 2017 Chris Davis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -32,8 +32,8 @@
<![CDATA[(WINDOWSBUILDNUMBER >= 17134)]]> <![CDATA[(WINDOWSBUILDNUMBER >= 17134)]]>
</Condition> </Condition>
<Icon Id="powertoys.ico" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/> <Icon Id="powertoys.exe" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
<Property Id="ARPPRODUCTICON" Value="powertoys.ico" /> <Property Id="ARPPRODUCTICON" Value="powertoys.exe" />
<Feature Id="CoreFeature" Title="PowerToys" AllowAdvertise="no" Absent="disallow" TypicalDefault="install" <Feature Id="CoreFeature" Title="PowerToys" AllowAdvertise="no" Absent="disallow" TypicalDefault="install"
Description="Contains the Shortcut Guide and Fancy Zones features."> Description="Contains the Shortcut Guide and Fancy Zones features.">
<ComponentGroupRef Id="CoreComponents" /> <ComponentGroupRef Id="CoreComponents" />
@@ -228,7 +228,7 @@
Description="PowerToys - Windows system utilities to maximize productivity" Description="PowerToys - Windows system utilities to maximize productivity"
Directory="ApplicationProgramsFolder" Directory="ApplicationProgramsFolder"
WorkingDirectory="INSTALLFOLDER" WorkingDirectory="INSTALLFOLDER"
Icon="powertoys.ico" Icon="powertoys.exe"
IconIndex="0" IconIndex="0"
Advertise="yes"> Advertise="yes">
<ShortcutProperty Key="System.AppUserModel.ID" Value="Microsoft.PowerToysWin32"/> <ShortcutProperty Key="System.AppUserModel.ID" Value="Microsoft.PowerToysWin32"/>
@@ -500,7 +500,7 @@
Description="PowerToys - Windows system utilities to maximize productivity" Description="PowerToys - Windows system utilities to maximize productivity"
Target="[!PowerToys.exe]" Target="[!PowerToys.exe]"
WorkingDirectory="INSTALLFOLDER" WorkingDirectory="INSTALLFOLDER"
Icon="powertoys.ico" Icon="powertoys.exe"
Directory="DesktopFolder"/> Directory="DesktopFolder"/>
</Component> </Component>
</DirectoryRef> </DirectoryRef>

View File

@@ -30,7 +30,8 @@ const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
// The path of the executable to run should be passed as the CustomActionData (Value). // The path of the executable to run should be passed as the CustomActionData (Value).
// Based on the Task Scheduler Logon Trigger Example: // Based on the Task Scheduler Logon Trigger Example:
// https://docs.microsoft.com/en-us/windows/win32/taskschd/logon-trigger-example--c---/ // https://docs.microsoft.com/en-us/windows/win32/taskschd/logon-trigger-example--c---/
UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) { UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -39,13 +40,13 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
std::wstring wstrTaskName; std::wstring wstrTaskName;
ITaskService *pService = NULL; ITaskService* pService = NULL;
ITaskFolder *pTaskFolder = NULL; ITaskFolder* pTaskFolder = NULL;
ITaskDefinition *pTask = NULL; ITaskDefinition* pTask = NULL;
IRegistrationInfo *pRegInfo = NULL; IRegistrationInfo* pRegInfo = NULL;
ITaskSettings *pSettings = NULL; ITaskSettings* pSettings = NULL;
ITriggerCollection *pTriggerCollection = NULL; ITriggerCollection* pTriggerCollection = NULL;
IRegisteredTask *pRegisteredTask = NULL; IRegisteredTask* pRegisteredTask = NULL;
hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); hr = WcaInitialize(hInstall, "CreateScheduledTaskCA");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@@ -58,10 +59,12 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
// This action needs to run as the system to get elevated privileges from the installation, // This action needs to run as the system to get elevated privileges from the installation,
// so GetUserNameEx can't be used to get the current user details. // so GetUserNameEx can't be used to get the current user details.
// The USERNAME and USERDOMAIN environment variables are used instead. // The USERNAME and USERDOMAIN environment variables are used instead.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) { if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
{
ExitWithLastError(hr, "Getting username failed: %x", hr); ExitWithLastError(hr, "Getting username failed: %x", hr);
} }
if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN)) { if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN))
{
ExitWithLastError(hr, "Getting the user's domain failed: %x", hr); ExitWithLastError(hr, "Getting the user's domain failed: %x", hr);
} }
wcscat_s(username_domain, L"\\"); wcscat_s(username_domain, L"\\");
@@ -90,20 +93,21 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service. // Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(), hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------ // ------------------------------------------------------
// Get the PowerToys task folder. Creates it if it doesn't exist. // Get the PowerToys task folder. Creates it if it doesn't exist.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder); hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) { if (FAILED(hr))
{
// Folder doesn't exist. Get the Root folder and create the PowerToys subfolder. // Folder doesn't exist. Get the Root folder and create the PowerToys subfolder.
ITaskFolder *pRootFolder = NULL; ITaskFolder* pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder); hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr); ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder); hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder);
if (FAILED(hr)) { if (FAILED(hr))
{
pRootFolder->Release(); pRootFolder->Release();
ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr); ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr);
} }
@@ -144,25 +148,27 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
ExitOnFailure(hr, "Cannot get trigger collection: %x", hr); ExitOnFailure(hr, "Cannot get trigger collection: %x", hr);
// Add the logon trigger to the task. // Add the logon trigger to the task.
ITrigger *pTrigger = NULL; ITrigger* pTrigger = NULL;
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger); hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
ExitOnFailure(hr, "Cannot create the trigger: %x", hr); ExitOnFailure(hr, "Cannot create the trigger: %x", hr);
ILogonTrigger *pLogonTrigger = NULL; ILogonTrigger* pLogonTrigger = NULL;
hr = pTrigger->QueryInterface( hr = pTrigger->QueryInterface(
IID_ILogonTrigger, (void**)&pLogonTrigger); IID_ILogonTrigger, (void**)&pLogonTrigger);
pTrigger->Release(); pTrigger->Release();
ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr); ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr);
hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1")); hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1"));
if (FAILED(hr)) { if (FAILED(hr))
{
WcaLogError(hr, "Cannot put the trigger ID: %x", hr); WcaLogError(hr, "Cannot put the trigger ID: %x", hr);
} }
// Timing issues may make explorer not be started when the task runs. // Timing issues may make explorer not be started when the task runs.
// Add a little delay to mitigate this. // Add a little delay to mitigate this.
hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S")); hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S"));
if (FAILED(hr)) { if (FAILED(hr))
{
WcaLogError(hr, "Cannot put the trigger delay: %x", hr); WcaLogError(hr, "Cannot put the trigger delay: %x", hr);
} }
@@ -174,19 +180,19 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
// ------------------------------------------------------ // ------------------------------------------------------
// Add an Action to the task. This task will execute the path passed to this custom action. // Add an Action to the task. This task will execute the path passed to this custom action.
IActionCollection *pActionCollection = NULL; IActionCollection* pActionCollection = NULL;
// Get the task action collection pointer. // Get the task action collection pointer.
hr = pTask->get_Actions(&pActionCollection); hr = pTask->get_Actions(&pActionCollection);
ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr); ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr);
// Create the action, specifying that it is an executable action. // Create the action, specifying that it is an executable action.
IAction *pAction = NULL; IAction* pAction = NULL;
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction); hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release(); pActionCollection->Release();
ExitOnFailure(hr, "Cannot create the action: %x", hr); ExitOnFailure(hr, "Cannot create the action: %x", hr);
IExecAction *pExecAction = NULL; IExecAction* pExecAction = NULL;
// QI for the executable task pointer. // QI for the executable task pointer.
hr = pAction->QueryInterface( hr = pAction->QueryInterface(
IID_IExecAction, (void**)&pExecAction); IID_IExecAction, (void**)&pExecAction);
@@ -200,23 +206,26 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
// ------------------------------------------------------ // ------------------------------------------------------
// Create the principal for the task // Create the principal for the task
IPrincipal *pPrincipal = NULL; IPrincipal* pPrincipal = NULL;
hr = pTask->get_Principal(&pPrincipal); hr = pTask->get_Principal(&pPrincipal);
ExitOnFailure(hr, "Cannot get principal pointer: %x", hr); ExitOnFailure(hr, "Cannot get principal pointer: %x", hr);
// Set up principal information: // Set up principal information:
hr = pPrincipal->put_Id(_bstr_t(L"Principal1")); hr = pPrincipal->put_Id(_bstr_t(L"Principal1"));
if (FAILED(hr)) { if (FAILED(hr))
{
WcaLogError(hr, "Cannot put the principal ID: %x", hr); WcaLogError(hr, "Cannot put the principal ID: %x", hr);
} }
hr = pPrincipal->put_UserId(_bstr_t(username_domain)); hr = pPrincipal->put_UserId(_bstr_t(username_domain));
if (FAILED(hr)) { if (FAILED(hr))
{
WcaLogError(hr, "Cannot put principal user Id: %x", hr); WcaLogError(hr, "Cannot put principal user Id: %x", hr);
} }
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
if (FAILED(hr)) { if (FAILED(hr))
{
WcaLogError(hr, "Cannot put principal logon type: %x", hr); WcaLogError(hr, "Cannot put principal logon type: %x", hr);
} }
@@ -245,15 +254,37 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
LExit: LExit:
ReleaseStr(wszExecutablePath); ReleaseStr(wszExecutablePath);
if (pService) pService->Release(); if (pService)
if (pTaskFolder) pTaskFolder->Release(); {
if (pTask) pTask->Release(); pService->Release();
if (pRegInfo) pRegInfo->Release(); }
if (pSettings) pSettings->Release(); if (pTaskFolder)
if (pTriggerCollection) pTriggerCollection->Release(); {
if (pRegisteredTask) pRegisteredTask->Release(); pTaskFolder->Release();
}
if (pTask)
{
pTask->Release();
}
if (pRegInfo)
{
pRegInfo->Release();
}
if (pSettings)
{
pSettings->Release();
}
if (pTriggerCollection)
{
pTriggerCollection->Release();
}
if (pRegisteredTask)
{
pRegisteredTask->Release();
}
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0); PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to create a scheduled task to start PowerToys at user login. You can re-try to create the scheduled task using the PowerToys settings.")); MsiRecordSetString(hRecord, 0, TEXT("Failed to create a scheduled task to start PowerToys at user login. You can re-try to create the scheduled task using the PowerToys settings."));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord); MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
@@ -266,12 +297,13 @@ LExit:
// Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards. // Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards.
// Based on the Task Scheduler Displaying Task Names and State example: // Based on the Task Scheduler Displaying Task Names and State example:
// https://docs.microsoft.com/en-us/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/ // https://docs.microsoft.com/en-us/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/
UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) { UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
ITaskService *pService = NULL; ITaskService* pService = NULL;
ITaskFolder *pTaskFolder = NULL; ITaskFolder* pTaskFolder = NULL;
IRegisteredTaskCollection* pTaskCollection = NULL; IRegisteredTaskCollection* pTaskCollection = NULL;
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA"); hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
@@ -291,14 +323,14 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) {
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service. // Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(), hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------ // ------------------------------------------------------
// Get the PowerToys task folder. // Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder); hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) { if (FAILED(hr))
{
// Folder doesn't exist. No need to delete anything. // Folder doesn't exist. No need to delete anything.
WcaLog(LOGMSG_STANDARD, "The PowerToys scheduled task folder wasn't found. Nothing to delete."); WcaLog(LOGMSG_STANDARD, "The PowerToys scheduled task folder wasn't found. Nothing to delete.");
hr = S_OK; hr = S_OK;
@@ -312,32 +344,40 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) {
LONG numTasks = 0; LONG numTasks = 0;
hr = pTaskCollection->get_Count(&numTasks); hr = pTaskCollection->get_Count(&numTasks);
for (LONG i = 0; i < numTasks; i++) { for (LONG i = 0; i < numTasks; i++)
{
// Delete all the tasks found. // Delete all the tasks found.
// If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified. // If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
IRegisteredTask* pRegisteredTask = NULL; IRegisteredTask* pRegisteredTask = NULL;
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask); hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr))
{
BSTR taskName = NULL; BSTR taskName = NULL;
hr = pRegisteredTask->get_Name(&taskName); hr = pRegisteredTask->get_Name(&taskName);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr))
{
hr = pTaskFolder->DeleteTask(taskName, NULL); hr = pTaskFolder->DeleteTask(taskName, NULL);
if (FAILED(hr)) { if (FAILED(hr))
{
WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr); WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr);
} }
SysFreeString(taskName); SysFreeString(taskName);
} else { }
else
{
WcaLogError(hr, "Cannot get the registered task name: %x", hr); WcaLogError(hr, "Cannot get the registered task name: %x", hr);
} }
pRegisteredTask->Release(); pRegisteredTask->Release();
} else { }
else
{
WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr); WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr);
} }
} }
// ------------------------------------------------------ // ------------------------------------------------------
// Get the pointer to the root task folder and delete the PowerToys subfolder. // Get the pointer to the root task folder and delete the PowerToys subfolder.
ITaskFolder *pRootFolder = NULL; ITaskFolder* pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder); hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr); ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), NULL); hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), NULL);
@@ -347,11 +387,21 @@ UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) {
WcaLog(LOGMSG_STANDARD, "Deleted the PowerToys Task Scheduler folder."); WcaLog(LOGMSG_STANDARD, "Deleted the PowerToys Task Scheduler folder.");
LExit: LExit:
if (pService) pService->Release(); if (pService)
if (pTaskFolder) pTaskFolder->Release(); {
if (pTaskCollection) pTaskCollection->Release(); pService->Release();
}
if (pTaskFolder)
{
pTaskFolder->Release();
}
if (pTaskCollection)
{
pTaskCollection->Release();
}
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0); PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later.")); MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later."));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord); MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
@@ -361,7 +411,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -380,7 +431,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -399,7 +451,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -418,7 +471,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -437,7 +491,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -456,7 +511,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -475,7 +531,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -494,7 +551,8 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall) { UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
@@ -514,8 +572,10 @@ LExit:
} }
// DllMain - Initialize and cleanup WiX custom action utils. // DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID) { extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
switch (ulReason) { {
switch (ulReason)
{
case DLL_PROCESS_ATTACH: case DLL_PROCESS_ATTACH:
WcaGlobalInitialize(hInst); WcaGlobalInitialize(hInst);
TraceLoggingRegister(g_hProvider); TraceLoggingRegister(g_hProvider);

View File

@@ -364,8 +364,9 @@ WindowState get_window_state(HWND hwnd)
return RESTORED; return RESTORED;
} }
bool is_process_elevated() bool is_process_elevated(const bool use_cached_value)
{ {
auto detection_func = []() {
HANDLE token = nullptr; HANDLE token = nullptr;
bool elevated = false; bool elevated = false;
@@ -385,6 +386,9 @@ bool is_process_elevated()
} }
return elevated; return elevated;
};
static const bool cached_value = detection_func();
return use_cached_value ? cached_value : detection_func();
} }
bool drop_elevated_privileges() bool drop_elevated_privileges()

View File

@@ -61,7 +61,7 @@ enum WindowState
WindowState get_window_state(HWND hwnd); WindowState get_window_state(HWND hwnd);
// Returns true if the current process is running with elevated privileges // Returns true if the current process is running with elevated privileges
bool is_process_elevated(); bool is_process_elevated(const bool use_cached_value = true);
// Drops the elevated privilages if present // Drops the elevated privilages if present
bool drop_elevated_privileges(); bool drop_elevated_privileges();
@@ -78,7 +78,7 @@ bool run_same_elevation(const std::wstring& file, const std::wstring& params);
// Returns true if the current process is running from administrator account // Returns true if the current process is running from administrator account
bool check_user_is_admin(); bool check_user_is_admin();
//Returns true when one or more strings from vector found in string // Returns true when one or more strings from vector found in string
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what); bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
// Get the executable path or module name for modern apps // Get the executable path or module name for modern apps

View File

@@ -38,6 +38,11 @@ std::vector<MonitorInfo> MonitorInfo::GetMonitors(bool include_toolbar)
return monitors; return monitors;
} }
int MonitorInfo::GetMonitorsCount()
{
return GetMonitors(true).size();
}
static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data) static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
{ {
MONITORINFOEX monitor_info; MONITORINFOEX monitor_info;

View File

@@ -32,6 +32,7 @@ struct MonitorInfo : ScreenSize
// Returns monitor rects ordered from left to right // Returns monitor rects ordered from left to right
static std::vector<MonitorInfo> GetMonitors(bool include_toolbar); static std::vector<MonitorInfo> GetMonitors(bool include_toolbar);
static int GetMonitorsCount();
// Return primary display // Return primary display
static MonitorInfo GetPrimaryMonitor(); static MonitorInfo GetPrimaryMonitor();
// Return monitor on which hwnd window is displayed // Return monitor on which hwnd window is displayed

View File

@@ -12,6 +12,10 @@
<!-- Accent and AppTheme setting --> <!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF333333"/>
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#FF1a1a1a"/>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@@ -5,14 +5,73 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FancyZonesEditor" xmlns:local="clr-namespace:FancyZonesEditor"
mc:Ignorable="d" mc:Ignorable="d"
Background="LightGray" Background="Transparent"
Opacity="0.75"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style x:Key="CanvasZoneThumbStyle" TargetType="{x:Type Thumb}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" >
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.15">
<VisualTransition.GeneratedEasingFunction>
<ExponentialEase EasingMode="EaseInOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ThumbBorder" Duration="0:0:0.15" Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.6"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Border BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" Background="{StaticResource CanvasZoneBackgroundBrush}" BorderThickness="1">
<Grid x:Name="Frame"> <Grid x:Name="Frame">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="8"/> <RowDefinition Height="8"/>
<RowDefinition Height="16"/> <RowDefinition Height="16"/>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="16"/> <RowDefinition Height="16"/>
<RowDefinition Height="8"/> <RowDefinition Height="8"/>
@@ -24,32 +83,35 @@
<ColumnDefinition Width="16"/> <ColumnDefinition Width="16"/>
<ColumnDefinition Width="8"/> <ColumnDefinition Width="8"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" Background="Black" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" Background="Black" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" Background="Black" Grid.Row="4" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" Background="Black" Grid.Row="4" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted"/>
<Thumb x:Name="NResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="0" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted"/>
<Thumb x:Name="SResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="5" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted"/>
<Thumb x:Name="WResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted"/>
<Thumb x:Name="EResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="4" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted"/>
<DockPanel Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3">
<Button DockPanel.Dock="Right" Padding="8,0" Click="OnClose">
<Image Source="images/ChromeClose.png" Height="24" Width="24" />
</Button>
<Thumb x:Name="Caption" Cursor="SizeAll" Background="DarkGray" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted"/>
</DockPanel>
<Rectangle Fill="LightGray" Grid.Row="3" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3"/>
<Canvas x:Name="Body" />
<Label Name="LabelID" <Label Name="LabelID"
Content="ID" Content="ID"
Canvas.Left="10" Canvas.Left="10"
Canvas.Bottom="10" Canvas.Bottom="10"
FontSize="80" FontSize="64"
FontFamily="Segoe UI" FontFamily="Segoe UI Light"
Foreground="Black" Foreground="White"
Grid.Column="2" Grid.Column="2"
Grid.Row="3" Grid.Row="2"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
HorizontalContentAlignment="Center" /> HorizontalContentAlignment="Center" />
<Thumb x:Name="Caption" Cursor="SizeAll" Background="Transparent" BorderThickness="3" Padding="4" Grid.Column="0" Grid.ColumnSpan="5" Grid.Row="0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NResize" Cursor="SizeNS" BorderThickness="0,3,0,0" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SResize" Cursor="SizeNS" BorderThickness="0,0,0,3" Grid.Row="4" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="WResize" Cursor="SizeWE" BorderThickness="3,0,0,0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="EResize" Cursor="SizeWE" BorderThickness="0,0,3,0" Grid.Column="4" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NWResize" Cursor="SizeNWSE" BorderThickness="3,3,0,0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="NEResize" Cursor="SizeNESW" BorderThickness="0,3,3,0" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="3,0,0,3" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,3,3" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
<Button Content="&#xE894;" BorderThickness="0" ToolTip="Delete zone" Background="Transparent" Foreground="White" FontSize="16" Padding="4" Click="OnClose" Grid.Row="2" Grid.Column="2" FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{DynamicResource CloseButtonStyle}"/>
<Canvas x:Name="Body" />
</Grid> </Grid>
</Border>
</UserControl> </UserControl>

View File

@@ -76,6 +76,9 @@
<RunCodeAnalysis>false</RunCodeAnalysis> <RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisRuleSet>..\..\..\..\codeAnalysis\Rules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\..\..\codeAnalysis\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationIcon>images\FancyZonesEditor.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
@@ -248,5 +251,8 @@
<ItemGroup> <ItemGroup>
<Resource Include="images\Merge.png" /> <Resource Include="images\Merge.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Resource Include="images\FancyZonesEditor.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@@ -9,10 +9,16 @@
<Thumb.Template> <Thumb.Template>
<ControlTemplate> <ControlTemplate>
<StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48"> <StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48">
<Ellipse Height="48" Width="48" Fill="#0078D7" />
<Ellipse x:Name="BackgroundEllipse" Height="48" Width="48" Fill="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" />
<Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/> <Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/>
<Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/> <Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/>
</StackPanel> </StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" TargetName="BackgroundEllipse" Value="0.6"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate> </ControlTemplate>
</Thumb.Template> </Thumb.Template>
</Thumb> </Thumb>

View File

@@ -7,10 +7,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
Background="LightGray" Background="{StaticResource GridZoneBackgroundBrush}"
BorderBrush="DarkGray" BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"
BorderThickness="1" BorderThickness="1"
Opacity="0.5" Opacity="0.8"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid x:Name="Frame"> <Grid x:Name="Frame">
<Canvas x:Name="Body" /> <Canvas x:Name="Body" />
@@ -23,9 +23,9 @@
HorizontalContentAlignment="Center" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Content="ID" Content="ID"
FontFamily="Segoe UI" FontFamily="Segoe UI Light"
FontSize="80" FontSize="64"
Foreground="Black" /> Foreground="White" />
<!--<TextBlock Margin="2" Text="Shift Key switches direction&#13;Ctrl Key repeats"/>--> <!--<TextBlock Margin="2" Text="Shift Key switches direction&#13;Ctrl Key repeats"/>-->
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -45,7 +45,7 @@ namespace FancyZonesEditor
private void OnSelectionChanged() private void OnSelectionChanged()
{ {
Background = IsSelected ? Brushes.SteelBlue : Brushes.LightGray; Background = IsSelected ? SystemParameters.WindowGlassBrush : App.Current.Resources["GridZoneBackgroundBrush"] as SolidColorBrush;
} }
public bool IsSelected public bool IsSelected
@@ -60,7 +60,7 @@ namespace FancyZonesEditor
OnSelectionChanged(); OnSelectionChanged();
_splitter = new Rectangle _splitter = new Rectangle
{ {
Fill = Brushes.DarkGray, Fill = SystemParameters.WindowGlassBrush,
}; };
Body.Children.Add(_splitter); Body.Children.Add(_splitter);
@@ -101,7 +101,16 @@ namespace FancyZonesEditor
private int SplitterThickness private int SplitterThickness
{ {
get { return Math.Max(((App)Application.Current).ZoneSettings.Spacing, 5); } get
{
Settings settings = ((App)Application.Current).ZoneSettings;
if (!settings.ShowSpacing)
{
return 1;
}
return Math.Max(settings.Spacing, 1);
}
} }
private void UpdateSplitter() private void UpdateSplitter()
@@ -146,7 +155,7 @@ namespace FancyZonesEditor
protected override void OnMouseEnter(MouseEventArgs e) protected override void OnMouseEnter(MouseEventArgs e)
{ {
_splitter.Fill = Brushes.DarkGray; _splitter.Fill = SystemParameters.WindowGlassBrush; // Active Accent color
base.OnMouseEnter(e); base.OnMouseEnter(e);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -150,6 +150,7 @@ public:
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept; void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept; void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept;
protected: protected:
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept; static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
@@ -180,6 +181,7 @@ private:
}; };
bool IsInterestingWindow(HWND window) noexcept; bool IsInterestingWindow(HWND window) noexcept;
bool IsCursorTypeIndicatingSizeEvent();
void UpdateZoneWindows() noexcept; void UpdateZoneWindows() noexcept;
void MoveWindowsOnDisplayChange() noexcept; void MoveWindowsOnDisplayChange() noexcept;
void UpdateDragState(HWND window, require_write_lock) noexcept; void UpdateDragState(HWND window, require_write_lock) noexcept;
@@ -679,7 +681,7 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea; //const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
const bool flash = false; const bool flash = false;
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash); auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash, newWorkArea);
if (zoneWindow) if (zoneWindow)
{ {
m_zoneWindowMap[monitor] = std::move(zoneWindow); m_zoneWindowMap[monitor] = std::move(zoneWindow);
@@ -694,6 +696,12 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
} }
void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept
{
std::shared_lock readLock(m_lock);
MoveWindowIntoZoneByIndexSet(window, monitor, { index });
}
void FancyZones::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept
{ {
std::shared_lock readLock(m_lock); std::shared_lock readLock(m_lock);
if (window != m_windowMoveSize) if (window != m_windowMoveSize)
@@ -705,7 +713,7 @@ void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int in
if (zoneWindow != m_zoneWindowMap.end()) if (zoneWindow != m_zoneWindowMap.end())
{ {
const auto& zoneWindowPtr = zoneWindow->second; const auto& zoneWindowPtr = zoneWindow->second;
zoneWindowPtr->MoveWindowIntoZoneByIndex(window, index); zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet);
} }
} }
} }
@@ -745,6 +753,33 @@ bool FancyZones::IsInterestingWindow(HWND window) noexcept
return true; return true;
} }
bool FancyZones::IsCursorTypeIndicatingSizeEvent()
{
CURSORINFO cursorInfo = { 0 };
cursorInfo.cbSize = sizeof(cursorInfo);
if (::GetCursorInfo(&cursorInfo))
{
if (::LoadCursor(NULL, IDC_SIZENS) == cursorInfo.hCursor)
{
return true;
}
if (::LoadCursor(NULL, IDC_SIZEWE) == cursorInfo.hCursor)
{
return true;
}
if (::LoadCursor(NULL, IDC_SIZENESW) == cursorInfo.hCursor)
{
return true;
}
if (::LoadCursor(NULL, IDC_SIZENWSE) == cursorInfo.hCursor)
{
return true;
}
}
return false;
}
void FancyZones::UpdateZoneWindows() noexcept void FancyZones::UpdateZoneWindows() noexcept
{ {
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL { auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
@@ -832,10 +867,8 @@ void FancyZones::UpdateDragState(HWND window, require_write_lock) noexcept
m_dragEnabled = !(shift | mouse); m_dragEnabled = !(shift | mouse);
} }
const bool windowElevated = IsProcessOfWindowElevated(window);
static const bool meElevated = is_process_elevated();
static bool warning_shown = false; static bool warning_shown = false;
if (windowElevated && !meElevated) if (!is_process_elevated() && IsProcessOfWindowElevated(window))
{ {
m_dragEnabled = false; m_dragEnabled = false;
if (!warning_shown && !is_cant_drag_elevated_warning_disabled()) if (!warning_shown && !is_cant_drag_elevated_warning_disabled())
@@ -920,23 +953,10 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
{ {
// Only enter move/size if the cursor is inside the window rect by a certain padding. if (IsCursorTypeIndicatingSizeEvent())
// This prevents resize from triggering zones.
RECT windowRect{};
::GetWindowRect(window, &windowRect);
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) == FALSE)
{ {
return; return;
} }
m_inMoveSize = true; m_inMoveSize = true;
auto iter = m_zoneWindowMap.find(monitor); auto iter = m_zoneWindowMap.find(monitor);

View File

@@ -604,9 +604,6 @@ namespace JSONHelpers
if (!std::filesystem::exists(jsonFilePath)) if (!std::filesystem::exists(jsonFilePath))
{ {
TmpMigrateAppliedZoneSetsFromRegistry();
// Custom zone sets have to be migrated after applied zone sets!
MigrateCustomZoneSetsFromRegistry(); MigrateCustomZoneSetsFromRegistry();
SaveFancyZonesData(); SaveFancyZonesData();
@@ -639,56 +636,6 @@ namespace JSONHelpers
json::to_file(jsonFilePath, root); json::to_file(jsonFilePath, root);
} }
void FancyZonesData::TmpMigrateAppliedZoneSetsFromRegistry()
{
std::wregex ex(L"^[0-9]{3,4}_[0-9]{3,4}$");
std::scoped_lock lock{ dataLock };
wchar_t key[256];
StringCchPrintf(key, ARRAYSIZE(key), L"%s", RegistryHelpers::REG_SETTINGS);
HKEY hkey;
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
{
wchar_t resolutionKey[256]{};
DWORD resolutionKeyLength = ARRAYSIZE(resolutionKey);
DWORD i = 0;
while (RegEnumKeyW(hkey, i++, resolutionKey, resolutionKeyLength) == ERROR_SUCCESS)
{
std::wstring resolution{ resolutionKey };
wchar_t appliedZoneSetskey[256];
StringCchPrintf(appliedZoneSetskey, ARRAYSIZE(appliedZoneSetskey), L"%s\\%s", RegistryHelpers::REG_SETTINGS, resolutionKey);
HKEY appliedZoneSetsHkey;
if (std::regex_match(resolution, ex) && RegOpenKeyExW(HKEY_CURRENT_USER, appliedZoneSetskey, 0, KEY_ALL_ACCESS, &appliedZoneSetsHkey) == ERROR_SUCCESS)
{
ZoneSetPersistedDataOLD data;
DWORD dataSize = sizeof(data);
wchar_t value[256]{};
DWORD valueLength = ARRAYSIZE(value);
DWORD i = 0;
while (RegEnumValueW(appliedZoneSetsHkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
{
ZoneSetData appliedZoneSetData;
appliedZoneSetData.type = TypeFromLayoutId(data.LayoutId);
if (appliedZoneSetData.type != ZoneSetLayoutType::Custom)
{
appliedZoneSetData.uuid = std::wstring{ value };
}
else
{
// uuid is changed later to actual uuid when migrating custom zone sets
appliedZoneSetData.uuid = std::to_wstring(data.LayoutId);
}
appliedZoneSetsMap[value] = appliedZoneSetData;
dataSize = sizeof(data);
valueLength = ARRAYSIZE(value);
}
}
resolutionKeyLength = ARRAYSIZE(resolutionKey);
}
}
}
void FancyZonesData::MigrateCustomZoneSetsFromRegistry() void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
{ {
std::scoped_lock lock{ dataLock }; std::scoped_lock lock{ dataLock };
@@ -709,30 +656,20 @@ namespace JSONHelpers
zoneSetData.type = static_cast<CustomLayoutType>(data[2]); zoneSetData.type = static_cast<CustomLayoutType>(data[2]);
// int version = data[0] * 256 + data[1]; - Not used anymore // int version = data[0] * 256 + data[1]; - Not used anymore
std::wstring uuid = std::to_wstring(data[3] * 256 + data[4]);
auto it = std::find_if(appliedZoneSetsMap.cbegin(), appliedZoneSetsMap.cend(), [&uuid](std::pair<std::wstring, ZoneSetData> zoneSetMap) {
return zoneSetMap.second.uuid.compare(uuid) == 0;
});
if (it != appliedZoneSetsMap.cend())
{
uuid = it->first;
}
else
{
GUID guid; GUID guid;
auto result = CoCreateGuid(&guid); auto result = CoCreateGuid(&guid);
if (result != S_OK) if (result != S_OK)
{ {
return; continue;
} }
wil::unique_cotaskmem_string guidString; wil::unique_cotaskmem_string guidString;
if (SUCCEEDED_LOG(StringFromCLSID(guid, &guidString))) if (!SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
{ {
uuid = guidString.get(); continue;
}
} }
std::wstring uuid = guidString.get();
switch (zoneSetData.type) switch (zoneSetData.type)
{ {
case CustomLayoutType::Grid: { case CustomLayoutType::Grid: {

View File

@@ -207,12 +207,16 @@ namespace JSONHelpers
#if defined(UNIT_TESTS) #if defined(UNIT_TESTS)
inline void clear_data() inline void clear_data()
{ {
appliedZoneSetsMap.clear();
appZoneHistoryMap.clear(); appZoneHistoryMap.clear();
deviceInfoMap.clear(); deviceInfoMap.clear();
customZoneSetsMap.clear(); customZoneSetsMap.clear();
activeDeviceId.clear(); activeDeviceId.clear();
} }
inline void SetDeviceInfo(const std::wstring& deviceId, DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
#endif #endif
inline void SetActiveDeviceId(const std::wstring& deviceId) inline void SetActiveDeviceId(const std::wstring& deviceId)
@@ -254,10 +258,8 @@ namespace JSONHelpers
void SaveFancyZonesData() const; void SaveFancyZonesData() const;
private: private:
void TmpMigrateAppliedZoneSetsFromRegistry();
void MigrateCustomZoneSetsFromRegistry(); void MigrateCustomZoneSetsFromRegistry();
std::unordered_map<std::wstring, ZoneSetData> appliedZoneSetsMap{};
std::unordered_map<std::wstring, AppZoneHistoryData> appZoneHistoryMap{}; std::unordered_map<std::wstring, AppZoneHistoryData> appZoneHistoryMap{};
std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{}; std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{};
std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{}; std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{};

View File

@@ -4,6 +4,9 @@
#include <common/monitors.h> #include <common/monitors.h>
#include "Zone.h" #include "Zone.h"
#include "Settings.h" #include "Settings.h"
#include "util.h"
#include "common/monitors.h"
struct Zone : winrt::implements<Zone, IZone> struct Zone : winrt::implements<Zone, IZone>
{ {
@@ -20,6 +23,7 @@ public:
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept; IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; } IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; } IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
IFACEMETHODIMP_(RECT) ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept;
private: private:
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept; void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
@@ -61,14 +65,13 @@ IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize)
void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
{ {
// Skip invisible windows SizeWindowToRect(window, ComputeActualZoneRect(window, zoneWindow));
if (!IsWindowVisible(window)) }
{
return;
}
RECT Zone::ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept
{
// Take care of 1px border // Take care of 1px border
RECT zoneRect = m_zoneRect; RECT newWindowRect = m_zoneRect;
RECT windowRect{}; RECT windowRect{};
::GetWindowRect(window, &windowRect); ::GetWindowRect(window, &windowRect);
@@ -77,57 +80,43 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
const auto level = DPIAware::GetAwarenessLevel(GetWindowDpiAwarenessContext(window)); const auto level = DPIAware::GetAwarenessLevel(GetWindowDpiAwarenessContext(window));
const bool accountForUnawareness = level < DPIAware::PER_MONITOR_AWARE; const bool accountForUnawareness = level < DPIAware::PER_MONITOR_AWARE;
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect)))) if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
{ {
const auto left_margin = frameRect.left - windowRect.left; LONG leftMargin = frameRect.left - windowRect.left;
const auto right_margin = frameRect.right - windowRect.right; LONG rightMargin = frameRect.right - windowRect.right;
const auto bottom_margin = frameRect.bottom - windowRect.bottom; LONG bottomMargin = frameRect.bottom - windowRect.bottom;
zoneRect.left -= left_margin; newWindowRect.left -= leftMargin;
zoneRect.right -= right_margin; newWindowRect.right -= rightMargin;
zoneRect.bottom -= bottom_margin; newWindowRect.bottom -= bottomMargin;
} }
// Map to screen coords // Map to screen coords
MapWindowRect(zoneWindow, nullptr, &zoneRect); MapWindowRect(zoneWindow, nullptr, &newWindowRect);
MONITORINFO mi{sizeof(mi)}; MONITORINFO mi{ sizeof(mi) };
if (GetMonitorInfoW(MonitorFromWindow(zoneWindow, MONITOR_DEFAULTTONEAREST), &mi)) if (GetMonitorInfoW(MonitorFromWindow(zoneWindow, MONITOR_DEFAULTTONEAREST), &mi))
{ {
const auto taskbar_left_size = std::abs(mi.rcMonitor.left - mi.rcWork.left); const auto taskbar_left_size = std::abs(mi.rcMonitor.left - mi.rcWork.left);
const auto taskbar_top_size = std::abs(mi.rcMonitor.top - mi.rcWork.top); const auto taskbar_top_size = std::abs(mi.rcMonitor.top - mi.rcWork.top);
OffsetRect(&zoneRect, -taskbar_left_size, -taskbar_top_size); OffsetRect(&newWindowRect, -taskbar_left_size, -taskbar_top_size);
if (accountForUnawareness)
if (accountForUnawareness && MonitorInfo::GetMonitorsCount() > 1)
{ {
zoneRect.left = max(mi.rcMonitor.left, zoneRect.left); newWindowRect.left = max(mi.rcMonitor.left, newWindowRect.left);
zoneRect.right = min(mi.rcMonitor.right - taskbar_left_size, zoneRect.right); newWindowRect.right = min(mi.rcMonitor.right - taskbar_left_size, newWindowRect.right);
zoneRect.top = max(mi.rcMonitor.top, zoneRect.top); newWindowRect.top = max(mi.rcMonitor.top, newWindowRect.top);
zoneRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, zoneRect.bottom); newWindowRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, newWindowRect.bottom);
} }
} }
WINDOWPLACEMENT placement{}; if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0)
::GetWindowPlacement(window, &placement);
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); newWindowRect.right = newWindowRect.left + (windowRect.right - windowRect.left);
::GetWindowPlacement(window, &placement); newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top);
} }
// Do not restore minimized windows. We change their placement though so they restore to the correct zone. return newWindowRect;
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
{
placement.showCmd = SW_RESTORE | SW_SHOWNA;
}
placement.rcNormalPosition = zoneRect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
::SetWindowPlacement(window, &placement);
// Do it again, allowing Windows to resize the window and set correct scaling
// This fixes Issue #365
::SetWindowPlacement(window, &placement);
} }
void Zone::StampZone(HWND window, bool stamp) noexcept void Zone::StampZone(HWND window, bool stamp) noexcept

View File

@@ -42,9 +42,20 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
*/ */
IFACEMETHOD_(void, SetId)(size_t id) = 0; IFACEMETHOD_(void, SetId)(size_t id) = 0;
/** /**
* @retirns Zone identifier. * @returns Zone identifier.
*/ */
IFACEMETHOD_(size_t, Id)() = 0; IFACEMETHOD_(size_t, Id)() = 0;
/**
* Compute the coordinates of the rectangle to which a window should be resized.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @returns a RECT structure, describing global coordinates to which a window should be resized
*/
IFACEMETHOD_(RECT, ComputeActualZoneRect)(HWND window, HWND zoneWindow) = 0;
}; };
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept; winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept;

View File

@@ -122,14 +122,16 @@ public:
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType) IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
LayoutType() noexcept { return m_config.LayoutType; } LayoutType() noexcept { return m_config.LayoutType; }
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept; IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
IFACEMETHODIMP_(winrt::com_ptr<IZone>) IFACEMETHODIMP_(std::vector<int>)
ZoneFromPoint(POINT pt) noexcept; ZonesFromPoint(POINT pt) noexcept;
IFACEMETHODIMP_(int) IFACEMETHODIMP_(int)
GetZoneIndexFromWindow(HWND window) noexcept; GetZoneIndexFromWindow(HWND window) noexcept;
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>) IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>)
GetZones() noexcept { return m_zones; } GetZones() noexcept { return m_zones; }
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept; MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index, bool stampZone) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept;
IFACEMETHODIMP_(bool) IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept; MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
@@ -162,41 +164,79 @@ IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
return S_OK; return S_OK;
} }
IFACEMETHODIMP_(winrt::com_ptr<IZone>) IFACEMETHODIMP_(std::vector<int>)
ZoneSet::ZoneFromPoint(POINT pt) noexcept ZoneSet::ZonesFromPoint(POINT pt) noexcept
{ {
winrt::com_ptr<IZone> smallestKnownZone = nullptr; const int SENSITIVITY_RADIUS = 20;
// To reduce redundant calculations, we will store the last known zones area. std::vector<int> capturedZones;
int smallestKnownZoneArea = INT32_MAX; std::vector<int> strictlyCapturedZones;
for (auto iter = m_zones.rbegin(); iter != m_zones.rend(); iter++) for (size_t i = 0; i < m_zones.size(); i++)
{ {
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>()) auto zone = m_zones[i];
RECT newZoneRect = zone->GetZoneRect();
if (newZoneRect.left < newZoneRect.right && newZoneRect.top < newZoneRect.bottom) // proper zone
{ {
RECT* newZoneRect = &zone->GetZoneRect(); if (newZoneRect.left - SENSITIVITY_RADIUS <= pt.x && pt.x <= newZoneRect.right + SENSITIVITY_RADIUS &&
if (PtInRect(newZoneRect, pt)) newZoneRect.top - SENSITIVITY_RADIUS <= pt.y && pt.y <= newZoneRect.bottom + SENSITIVITY_RADIUS)
{ {
if (smallestKnownZone == nullptr) capturedZones.emplace_back(static_cast<int>(i));
{ }
smallestKnownZone = zone;
RECT* r = &smallestKnownZone->GetZoneRect(); if (newZoneRect.left <= pt.x && pt.x < newZoneRect.right &&
smallestKnownZoneArea = (r->right - r->left) * (r->bottom - r->top); newZoneRect.top <= pt.y && pt.y < newZoneRect.bottom)
}
else
{ {
int newZoneArea = (newZoneRect->right - newZoneRect->left) * (newZoneRect->bottom - newZoneRect->top); strictlyCapturedZones.emplace_back(static_cast<int>(i));
if (newZoneArea < smallestKnownZoneArea)
{
smallestKnownZone = zone;
smallestKnownZoneArea = newZoneArea;
}
}
} }
} }
} }
return smallestKnownZone; // If only one zone is captured, but it's not strictly captured
// don't consider it as captured
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
{
return {};
}
// If captured zones do not overlap, return all of them
// Otherwise, return the smallest one
bool overlap = false;
for (size_t i = 0; i < capturedZones.size(); ++i)
{
for (size_t j = i + 1; j < capturedZones.size(); ++j)
{
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
auto rectJ = m_zones[capturedZones[j]]->GetZoneRect();
if (max(rectI.top, rectJ.top) < min(rectI.bottom, rectJ.bottom) &&
max(rectI.left, rectJ.left) < min(rectI.right, rectJ.right))
{
overlap = true;
i = capturedZones.size() - 1;
break;
}
}
}
if (overlap)
{
size_t smallestIdx = 0;
for (size_t i = 1; i < capturedZones.size(); ++i)
{
auto rectS = m_zones[capturedZones[smallestIdx]]->GetZoneRect();
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
int smallestSize = (rectS.bottom - rectS.top) * (rectS.right - rectS.left);
int iSize = (rectI.bottom - rectI.top) * (rectI.right - rectI.left);
if (iSize <= smallestSize)
{
smallestIdx = i;
}
}
capturedZones = { capturedZones[smallestIdx] };
}
return capturedZones;
} }
IFACEMETHODIMP_(int) IFACEMETHODIMP_(int)
@@ -217,7 +257,7 @@ ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
} }
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index, bool stampZone) noexcept
{ {
if (m_zones.empty()) if (m_zones.empty())
{ {
@@ -236,7 +276,55 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
if (auto zone = m_zones.at(index)) if (auto zone = m_zones.at(index))
{ {
zone->AddWindowToZone(window, windowZone, false); zone->AddWindowToZone(window, windowZone, stampZone);
}
}
IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept
{
if (m_zones.empty())
{
return;
}
while (auto zoneDrop = ZoneFromWindow(window))
{
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
}
if (indexSet.size() == 1)
{
MoveWindowIntoZoneByIndex(window, windowZone, indexSet[0], stampZone);
return;
}
RECT size;
bool sizeEmpty = true;
for (int index : indexSet)
{
if (index < static_cast<int>(m_zones.size()))
{
RECT newSize = m_zones.at(index)->ComputeActualZoneRect(window, windowZone);
if (!sizeEmpty)
{
size.left = min(size.left, newSize.left);
size.top = min(size.top, newSize.top);
size.right = max(size.right, newSize.right);
size.bottom = max(size.bottom, newSize.bottom);
}
else
{
size = newSize;
sizeEmpty = false;
}
}
}
if (!sizeEmpty)
{
SizeWindowToRect(window, size);
} }
} }
@@ -306,10 +394,8 @@ ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient)
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window)); zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
} }
if (auto zone = ZoneFromPoint(ptClient)) auto zones = ZonesFromPoint(ptClient);
{ MoveWindowIntoZoneByIndexSet(window, zoneWindow, zones, true);
zone->AddWindowToZone(window, zoneWindow, true);
}
} }
IFACEMETHODIMP_(bool) IFACEMETHODIMP_(bool)

View File

@@ -24,12 +24,12 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
*/ */
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0; IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
/** /**
* Get zone from cursor coordinates. * Get zones from cursor coordinates.
* *
* @param pt Cursor coordinates. * @param pt Cursor coordinates.
* @returns Zone object (defining coordinates of the zone). * @returns Vector of indices, corresponding to the current set of zones - the zones considered active.
*/ */
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0; IFACEMETHOD_(std::vector<int>, ZonesFromPoint)(POINT pt) = 0;
/** /**
* Get index of the zone inside zone layout by window assigned to it. * Get index of the zone inside zone layout by window assigned to it.
* *
@@ -48,8 +48,20 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the * @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area. * current monitor desktop work area.
* @param index Zone index within zone layout. * @param index Zone index within zone layout.
* @param stampZone Whether the window being added to the zone should be stamped.
*/ */
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0; IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index, bool stampZone) = 0;
/**
* Assign window to the zones based on the set of zone indices inside zone layout.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @param indexSet The set of zone indices within zone layout.
* @param stampZone Whether the window being added to the zone should be stamped,
in case a single window is to be added.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND zoneWindow, const std::vector<int>& indexSet, bool stampZone) = 0;
/** /**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow). * Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
* *

View File

@@ -130,7 +130,7 @@ namespace ZoneWindowDrawUtils
Gdiplus::Color fillColor(colorSetting.fillAlpha, GetRValue(colorSetting.fill), GetGValue(colorSetting.fill), GetBValue(colorSetting.fill)); Gdiplus::Color fillColor(colorSetting.fillAlpha, GetRValue(colorSetting.fill), GetGValue(colorSetting.fill), GetBValue(colorSetting.fill));
Gdiplus::Color borderColor(colorSetting.borderAlpha, GetRValue(colorSetting.border), GetGValue(colorSetting.border), GetBValue(colorSetting.border)); Gdiplus::Color borderColor(colorSetting.borderAlpha, GetRValue(colorSetting.border), GetGValue(colorSetting.border), GetBValue(colorSetting.border));
Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left, zoneRect.bottom - zoneRect.top); Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left - 1, zoneRect.bottom - zoneRect.top - 1);
Gdiplus::Pen pen(borderColor, static_cast<Gdiplus::REAL>(colorSetting.thickness)); Gdiplus::Pen pen(borderColor, static_cast<Gdiplus::REAL>(colorSetting.thickness));
g.FillRectangle(new Gdiplus::SolidBrush(fillColor), rectangle); g.FillRectangle(new Gdiplus::SolidBrush(fillColor), rectangle);
@@ -148,7 +148,7 @@ namespace ZoneWindowDrawUtils
COLORREF highlightColor, COLORREF highlightColor,
int zoneOpacity, int zoneOpacity,
const std::vector<winrt::com_ptr<IZone>>& zones, const std::vector<winrt::com_ptr<IZone>>& zones,
const winrt::com_ptr<IZone>& highlightZone, const std::vector<int>& highlightZones,
bool flashMode, bool flashMode,
bool drawHints) noexcept bool drawHints) noexcept
{ {
@@ -158,15 +158,22 @@ namespace ZoneWindowDrawUtils
ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 }; ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 };
ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 }; ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
std::vector<bool> isHighlighted(zones.size(), false);
for (int x : highlightZones)
{
isHighlighted[x] = true;
}
for (auto iter = zones.begin(); iter != zones.end(); iter++) for (auto iter = zones.begin(); iter != zones.end(); iter++)
{ {
int zoneId = static_cast<int>(iter - zones.begin());
winrt::com_ptr<IZone> zone = iter->try_as<IZone>(); winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
if (!zone) if (!zone)
{ {
continue; continue;
} }
if (zone != highlightZone) if (!isHighlighted[zoneId])
{ {
if (flashMode) if (flashMode)
{ {
@@ -182,13 +189,12 @@ namespace ZoneWindowDrawUtils
DrawZone(hdc, colorViewer, zone, zones, flashMode); DrawZone(hdc, colorViewer, zone, zones, flashMode);
} }
} }
} else
if (highlightZone)
{ {
colorHighlight.fill = highlightColor; colorHighlight.fill = highlightColor;
colorHighlight.border = zoneBorderColor; colorHighlight.border = zoneBorderColor;
DrawZone(hdc, colorHighlight, highlightZone, zones, flashMode); DrawZone(hdc, colorHighlight, zone, zones, flashMode);
}
} }
} }
} }
@@ -199,7 +205,7 @@ public:
ZoneWindow(HINSTANCE hinstance); ZoneWindow(HINSTANCE hinstance);
~ZoneWindow(); ~ZoneWindow();
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones); bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea);
IFACEMETHODIMP MoveSizeEnter(HWND window, bool dragEnabled) noexcept; IFACEMETHODIMP MoveSizeEnter(HWND window, bool dragEnabled) noexcept;
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled) noexcept; IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled) noexcept;
@@ -210,6 +216,8 @@ public:
IsDragEnabled() noexcept { return m_dragEnabled; } IsDragEnabled() noexcept { return m_dragEnabled; }
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, int index) noexcept; MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept;
IFACEMETHODIMP_(bool) IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept; MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
@@ -232,13 +240,13 @@ protected:
private: private:
void LoadSettings() noexcept; void LoadSettings() noexcept;
void InitializeZoneSets(MONITORINFO const& mi) noexcept; void InitializeZoneSets(bool newWorkArea) noexcept;
void CalculateZoneSet() noexcept; void CalculateZoneSet() noexcept;
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept; void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept; LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
void OnPaint(wil::unique_hdc& hdc) noexcept; void OnPaint(wil::unique_hdc& hdc) noexcept;
void OnKeyUp(WPARAM wparam) noexcept; void OnKeyUp(WPARAM wparam) noexcept;
winrt::com_ptr<IZone> ZoneFromPoint(POINT pt) noexcept; std::vector<int> ZonesFromPoint(POINT pt) noexcept;
void CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept; void CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept;
void FlashZones() noexcept; void FlashZones() noexcept;
@@ -253,7 +261,7 @@ private:
bool m_dragEnabled{}; bool m_dragEnabled{};
winrt::com_ptr<IZoneSet> m_activeZoneSet; winrt::com_ptr<IZoneSet> m_activeZoneSet;
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets; std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
winrt::com_ptr<IZone> m_highlightZone; std::vector<int> m_highlightZone;
WPARAM m_keyLast{}; WPARAM m_keyLast{};
size_t m_keyCycle{}; size_t m_keyCycle{};
static const UINT m_showAnimationDuration = 200; // ms static const UINT m_showAnimationDuration = 200; // ms
@@ -289,7 +297,7 @@ ZoneWindow::~ZoneWindow()
Gdiplus::GdiplusShutdown(gdiplusToken); Gdiplus::GdiplusShutdown(gdiplusToken);
} }
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones) bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea)
{ {
m_host.copy_from(host); m_host.copy_from(host);
@@ -308,7 +316,7 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
m_uniqueId = uniqueId; m_uniqueId = uniqueId;
LoadSettings(); LoadSettings();
InitializeZoneSets(mi); InitializeZoneSets(newWorkArea);
m_window = wil::unique_hwnd{ 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) CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP, workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(), nullptr, nullptr, hinstance, this)
@@ -363,7 +371,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
m_dragEnabled = dragEnabled; m_dragEnabled = dragEnabled;
m_windowMoveSize = window; m_windowMoveSize = window;
m_drawHints = true; m_drawHints = true;
m_highlightZone = nullptr; m_highlightZone = {};
ShowZoneWindow(); ShowZoneWindow();
return S_OK; return S_OK;
} }
@@ -378,13 +386,13 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
if (dragEnabled) if (dragEnabled)
{ {
auto highlightZone = ZoneFromPoint(ptClient); auto highlightZone = ZonesFromPoint(ptClient);
redraw = (highlightZone != m_highlightZone); redraw = (highlightZone != m_highlightZone);
m_highlightZone = std::move(highlightZone); m_highlightZone = std::move(highlightZone);
} }
else if (m_highlightZone) else if (m_highlightZone.size())
{ {
m_highlightZone = nullptr; m_highlightZone = {};
redraw = true; redraw = true;
} }
@@ -432,10 +440,16 @@ ZoneWindow::RestoreOrginalTransparency() noexcept
IFACEMETHODIMP_(void) IFACEMETHODIMP_(void)
ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
{
MoveWindowIntoZoneByIndexSet(window, { index });
}
IFACEMETHODIMP_(void)
ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept
{ {
if (m_activeZoneSet) if (m_activeZoneSet)
{ {
m_activeZoneSet->MoveWindowIntoZoneByIndex(window, m_window.get(), index); m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet, false);
} }
} }
@@ -518,7 +532,7 @@ ZoneWindow::HideZoneWindow() noexcept
m_keyLast = 0; m_keyLast = 0;
m_windowMoveSize = nullptr; m_windowMoveSize = nullptr;
m_drawHints = false; m_drawHints = false;
m_highlightZone = nullptr; m_highlightZone = {};
} }
} }
@@ -529,10 +543,10 @@ void ZoneWindow::LoadSettings() noexcept
JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId); JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId);
} }
void ZoneWindow::InitializeZoneSets(MONITORINFO const& mi) noexcept void ZoneWindow::InitializeZoneSets(bool newWorkArea) noexcept
{ {
auto parent = m_host->GetParentZoneWindow(m_monitor); auto parent = m_host->GetParentZoneWindow(m_monitor);
if (parent) if (newWorkArea && parent)
{ {
// Update device info with device info from parent virtual desktop (if empty). // Update device info with device info from parent virtual desktop (if empty).
JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parent->UniqueId(), m_uniqueId); JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parent->UniqueId(), m_uniqueId);
@@ -685,13 +699,13 @@ void ZoneWindow::OnKeyUp(WPARAM wparam) noexcept
} }
} }
winrt::com_ptr<IZone> ZoneWindow::ZoneFromPoint(POINT pt) noexcept std::vector<int> ZoneWindow::ZonesFromPoint(POINT pt) noexcept
{ {
if (m_activeZoneSet) if (m_activeZoneSet)
{ {
return m_activeZoneSet->ZoneFromPoint(pt); return m_activeZoneSet->ZonesFromPoint(pt);
} }
return nullptr; return {};
} }
void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept
@@ -739,7 +753,7 @@ void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::Inp
{ {
m_host->MoveWindowsOnActiveZoneSetChange(); m_host->MoveWindowsOnActiveZoneSetChange();
} }
m_highlightZone = nullptr; m_highlightZone = {};
} }
void ZoneWindow::FlashZones() noexcept void ZoneWindow::FlashZones() noexcept
@@ -773,10 +787,10 @@ LRESULT CALLBACK ZoneWindow::s_WndProc(HWND window, UINT message, WPARAM wparam,
DefWindowProc(window, message, wparam, lparam); DefWindowProc(window, message, wparam, lparam);
} }
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones) noexcept winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept
{ {
auto self = winrt::make_self<ZoneWindow>(hinstance); auto self = winrt::make_self<ZoneWindow>(hinstance);
if (self->Init(host, hinstance, monitor, uniqueId, flashZones)) if (self->Init(host, hinstance, monitor, uniqueId, flashZones, newWorkArea))
{ {
return self; return self;
} }

View File

@@ -53,6 +53,13 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
* @param index Zone index within zone layout. * @param index Zone index within zone layout.
*/ */
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0; IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0;
/**
* Assign window to the zones based on the set of zone indices inside zone layout.
*
* @param window Handle of window which should be assigned to zone.
* @param indexSet The set of zone indices within zone layout.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const std::vector<int>& indexSet) = 0;
/** /**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow). * Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
* *
@@ -98,4 +105,4 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
}; };
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
const std::wstring& uniqueId, bool flashZones) noexcept; const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept;

View File

@@ -108,3 +108,30 @@ void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
monitorInfo = std::move(sortedMonitorInfo); monitorInfo = std::move(sortedMonitorInfo);
} }
void SizeWindowToRect(HWND window, RECT rect) noexcept
{
WINDOWPLACEMENT placement{};
::GetWindowPlacement(window, &placement);
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
::GetWindowPlacement(window, &placement);
}
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
{
placement.showCmd = SW_RESTORE | SW_SHOWNA;
}
placement.rcNormalPosition = rect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
::SetWindowPlacement(window, &placement);
// Do it again, allowing Windows to resize the window and set correct scaling
// This fixes Issue #365
::SetWindowPlacement(window, &placement);
}

View File

@@ -118,3 +118,4 @@ inline BYTE OpacitySettingToAlpha(int opacity)
UINT GetDpiForMonitor(HMONITOR monitor) noexcept; UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo); void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
void SizeWindowToRect(HWND window, RECT rect) noexcept;

View File

@@ -132,8 +132,8 @@ namespace FancyZonesUnitTests
TEST_METHOD (ZoneFromPointEmpty) TEST_METHOD (ZoneFromPointEmpty)
{ {
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 }); auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
Assert::IsTrue(nullptr == actual); Assert::IsTrue(actual.size() == 0);
} }
TEST_METHOD (ZoneFromPointInner) TEST_METHOD (ZoneFromPointInner)
@@ -146,9 +146,9 @@ namespace FancyZonesUnitTests
{ {
for (int j = top + 1; j < bottom; j++) for (int j = top + 1; j < bottom; j++)
{ {
auto actual = m_set->ZoneFromPoint(POINT{ i, j }); auto actual = m_set->ZonesFromPoint(POINT{ i, j });
Assert::IsTrue(actual != nullptr); Assert::IsTrue(actual.size() == 1);
compareZones(expected, actual); compareZones(expected, m_set->GetZones()[actual[0]]);
} }
} }
} }
@@ -161,29 +161,29 @@ namespace FancyZonesUnitTests
for (int i = left; i < right; i++) for (int i = left; i < right; i++)
{ {
auto actual = m_set->ZoneFromPoint(POINT{ i, top }); auto actual = m_set->ZonesFromPoint(POINT{ i, top });
Assert::IsTrue(actual != nullptr); Assert::IsTrue(actual.size() == 1);
compareZones(expected, actual); compareZones(expected, m_set->GetZones()[actual[0]]);
} }
for (int i = top; i < bottom; i++) for (int i = top; i < bottom; i++)
{ {
auto actual = m_set->ZoneFromPoint(POINT{ left, i }); auto actual = m_set->ZonesFromPoint(POINT{ left, i });
Assert::IsTrue(actual != nullptr); Assert::IsTrue(actual.size() == 1);
compareZones(expected, actual); compareZones(expected, m_set->GetZones()[actual[0]]);
} }
//bottom and right borders considered to be outside //bottom and right borders considered to be outside
for (int i = left; i < right; i++) for (int i = left; i < right; i++)
{ {
auto actual = m_set->ZoneFromPoint(POINT{ i, bottom }); auto actual = m_set->ZonesFromPoint(POINT{ i, bottom });
Assert::IsTrue(nullptr == actual); Assert::IsTrue(actual.size() == 0);
} }
for (int i = top; i < bottom; i++) for (int i = top; i < bottom; i++)
{ {
auto actual = m_set->ZoneFromPoint(POINT{ right, i }); auto actual = m_set->ZonesFromPoint(POINT{ right, i });
Assert::IsTrue(nullptr == actual); Assert::IsTrue(actual.size() == 0);
} }
} }
@@ -193,8 +193,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ left, top, right, bottom }); winrt::com_ptr<IZone> zone = MakeZone({ left, top, right, bottom });
m_set->AddZone(zone); m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 101, 101 }); auto actual = m_set->ZonesFromPoint(POINT{ 200, 200 });
Assert::IsTrue(actual == nullptr); Assert::IsTrue(actual.size() == 0);
} }
TEST_METHOD (ZoneFromPointOverlapping) TEST_METHOD (ZoneFromPointOverlapping)
@@ -208,9 +208,65 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone4 = MakeZone({ 10, 10, 50, 50 }); winrt::com_ptr<IZone> zone4 = MakeZone({ 10, 10, 50, 50 });
m_set->AddZone(zone4); m_set->AddZone(zone4);
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 }); // zone4 is expected because it's the smallest one, and it's considered to be inside
Assert::IsTrue(actual != nullptr); // since Multizones support
compareZones(zone2, actual);
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual.size() == 1);
compareZones(zone4, m_set->GetZones()[actual[0]]);
}
TEST_METHOD (ZoneFromPointMultizoneHorizontal)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 50, 100 });
Assert::IsTrue(actual.size() == 2);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone3, m_set->GetZones()[actual[1]]);
}
TEST_METHOD (ZoneFromPointMultizoneVertical)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 100, 50 });
Assert::IsTrue(actual.size() == 2);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone2, m_set->GetZones()[actual[1]]);
}
TEST_METHOD(ZoneFromPointMultizoneQuad)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 100, 100 });
Assert::IsTrue(actual.size() == 4);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone2, m_set->GetZones()[actual[1]]);
compareZones(zone3, m_set->GetZones()[actual[2]]);
compareZones(zone4, m_set->GetZones()[actual[3]]);
} }
TEST_METHOD (ZoneFromPointWithNotNormalizedRect) TEST_METHOD (ZoneFromPointWithNotNormalizedRect)
@@ -218,8 +274,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ 100, 100, 0, 0 }); winrt::com_ptr<IZone> zone = MakeZone({ 100, 100, 0, 0 });
m_set->AddZone(zone); m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 }); auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual == nullptr); Assert::IsTrue(actual.size() == 0);
} }
TEST_METHOD (ZoneFromPointWithZeroRect) TEST_METHOD (ZoneFromPointWithZeroRect)
@@ -227,8 +283,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ 0, 0, 0, 0 }); winrt::com_ptr<IZone> zone = MakeZone({ 0, 0, 0, 0 });
m_set->AddZone(zone); m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 }); auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual == nullptr); Assert::IsTrue(actual.size() == 0);
} }
TEST_METHOD (ZoneIndexFromWindow) TEST_METHOD (ZoneIndexFromWindow)
@@ -316,7 +372,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3); m_set->AddZone(zone3);
HWND window = Mocks::Window(); HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
Assert::IsFalse(zone1->ContainsWindow(window)); Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsTrue(zone2->ContainsWindow(window)); Assert::IsTrue(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window)); Assert::IsFalse(zone3->ContainsWindow(window));
@@ -325,7 +381,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveWindowIntoZoneByIndexWithNoZones) TEST_METHOD (MoveWindowIntoZoneByIndexWithNoZones)
{ {
HWND window = Mocks::Window(); HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
} }
TEST_METHOD (MoveWindowIntoZoneByIndexWithInvalidIndex) TEST_METHOD (MoveWindowIntoZoneByIndexWithInvalidIndex)
@@ -338,8 +394,8 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3); m_set->AddZone(zone3);
HWND window = Mocks::Window(); HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100, false);
Assert::IsTrue(zone1->ContainsWindow(window)); Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window)); Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window)); Assert::IsFalse(zone3->ContainsWindow(window));
} }
@@ -355,17 +411,17 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3); m_set->AddZone(zone3);
HWND window = Mocks::Window(); HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
Assert::IsTrue(zone1->ContainsWindow(window)); Assert::IsTrue(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window)); Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window)); Assert::IsFalse(zone3->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
Assert::IsFalse(zone1->ContainsWindow(window)); Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsTrue(zone2->ContainsWindow(window)); Assert::IsTrue(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window)); Assert::IsFalse(zone3->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2, false);
Assert::IsFalse(zone1->ContainsWindow(window)); Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window)); Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsTrue(zone3->ContainsWindow(window)); Assert::IsTrue(zone3->ContainsWindow(window));
@@ -382,9 +438,9 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3); m_set->AddZone(zone3);
HWND window = Mocks::Window(); HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0); m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
Assert::IsTrue(zone1->ContainsWindow(window)); Assert::IsTrue(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window)); Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window)); Assert::IsFalse(zone3->ContainsWindow(window));
@@ -401,7 +457,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone1); m_set->AddZone(zone1);
auto window = Mocks::Window(); auto window = Mocks::Window();
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 101, 101 }); m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 200, 200 });
Assert::IsFalse(zone1->ContainsWindow(window)); Assert::IsFalse(zone1->ContainsWindow(window));
} }

View File

@@ -55,6 +55,7 @@ namespace FancyZonesUnitTests
{ {
const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"; const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId"; const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId";
std::wstringstream m_parentUniqueId;
std::wstringstream m_uniqueId; std::wstringstream m_uniqueId;
HINSTANCE m_hInst{}; HINSTANCE m_hInst{};
@@ -75,6 +76,7 @@ namespace FancyZonesUnitTests
m_monitorInfo.cbSize = sizeof(m_monitorInfo); m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo)); Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}";
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}"; m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty()); Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty());
@@ -113,7 +115,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath); m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
} }
void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow) void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow)
@@ -129,14 +131,14 @@ namespace FancyZonesUnitTests
public: public:
TEST_METHOD(CreateZoneWindow) TEST_METHOD(CreateZoneWindow)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(m_zoneWindow); testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet()); Assert::IsNull(m_zoneWindow->ActiveZoneSet());
} }
TEST_METHOD(CreateZoneWindowNoHinst) TEST_METHOD(CreateZoneWindowNoHinst)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(m_zoneWindow); testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet()); Assert::IsNull(m_zoneWindow->ActiveZoneSet());
@@ -144,7 +146,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoHinstFlashZones) TEST_METHOD(CreateZoneWindowNoHinstFlashZones)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true); m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true, false);
testZoneWindow(m_zoneWindow); testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet()); Assert::IsNull(m_zoneWindow->ActiveZoneSet());
@@ -152,7 +154,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoMonitor) TEST_METHOD(CreateZoneWindowNoMonitor)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow.get()); Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr); Assert::IsNotNull(m_hostPtr);
@@ -160,7 +162,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoMonitorFlashZones) TEST_METHOD(CreateZoneWindowNoMonitorFlashZones)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true, false);
Assert::IsNull(m_zoneWindow.get()); Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr); Assert::IsNotNull(m_hostPtr);
@@ -170,7 +172,7 @@ namespace FancyZonesUnitTests
{ {
// Generate unique id without device id // Generate unique id without device id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str()); std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str());
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId; const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
@@ -186,7 +188,7 @@ namespace FancyZonesUnitTests
{ {
// Generate unique id without virtual desktop id // Generate unique id without virtual desktop id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr); std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom); const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(m_zoneWindow.get()); Assert::IsNotNull(m_zoneWindow.get());
@@ -213,7 +215,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath); m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization //temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual); testZoneWindow(actual);
@@ -237,7 +239,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath); m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization //temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual); testZoneWindow(actual);
@@ -273,7 +275,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath); m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization //temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual); testZoneWindow(actual);
@@ -320,7 +322,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath); m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization //temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual); testZoneWindow(actual);
@@ -367,7 +369,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath); m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization //temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual); testZoneWindow(actual);
@@ -376,9 +378,68 @@ namespace FancyZonesUnitTests
Assert::AreEqual((size_t)1, actualZoneSet.size()); Assert::AreEqual((size_t)1, actualZoneSet.size());
} }
TEST_METHOD (CreateZoneWindowClonedFromParent)
{
using namespace JSONHelpers;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
// newWorkArea = true - zoneWindow will be cloned from parent
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, true);
Assert::IsNotNull(actualZoneWindow->ActiveZoneSet());
const auto actualZoneSet = actualZoneWindow->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)zoneCount, actualZoneSet.size());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
Assert::AreEqual(zoneCount, currentDeviceInfo.zoneCount);
Assert::AreEqual(spacing, currentDeviceInfo.spacing);
Assert::AreEqual(static_cast<int>(type), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD (CreateZoneWindowNotClonedFromParent)
{
using namespace JSONHelpers;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
// newWorkArea = false - zoneWindow won't be cloned from parent
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(actualZoneWindow->ActiveZoneSet());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
// default values
Assert::AreEqual(false, currentDeviceInfo.showSpacing);
Assert::AreEqual(0, currentDeviceInfo.zoneCount);
Assert::AreEqual(0, currentDeviceInfo.spacing);
Assert::AreEqual(std::wstring{ L"null" }, currentDeviceInfo.activeZoneSet.uuid);
Assert::AreEqual(static_cast<int>(ZoneSetLayoutType::Blank), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD(MoveSizeEnter) TEST_METHOD(MoveSizeEnter)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK; const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true); const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
@@ -389,7 +450,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEnterTwice) TEST_METHOD(MoveSizeEnterTwice)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = E_INVALIDARG; const auto expected = E_INVALIDARG;
@@ -402,7 +463,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdate) TEST_METHOD(MoveSizeUpdate)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK; const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true); const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true);
@@ -413,7 +474,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates) TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK; const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true); const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true);
@@ -424,7 +485,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdatePointBigCoordinates) TEST_METHOD(MoveSizeUpdatePointBigCoordinates)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK; const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true); const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true);
@@ -445,7 +506,7 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected, actual); Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet(); const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false); zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window); const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex); Assert::AreNotEqual(-1, actualZoneIndex);
} }
@@ -458,7 +519,7 @@ namespace FancyZonesUnitTests
zoneWindow->MoveSizeEnter(window, true); zoneWindow->MoveSizeEnter(window, true);
const auto expected = S_OK; const auto expected = S_OK;
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 }); const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -100, -100 });
Assert::AreEqual(expected, actual); Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet(); const auto zoneSet = zoneWindow->ActiveZoneSet();
@@ -468,7 +529,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEndDifferentWindows) TEST_METHOD(MoveSizeEndDifferentWindows)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto window = Mocks::Window(); const auto window = Mocks::Window();
m_zoneWindow->MoveSizeEnter(window, true); m_zoneWindow->MoveSizeEnter(window, true);
@@ -481,7 +542,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEndWindowNotSet) TEST_METHOD(MoveSizeEndWindowNotSet)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = E_INVALIDARG; const auto expected = E_INVALIDARG;
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 }); const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
@@ -501,14 +562,14 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected, actual); Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet(); const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false); zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window); const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
} }
TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet) TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet()); Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0); m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@@ -526,7 +587,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet) TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet()); Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0); m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@@ -564,7 +625,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet) TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet)
{ {
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false); m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet()); Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window()); m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window());
@@ -650,5 +711,29 @@ namespace FancyZonesUnitTests
const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex; const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex;
Assert::AreEqual(expected, actual); Assert::AreEqual(expected, actual);
} }
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
int orginalWidth = 450;
int orginalHeight = 550;
SetWindowPos(window, nullptr, 150, 150, orginalWidth, orginalHeight, SWP_SHOWWINDOW);
SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX);
auto zone = MakeZone(RECT{ 50, 50, 300, 300 });
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_LEFT, true);
RECT inZoneRect;
GetWindowRect(window, &inZoneRect);
Assert::AreEqual(orginalWidth, (int)inZoneRect.right - (int) inZoneRect.left);
Assert::AreEqual(orginalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top);
}
}; };
} }

View File

@@ -147,6 +147,7 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu,
if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii)) if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
{ {
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
Trace::QueryContextMenuError(hr);
} }
else else
{ {
@@ -220,12 +221,12 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
HRESULT hr = E_FAIL; HRESULT hr = E_FAIL;
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
{ {
Trace::InvokedRet(hr); hr = HRESULT_FROM_WIN32(GetLastError());
return hr; return hr;
} }
if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0)) if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0))
{ {
Trace::InvokedRet(hr); hr = HRESULT_FROM_WIN32(GetLastError());
return hr; return hr;
} }
CAtlFile writePipe(hWritePipe); CAtlFile writePipe(hWritePipe);
@@ -277,12 +278,12 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
delete[] lpszCommandLine; delete[] lpszCommandLine;
if (!CloseHandle(processInformation.hProcess)) if (!CloseHandle(processInformation.hProcess))
{ {
Trace::InvokedRet(hr); hr = HRESULT_FROM_WIN32(GetLastError());
return hr; return hr;
} }
if (!CloseHandle(processInformation.hThread)) if (!CloseHandle(processInformation.hThread))
{ {
Trace::InvokedRet(hr); hr = HRESULT_FROM_WIN32(GetLastError());
return hr; return hr;
} }
@@ -322,7 +323,6 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
writePipe.Close(); writePipe.Close();
hr = S_OK; hr = S_OK;
Trace::InvokedRet(hr);
return hr; return hr;
} }

View File

@@ -47,3 +47,13 @@ void Trace::InvokedRet(_In_ HRESULT hr) noexcept
TraceLoggingHResult(hr), TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
} }
void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
{
TraceLoggingWrite(
g_hProvider,
"ImageResizer_QueryContextMenuError",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -8,4 +8,5 @@ public:
static void EnableImageResizer(_In_ bool enabled) noexcept; static void EnableImageResizer(_In_ bool enabled) noexcept;
static void Invoked() noexcept; static void Invoked() noexcept;
static void InvokedRet(_In_ HRESULT hr) noexcept; static void InvokedRet(_In_ HRESULT hr) noexcept;
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
}; };

View File

@@ -48,7 +48,7 @@ HRESULT CPowerRenameMenu::s_CreateInstance(_In_opt_ IUnknown*, _In_ REFIID riid,
HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject* pdtobj, HKEY) HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject* pdtobj, HKEY)
{ {
// Check if we have disabled ourselves // Check if we have disabled ourselves
if (!CSettings::GetEnabled()) if (!CSettingsInstance().GetEnabled())
return E_FAIL; return E_FAIL;
// Cache the data object to be used later // Cache the data object to be used later
@@ -60,11 +60,11 @@ HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObjec
HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirst, UINT, UINT uFlags) HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirst, UINT, UINT uFlags)
{ {
// Check if we have disabled ourselves // Check if we have disabled ourselves
if (!CSettings::GetEnabled()) if (!CSettingsInstance().GetEnabled())
return E_FAIL; return E_FAIL;
// Check if we should only be on the extended context menu // Check if we should only be on the extended context menu
if (CSettings::GetExtendedContextMenuOnly() && (!(uFlags & CMF_EXTENDEDVERBS))) if (CSettingsInstance().GetExtendedContextMenuOnly() && (!(uFlags & CMF_EXTENDEDVERBS)))
return E_FAIL; return E_FAIL;
HRESULT hr = E_UNEXPECTED; HRESULT hr = E_UNEXPECTED;
@@ -81,7 +81,7 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
mii.dwTypeData = (PWSTR)menuName; mii.dwTypeData = (PWSTR)menuName;
mii.fState = MFS_ENABLED; mii.fState = MFS_ENABLED;
if (CSettings::GetShowIconOnMenu()) if (CSettingsInstance().GetShowIconOnMenu())
{ {
HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0); HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0);
if (hIcon) if (hIcon)
@@ -113,7 +113,7 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
{ {
HRESULT hr = E_FAIL; HRESULT hr = E_FAIL;
if (CSettings::GetEnabled() && if (CSettingsInstance().GetEnabled() &&
(IS_INTRESOURCE(pici->lpVerb)) && (IS_INTRESOURCE(pici->lpVerb)) &&
(LOWORD(pici->lpVerb) == 0)) (LOWORD(pici->lpVerb) == 0))
{ {
@@ -203,7 +203,7 @@ HRESULT __stdcall CPowerRenameMenu::GetTitle(IShellItemArray* /*psiItemArray*/,
HRESULT __stdcall CPowerRenameMenu::GetIcon(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszIcon) HRESULT __stdcall CPowerRenameMenu::GetIcon(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszIcon)
{ {
if (!CSettings::GetShowIconOnMenu()) if (!CSettingsInstance().GetShowIconOnMenu())
{ {
*ppszIcon = nullptr; *ppszIcon = nullptr;
return E_NOTIMPL; return E_NOTIMPL;
@@ -229,7 +229,7 @@ HRESULT __stdcall CPowerRenameMenu::GetCanonicalName(GUID* pguidCommandName)
HRESULT __stdcall CPowerRenameMenu::GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState) HRESULT __stdcall CPowerRenameMenu::GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState)
{ {
*pCmdState = CSettings::GetEnabled() ? ECS_ENABLED : ECS_HIDDEN; *pCmdState = CSettingsInstance().GetEnabled() ? ECS_ENABLED : ECS_HIDDEN;
return S_OK; return S_OK;
} }

View File

@@ -162,7 +162,7 @@
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>PowerRenameExt.def</ModuleDefinitionFile> <ModuleDefinitionFile>PowerRenameExt.def</ModuleDefinitionFile>
<DelayLoadDLLs>gdi32.dll;advapi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs> <DelayLoadDLLs>gdi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>

View File

@@ -207,17 +207,17 @@ public:
settings.add_bool_toogle( settings.add_bool_toogle(
L"bool_persist_input", L"bool_persist_input",
GET_RESOURCE_STRING(IDS_RESTORE_SEARCH), GET_RESOURCE_STRING(IDS_RESTORE_SEARCH),
CSettings::GetPersistState()); CSettingsInstance().GetPersistState());
settings.add_bool_toogle( settings.add_bool_toogle(
L"bool_mru_enabled", L"bool_mru_enabled",
GET_RESOURCE_STRING(IDS_ENABLE_AUTO), GET_RESOURCE_STRING(IDS_ENABLE_AUTO),
CSettings::GetMRUEnabled()); CSettingsInstance().GetMRUEnabled());
settings.add_int_spinner( settings.add_int_spinner(
L"int_max_mru_size", L"int_max_mru_size",
GET_RESOURCE_STRING(IDS_MAX_ITEMS), GET_RESOURCE_STRING(IDS_MAX_ITEMS),
CSettings::GetMaxMRUSize(), CSettingsInstance().GetMaxMRUSize(),
0, 0,
20, 20,
1); 1);
@@ -225,12 +225,12 @@ public:
settings.add_bool_toogle( settings.add_bool_toogle(
L"bool_show_icon_on_menu", L"bool_show_icon_on_menu",
GET_RESOURCE_STRING(IDS_ICON_CONTEXT_MENU), GET_RESOURCE_STRING(IDS_ICON_CONTEXT_MENU),
CSettings::GetShowIconOnMenu()); CSettingsInstance().GetShowIconOnMenu());
settings.add_bool_toogle( settings.add_bool_toogle(
L"bool_show_extended_menu", L"bool_show_extended_menu",
GET_RESOURCE_STRING(IDS_EXTENDED_MENU_INFO), GET_RESOURCE_STRING(IDS_EXTENDED_MENU_INFO),
CSettings::GetExtendedContextMenuOnly()); CSettingsInstance().GetExtendedContextMenuOnly());
return settings.serialize_to_buffer(buffer, buffer_size); return settings.serialize_to_buffer(buffer, buffer_size);
} }
@@ -245,13 +245,12 @@ public:
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::from_json_string(config); PowerToysSettings::PowerToyValues::from_json_string(config);
CSettings::SetPersistState(values.get_bool_value(L"bool_persist_input").value()); CSettingsInstance().SetPersistState(values.get_bool_value(L"bool_persist_input").value());
CSettings::SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value()); CSettingsInstance().SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value());
CSettings::SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value()); CSettingsInstance().SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value());
CSettings::SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value()); CSettingsInstance().SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value());
CSettings::SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value()); CSettingsInstance().SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value());
CSettingsInstance().SavePowerRenameData();
values.save_to_settings_file();
Trace::SettingsChanged(); Trace::SettingsChanged();
} }
@@ -284,13 +283,15 @@ public:
void init_settings() void init_settings()
{ {
m_enabled = CSettings::GetEnabled(); CSettingsInstance().LoadPowerRenameData();
m_enabled = CSettingsInstance().GetEnabled();
Trace::EnablePowerRename(m_enabled); Trace::EnablePowerRename(m_enabled);
} }
void save_settings() void save_settings()
{ {
CSettings::SetEnabled(m_enabled); CSettingsInstance().SetEnabled(m_enabled);
CSettingsInstance().SavePowerRenameData();
Trace::EnablePowerRename(m_enabled); Trace::EnablePowerRename(m_enabled);
} }

View File

@@ -85,13 +85,14 @@
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel> <WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<LanguageStandard>stdcpp17</LanguageStandard> <LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@@ -99,7 +100,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel> <WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -107,6 +108,7 @@
<LanguageStandard>stdcpp17</LanguageStandard> <LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@@ -116,7 +118,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization> <Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
@@ -124,6 +126,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<LanguageStandard>stdcpp17</LanguageStandard> <LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
@@ -134,7 +137,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization> <Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
@@ -143,6 +146,7 @@
<LanguageStandard>stdcpp17</LanguageStandard> <LanguageStandard>stdcpp17</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>

View File

@@ -1,171 +1,70 @@
#include "stdafx.h" #include "stdafx.h"
#include <commctrl.h>
#include "Settings.h" #include "Settings.h"
#include "PowerRenameInterfaces.h" #include "PowerRenameInterfaces.h"
#include "settings_helpers.h"
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename"; #include <filesystem>
const wchar_t c_mruSearchRegPath[] = L"SearchMRU"; #include <commctrl.h>
const wchar_t c_mruReplaceRegPath[] = L"ReplaceMRU";
const wchar_t c_enabled[] = L"Enabled"; namespace
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
const wchar_t c_persistState[] = L"PersistState";
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
const wchar_t c_flags[] = L"Flags";
const wchar_t c_searchText[] = L"SearchText";
const wchar_t c_replaceText[] = L"ReplaceText";
const wchar_t c_mruEnabled[] = L"MRUEnabled";
const bool c_enabledDefault = true;
const bool c_showIconOnMenuDefault = true;
const bool c_extendedContextMenuOnlyDefaut = false;
const bool c_persistStateDefault = true;
const bool c_mruEnabledDefault = true;
const DWORD c_maxMRUSizeDefault = 10;
const DWORD c_flagsDefault = 0;
bool CSettings::GetEnabled()
{ {
return GetRegBoolValue(c_enabled, c_enabledDefault); const wchar_t c_powerRenameDataFilePath[] = L"power-rename-settings.json";
}
bool CSettings::SetEnabled(_In_ bool enabled) const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
{ const wchar_t c_mruSearchRegPath[] = L"SearchMRU";
return SetRegBoolValue(c_enabled, enabled); const wchar_t c_mruReplaceRegPath[] = L"ReplaceMRU";
}
bool CSettings::GetShowIconOnMenu() const wchar_t c_enabled[] = L"Enabled";
{ const wchar_t c_showIconOnMenu[] = L"ShowIcon";
return GetRegBoolValue(c_showIconOnMenu, c_showIconOnMenuDefault); const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
} const wchar_t c_persistState[] = L"PersistState";
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
const wchar_t c_flags[] = L"Flags";
const wchar_t c_searchText[] = L"SearchText";
const wchar_t c_replaceText[] = L"ReplaceText";
const wchar_t c_mruEnabled[] = L"MRUEnabled";
bool CSettings::SetShowIconOnMenu(_In_ bool show) long GetRegNumber(const std::wstring& valueName, long defaultValue)
{ {
return SetRegBoolValue(c_showIconOnMenu, show);
}
bool CSettings::GetExtendedContextMenuOnly()
{
return GetRegBoolValue(c_extendedContextMenuOnly, c_extendedContextMenuOnlyDefaut);
}
bool CSettings::SetExtendedContextMenuOnly(_In_ bool extendedOnly)
{
return SetRegBoolValue(c_extendedContextMenuOnly, extendedOnly);
}
bool CSettings::GetPersistState()
{
return GetRegBoolValue(c_persistState, c_persistStateDefault);
}
bool CSettings::SetPersistState(_In_ bool persistState)
{
return SetRegBoolValue(c_persistState, persistState);
}
bool CSettings::GetMRUEnabled()
{
return GetRegBoolValue(c_mruEnabled, c_mruEnabledDefault);
}
bool CSettings::SetMRUEnabled(_In_ bool enabled)
{
return SetRegBoolValue(c_mruEnabled, enabled);
}
DWORD CSettings::GetMaxMRUSize()
{
return GetRegDWORDValue(c_maxMRUSize, c_maxMRUSizeDefault);
}
bool CSettings::SetMaxMRUSize(_In_ DWORD maxMRUSize)
{
return SetRegDWORDValue(c_maxMRUSize, maxMRUSize);
}
DWORD CSettings::GetFlags()
{
return GetRegDWORDValue(c_flags, c_flagsDefault);
}
bool CSettings::SetFlags(_In_ DWORD flags)
{
return SetRegDWORDValue(c_flags, flags);
}
bool CSettings::GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
{
return GetRegStringValue(c_searchText, text, cchBuf);
}
bool CSettings::SetSearchText(_In_ PCWSTR text)
{
return SetRegStringValue(c_searchText, text);
}
bool CSettings::GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
{
return GetRegStringValue(c_replaceText, text, cchBuf);
}
bool CSettings::SetReplaceText(_In_ PCWSTR text)
{
return SetRegStringValue(c_replaceText, text);
}
bool CSettings::SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value)
{
DWORD dwValue = value ? 1 : 0;
return SetRegDWORDValue(valueName, dwValue);
}
bool CSettings::GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue)
{
DWORD value = GetRegDWORDValue(valueName, (defaultValue == 0) ? false : true);
return (value == 0) ? false : true;
}
bool CSettings::SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value)
{
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_DWORD, &value, sizeof(value)))));
}
DWORD CSettings::GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue)
{
DWORD retVal = defaultValue;
DWORD type = REG_DWORD; DWORD type = REG_DWORD;
DWORD dwEnabled = 0; DWORD data = 0;
DWORD cb = sizeof(dwEnabled); DWORD size = sizeof(DWORD);
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, &dwEnabled, &cb) == ERROR_SUCCESS) if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, &data, &size) == ERROR_SUCCESS)
{ {
retVal = dwEnabled; return data;
}
return defaultValue;
} }
return retVal; void SetRegNumber(const std::wstring& valueName, long value)
}
bool CSettings::SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value)
{
ULONG cb = (DWORD)((wcslen(value) + 1) * sizeof(*value));
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_SZ, (const BYTE*)value, cb))));
}
bool CSettings::GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf)
{
if (cchBuf > 0)
{ {
SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), REG_DWORD, &value, sizeof(value));
}
bool GetRegBoolean(const std::wstring& valueName, bool defaultValue)
{
DWORD value = GetRegNumber(valueName.c_str(), defaultValue ? 1 : 0);
return (value == 0) ? false : true;
}
void SetRegBoolean(const std::wstring& valueName, bool value)
{
SetRegNumber(valueName, value ? 1 : 0);
}
std::wstring GetRegString(const std::wstring& valueName) {
wchar_t value[CSettings::MAX_INPUT_STRING_LEN];
value[0] = L'\0'; value[0] = L'\0';
}
DWORD type = REG_SZ; DWORD type = REG_SZ;
ULONG cb = cchBuf * sizeof(*value); DWORD size = CSettings::MAX_INPUT_STRING_LEN * sizeof(wchar_t);
return (SUCCEEDED(HRESULT_FROM_WIN32(SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, value, &cb) == ERROR_SUCCESS))); if (SUCCEEDED(HRESULT_FROM_WIN32(SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, value, &size) == ERROR_SUCCESS)))
{
return std::wstring(value);
}
return std::wstring{};
}
} }
typedef int (CALLBACK* MRUCMPPROC)(LPCWSTR, LPCWSTR); typedef int (CALLBACK* MRUCMPPROC)(LPCWSTR, LPCWSTR);
typedef struct { typedef struct {
@@ -470,12 +369,121 @@ void CRenameMRU::_FreeMRUList()
} }
} }
CSettings::CSettings()
{
std::wstring result = PTSettingsHelper::get_module_save_folder_location(L"PowerRename");
jsonFilePath = result + L"\\" + std::wstring(c_powerRenameDataFilePath);
}
bool CSettings::GetEnabled()
{
return GetRegBoolean(c_enabled, true);
}
void CSettings::SetEnabled(bool enabled)
{
SetRegBoolean(c_enabled, enabled);
}
void CSettings::LoadPowerRenameData()
{
if (!std::filesystem::exists(jsonFilePath))
{
MigrateSettingsFromRegistry();
SavePowerRenameData();
}
else
{
ParseJsonSettings();
}
}
void CSettings::SavePowerRenameData() const
{
json::JsonObject jsonData;
jsonData.SetNamedValue(c_showIconOnMenu, json::value(settings.showIconOnMenu));
jsonData.SetNamedValue(c_extendedContextMenuOnly, json::value(settings.extendedContextMenuOnly));
jsonData.SetNamedValue(c_persistState, json::value(settings.persistState));
jsonData.SetNamedValue(c_mruEnabled, json::value(settings.MRUEnabled));
jsonData.SetNamedValue(c_maxMRUSize, json::value(settings.maxMRUSize));
jsonData.SetNamedValue(c_flags, json::value(settings.flags));
jsonData.SetNamedValue(c_searchText, json::value(settings.searchText));
jsonData.SetNamedValue(c_replaceText, json::value(settings.replaceText));
json::to_file(jsonFilePath, jsonData);
}
void CSettings::MigrateSettingsFromRegistry()
{
settings.showIconOnMenu = GetRegBoolean(c_showIconOnMenu, true);
settings.extendedContextMenuOnly = GetRegBoolean(c_extendedContextMenuOnly, false); // Disabled by default.
settings.persistState = GetRegBoolean(c_persistState, true);
settings.MRUEnabled = GetRegBoolean(c_mruEnabled, true);
settings.maxMRUSize = GetRegNumber(c_maxMRUSize, 10);
settings.flags = GetRegNumber(c_flags, 0);
settings.searchText = GetRegString(c_searchText);
settings.replaceText = GetRegString(c_replaceText);
}
void CSettings::ParseJsonSettings()
{
auto json = json::from_file(jsonFilePath);
if (json)
{
const json::JsonObject& jsonSettings = json.value();
try
{
if (json::has(jsonSettings, c_showIconOnMenu, json::JsonValueType::Boolean))
{
settings.showIconOnMenu = jsonSettings.GetNamedBoolean(c_showIconOnMenu);
}
if (json::has(jsonSettings, c_extendedContextMenuOnly, json::JsonValueType::Boolean))
{
settings.extendedContextMenuOnly = jsonSettings.GetNamedBoolean(c_extendedContextMenuOnly);
}
if (json::has(jsonSettings, c_persistState, json::JsonValueType::Boolean))
{
settings.persistState = jsonSettings.GetNamedBoolean(c_persistState);
}
if (json::has(jsonSettings, c_mruEnabled, json::JsonValueType::Boolean))
{
settings.MRUEnabled = jsonSettings.GetNamedBoolean(c_mruEnabled);
}
if (json::has(jsonSettings, c_maxMRUSize, json::JsonValueType::Number))
{
settings.maxMRUSize = (long)jsonSettings.GetNamedNumber(c_maxMRUSize);
}
if (json::has(jsonSettings, c_flags, json::JsonValueType::Number))
{
settings.flags = (long)jsonSettings.GetNamedNumber(c_flags);
}
if (json::has(jsonSettings, c_searchText, json::JsonValueType::String))
{
settings.searchText = jsonSettings.GetNamedString(c_searchText);
}
if (json::has(jsonSettings, c_replaceText, json::JsonValueType::String))
{
settings.replaceText = jsonSettings.GetNamedString(c_replaceText);
}
}
catch (const winrt::hresult_error&) { }
}
}
CSettings& CSettingsInstance()
{
static CSettings instance;
return instance;
}
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk) HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk)
{ {
return CRenameMRU::CreateInstance(c_mruSearchRegPath, CSettings::GetMaxMRUSize(), ppUnk); return CRenameMRU::CreateInstance(c_mruSearchRegPath, CSettingsInstance().GetMaxMRUSize(), ppUnk);
} }
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk) HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk)
{ {
return CRenameMRU::CreateInstance(c_mruReplaceRegPath, CSettings::GetMaxMRUSize(), ppUnk); return CRenameMRU::CreateInstance(c_mruReplaceRegPath, CSettingsInstance().GetMaxMRUSize(), ppUnk);
} }

View File

@@ -1,45 +1,124 @@
#pragma once #pragma once
#include "json.h"
#include <string>
class CSettings class CSettings
{ {
public: public:
static const int MAX_INPUT_STRING_LEN = 1024; static const int MAX_INPUT_STRING_LEN = 1024;
static bool GetEnabled(); CSettings();
static bool SetEnabled(_In_ bool enabled);
static bool GetShowIconOnMenu(); bool GetEnabled();
static bool SetShowIconOnMenu(_In_ bool show);
static bool GetExtendedContextMenuOnly(); void SetEnabled(bool enabled);
static bool SetExtendedContextMenuOnly(_In_ bool extendedOnly);
static bool GetPersistState(); inline bool GetShowIconOnMenu() const
static bool SetPersistState(_In_ bool extendedOnly); {
return settings.showIconOnMenu;
}
static bool GetMRUEnabled(); inline void SetShowIconOnMenu(bool show)
static bool SetMRUEnabled(_In_ bool enabled); {
settings.showIconOnMenu = show;
}
static DWORD GetMaxMRUSize(); inline bool GetExtendedContextMenuOnly() const
static bool SetMaxMRUSize(_In_ DWORD maxMRUSize); {
return settings.extendedContextMenuOnly;
}
static DWORD GetFlags(); inline void SetExtendedContextMenuOnly(bool extendedOnly)
static bool SetFlags(_In_ DWORD flags); {
settings.extendedContextMenuOnly = extendedOnly;
}
static bool GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf); inline bool GetPersistState() const
static bool SetSearchText(_In_ PCWSTR text); {
return settings.persistState;
}
static bool GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf); inline void SetPersistState(bool persistState)
static bool SetReplaceText(_In_ PCWSTR text); {
settings.persistState = persistState;
}
inline bool GetMRUEnabled() const
{
return settings.MRUEnabled;
}
inline void SetMRUEnabled(bool MRUEnabled)
{
settings.MRUEnabled = MRUEnabled;
}
inline long GetMaxMRUSize() const
{
return settings.maxMRUSize;
}
inline void SetMaxMRUSize(long maxMRUSize)
{
settings.maxMRUSize = maxMRUSize;
}
inline long GetFlags() const
{
return settings.flags;
}
inline void SetFlags(long flags)
{
settings.flags = flags;
}
inline const std::wstring& GetSearchText() const
{
return settings.searchText;
}
inline void SetSearchText(const std::wstring& text)
{
settings.searchText = text;
}
inline const std::wstring& GetReplaceText() const
{
return settings.replaceText;
}
inline void SetReplaceText(const std::wstring& text)
{
settings.replaceText = text;
}
void LoadPowerRenameData();
void SavePowerRenameData() const;
private: private:
static bool GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue); struct Settings
static bool SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value); {
static bool SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value); bool showIconOnMenu{ true };
static DWORD GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue); bool extendedContextMenuOnly{ false }; // Disabled by default.
static bool SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value); bool persistState{ true };
static bool GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf); bool MRUEnabled{ true };
long maxMRUSize{ 10 };
long flags{ 0 };
std::wstring searchText{};
std::wstring replaceText{};
};
void MigrateSettingsFromRegistry();
void ParseJsonSettings();
Settings settings;
std::wstring jsonFilePath;
}; };
CSettings& CSettingsInstance();
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk); HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk);
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk); HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk);

View File

@@ -19,3 +19,5 @@
#include <shlwapi.h> #include <shlwapi.h>
#include <ProjectTelemetry.h> #include <ProjectTelemetry.h>
#pragma comment(lib, "windowsapp")

View File

@@ -79,11 +79,11 @@ void Trace::SettingsChanged() noexcept
"PowerRename_SettingsChanged", "PowerRename_SettingsChanged",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(CSettings::GetEnabled(), "IsEnabled"), TraceLoggingBoolean(CSettingsInstance().GetEnabled(), "IsEnabled"),
TraceLoggingBoolean(CSettings::GetShowIconOnMenu(), "ShowIconOnMenu"), TraceLoggingBoolean(CSettingsInstance().GetShowIconOnMenu(), "ShowIconOnMenu"),
TraceLoggingBoolean(CSettings::GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"), TraceLoggingBoolean(CSettingsInstance().GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
TraceLoggingBoolean(CSettings::GetPersistState(), "PersistState"), TraceLoggingBoolean(CSettingsInstance().GetPersistState(), "PersistState"),
TraceLoggingBoolean(CSettings::GetMRUEnabled(), "IsMRUEnabled"), TraceLoggingBoolean(CSettingsInstance().GetMRUEnabled(), "IsMRUEnabled"),
TraceLoggingUInt64(CSettings::GetMaxMRUSize(), "MaxMRUSize"), TraceLoggingUInt64(CSettingsInstance().GetMaxMRUSize(), "MaxMRUSize"),
TraceLoggingUInt64(CSettings::GetFlags(), "Flags")); TraceLoggingUInt64(CSettingsInstance().GetFlags(), "Flags"));
} }

View File

@@ -308,7 +308,7 @@ HRESULT CPowerRenameUI::_Initialize(_In_ IPowerRenameManager* psrm, _In_opt_ IUn
HRESULT CPowerRenameUI::_InitAutoComplete() HRESULT CPowerRenameUI::_InitAutoComplete()
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
if (CSettings::GetMRUEnabled()) if (CSettingsInstance().GetMRUEnabled())
{ {
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spSearchAC)); hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spSearchAC));
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
@@ -387,19 +387,13 @@ HRESULT CPowerRenameUI::_ReadSettings()
// Check if we should read flags from settings // Check if we should read flags from settings
// or the defaults from the manager. // or the defaults from the manager.
DWORD flags = 0; DWORD flags = 0;
if (CSettings::GetPersistState()) if (CSettingsInstance().GetPersistState())
{ {
flags = CSettings::GetFlags(); flags = CSettingsInstance().GetFlags();
m_spsrm->put_flags(flags); m_spsrm->put_flags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN]; SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, CSettingsInstance().GetSearchText().c_str());
buffer[0] = L'\0'; SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, CSettingsInstance().GetReplaceText().c_str());
CSettings::GetSearchText(buffer, ARRAYSIZE(buffer));
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer);
buffer[0] = L'\0';
CSettings::GetReplaceText(buffer, ARRAYSIZE(buffer));
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer);
} }
else else
{ {
@@ -414,18 +408,18 @@ HRESULT CPowerRenameUI::_ReadSettings()
HRESULT CPowerRenameUI::_WriteSettings() HRESULT CPowerRenameUI::_WriteSettings()
{ {
// Check if we should store our settings // Check if we should store our settings
if (CSettings::GetPersistState()) if (CSettingsInstance().GetPersistState())
{ {
DWORD flags = 0; DWORD flags = 0;
m_spsrm->get_flags(&flags); m_spsrm->get_flags(&flags);
CSettings::SetFlags(flags); CSettingsInstance().SetFlags(flags);
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN]; wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
buffer[0] = L'\0'; buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer, ARRAYSIZE(buffer)); GetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer, ARRAYSIZE(buffer));
CSettings::SetSearchText(buffer); CSettingsInstance().SetSearchText(buffer);
if (CSettings::GetMRUEnabled() && m_spSearchACL) if (CSettingsInstance().GetMRUEnabled() && m_spSearchACL)
{ {
CComPtr<IPowerRenameMRU> spSearchMRU; CComPtr<IPowerRenameMRU> spSearchMRU;
if (SUCCEEDED(m_spSearchACL->QueryInterface(IID_PPV_ARGS(&spSearchMRU)))) if (SUCCEEDED(m_spSearchACL->QueryInterface(IID_PPV_ARGS(&spSearchMRU))))
@@ -436,9 +430,9 @@ HRESULT CPowerRenameUI::_WriteSettings()
buffer[0] = L'\0'; buffer[0] = L'\0';
GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer)); GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer));
CSettings::SetReplaceText(buffer); CSettingsInstance().SetReplaceText(buffer);
if (CSettings::GetMRUEnabled() && m_spReplaceACL) if (CSettingsInstance().GetMRUEnabled() && m_spReplaceACL)
{ {
CComPtr<IPowerRenameMRU> spReplaceMRU; CComPtr<IPowerRenameMRU> spReplaceMRU;
if (SUCCEEDED(m_spReplaceACL->QueryInterface(IID_PPV_ARGS(&spReplaceMRU)))) if (SUCCEEDED(m_spReplaceACL->QueryInterface(IID_PPV_ARGS(&spReplaceMRU))))

View File

@@ -81,7 +81,7 @@
<IntDir>$(Platform)\$(Configuration)\</IntDir> <IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<IncludePath>..\lib\;$(IncludePath)</IncludePath> <IncludePath>..\lib\;$(IncludePath)</IncludePath>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir> <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir> <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>

View File

@@ -87,7 +87,6 @@
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir> <OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir> <IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation // Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ // See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
@@ -594,9 +594,223 @@ namespace WindowWalker.Components
} }
} }
/// <summary>
/// GetWindow relationship between the specified window and the window whose handle is to be retrieved.
/// </summary>
public enum GetWindowCmd : uint
{
/// <summary>
/// The retrieved handle identifies the window of the same type that is highest in the Z order.
/// </summary>
GW_HWNDFIRST = 0,
/// <summary>
/// The retrieved handle identifies the window of the same type that is lowest in the Z order.
/// </summary>
GW_HWNDLAST = 1,
/// <summary>
/// The retrieved handle identifies the window below the specified window in the Z order.
/// </summary>
GW_HWNDNEXT = 2,
/// <summary>
/// The retrieved handle identifies the window above the specified window in the Z order.
/// </summary>
GW_HWNDPREV = 3,
/// <summary>
/// The retrieved handle identifies the specified window's owner window, if any.
/// </summary>
GW_OWNER = 4,
/// <summary>
/// The retrieved handle identifies the child window at the top of the Z order, if the specified window
/// is a parent window.
/// </summary>
GW_CHILD = 5,
/// <summary>
/// The retrieved handle identifies the enabled popup window owned by the specified window.
/// </summary>
GW_ENABLEDPOPUP = 6,
}
/// <summary>
/// GetWindowLong index to retrieves the extended window styles.
/// </summary>
#pragma warning disable SA1310 // Field names should not contain underscore
public const int GWL_EXSTYLE = -20;
#pragma warning restore SA1310 // Field names should not contain underscore
/// <summary>
/// The following are the extended window styles
/// </summary>
[Flags]
public enum ExtendedWindowStyles : uint
{
/// <summary>
/// The window has a double border; the window can, optionally, be created with a title bar by specifying
/// the WS_CAPTION style in the dwStyle parameter.
/// </summary>
WS_EX_DLGMODALFRAME = 0X0001,
/// <summary>
/// The child window created with this style does not send the WM_PARENTNOTIFY message to its parent window
/// when it is created or destroyed.
/// </summary>
WS_EX_NOPARENTNOTIFY = 0X0004,
/// <summary>
/// The window should be placed above all non-topmost windows and should stay above all non-topmost windows
/// and should stay above them, even when the window is deactivated.
/// </summary>
WS_EX_TOPMOST = 0X0008,
/// <summary>
/// The window accepts drag-drop files.
/// </summary>
WS_EX_ACCEPTFILES = 0x0010,
/// <summary>
/// The window should not be painted until siblings beneath the window (that were created by the same thread)
/// have been painted.
/// </summary>
WS_EX_TRANSPARENT = 0x0020,
/// <summary>
/// The window is a MDI child window.
/// </summary>
WS_EX_MDICHILD = 0x0040,
/// <summary>
/// The window is intended to be used as a floating toolbar. A tool window has a title bar that is shorter
/// than a normal title bar, and the window title is drawn using a smaller font. A tool window does not
/// appear in the taskbar or in the dialog that appears when the user presses ALT+TAB.
/// </summary>
WS_EX_TOOLWINDOW = 0x0080,
/// <summary>
/// The window has a border with a raised edge.
/// </summary>
WS_EX_WINDOWEDGE = 0x0100,
/// <summary>
/// The window has a border with a sunken edge.
/// </summary>
WS_EX_CLIENTEDGE = 0x0200,
/// <summary>
/// The title bar of the window includes a question mark.
/// </summary>
WS_EX_CONTEXTHELP = 0x0400,
/// <summary>
/// The window has generic "right-aligned" properties. This depends on the window class. This style has
/// an effect only if the shell language supports reading-order alignment, otherwise is ignored.
/// </summary>
WS_EX_RIGHT = 0x1000,
/// <summary>
/// The window has generic left-aligned properties. This is the default.
/// </summary>
WS_EX_LEFT = 0x0,
/// <summary>
/// If the shell language supports reading-order alignment, the window text is displayed using right-to-left
/// reading-order properties. For other languages, the styles is ignored.
/// </summary>
WS_EX_RTLREADING = 0x2000,
/// <summary>
/// The window text is displayed using left-to-right reading-order properties. This is the default.
/// </summary>
WS_EX_LTRREADING = 0x0,
/// <summary>
/// If the shell language supports reading order alignment, the vertical scroll bar (if present) is to
/// the left of the client area. For other languages, the style is ignored.
/// </summary>
WS_EX_LEFTSCROLLBAR = 0x4000,
/// <summary>
/// The vertical scroll bar (if present) is to the right of the client area. This is the default.
/// </summary>
WS_EX_RIGHTSCROLLBAR = 0x0,
/// <summary>
/// The window itself contains child windows that should take part in dialog box, navigation. If this
/// style is specified, the dialog manager recurses into children of this window when performing
/// navigation operations such as handling tha TAB key, an arrow key, or a keyboard mnemonic.
/// </summary>
WS_EX_CONTROLPARENT = 0x10000,
/// <summary>
/// The window has a three-dimensional border style intended to be used for items that do not accept
/// user input.
/// </summary>
WS_EX_STATICEDGE = 0x20000,
/// <summary>
/// Forces a top-level window onto the taskbar when the window is visible.
/// </summary>
WS_EX_APPWINDOW = 0x40000,
/// <summary>
/// The window is an overlapped window.
/// </summary>
WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
/// <summary>
/// The window is palette window, which is a modeless dialog box that presents an array of commands.
/// </summary>
WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
/// <summary>
/// The window is a layered window. This style cannot be used if the window has a class style of either
/// CS_OWNDC or CS_CLASSDC. Only for top level window before Windows 8, and child windows from Windows 8.
/// </summary>
WS_EX_LAYERED = 0x80000,
/// <summary>
/// The window does not pass its window layout to its child windows.
/// </summary>
WS_EX_NOINHERITLAYOUT = 0x100000,
/// <summary>
/// If the shell language supports reading order alignment, the horizontal origin of the window is on the
/// right edge. Increasing horizontal values advance to the left.
/// </summary>
WS_EX_LAYOUTRTL = 0x400000,
/// <summary>
/// Paints all descendants of a window in bottom-to-top painting order using double-buffering.
/// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and
/// transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT
/// bit set. Double-buffering allows the window and its descendents to be painted without flicker.
/// </summary>
WS_EX_COMPOSITED = 0x2000000,
/// <summary>
/// A top-level window created with this style does not become the foreground window when the user
/// clicks it. The system does not bring this window to the foreground when the user minimizes or closes
/// the foreground window.
/// </summary>
WS_EX_NOACTIVATE = 0x8000000,
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)] [DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int EnumWindows(CallBackPtr callPtr, int lPar); public static extern int EnumWindows(CallBackPtr callPtr, int lPar);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindow(IntPtr hWnd, GetWindowCmd uCmd);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int EnumChildWindows(IntPtr hWnd, CallBackPtr callPtr, int lPar);
[DllImport("user32.dll", CharSet = CharSet.Unicode)] [DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount); public static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
@@ -606,6 +820,9 @@ namespace WindowWalker.Components
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hWnd); public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")] [DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags); public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags uFlags);
@@ -633,6 +850,9 @@ namespace WindowWalker.Components
[DllImport("psapi.dll")] [DllImport("psapi.dll")]
public static extern uint GetProcessImageFileName(IntPtr hProcess, [Out] StringBuilder lpImageFileName, [In] [MarshalAs(UnmanagedType.U4)] int nSize); public static extern uint GetProcessImageFileName(IntPtr hProcess, [Out] StringBuilder lpImageFileName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetProp(IntPtr hWnd, string lpString);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId); public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
@@ -642,6 +862,9 @@ namespace WindowWalker.Components
[DllImport("dwmapi.dll", PreserveSig = false)] [DllImport("dwmapi.dll", PreserveSig = false)]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
[DllImport("dwmapi.dll", PreserveSig = false)]
public static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out int pvAttribute, int cbAttribute);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

View File

@@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace WindowWalker.Components namespace WindowWalker.Components
@@ -19,11 +18,15 @@ namespace WindowWalker.Components
/// </summary> /// </summary>
public delegate void OpenWindowsUpdateHandler(object sender, SearchController.SearchResultUpdateEventArgs e); public delegate void OpenWindowsUpdateHandler(object sender, SearchController.SearchResultUpdateEventArgs e);
#pragma warning disable 0067 // suppress false positive
/// <summary> /// <summary>
/// Event raised when there is an update to the list of open windows /// Event raised when there is an update to the list of open windows
/// </summary> /// </summary>
public event OpenWindowsUpdateHandler OnOpenWindowsUpdate; public event OpenWindowsUpdateHandler OnOpenWindowsUpdate;
#pragma warning restore 0067
/// <summary> /// <summary>
/// List of all the open windows /// List of all the open windows
/// </summary> /// </summary>
@@ -94,22 +97,11 @@ namespace WindowWalker.Components
{ {
Window newWindow = new Window(hwnd); Window newWindow = new Window(hwnd);
if (windows.Select(x => x.Title).Contains(newWindow.Title)) if (newWindow.IsWindow && newWindow.Visible && newWindow.IsOwner &&
{ (!newWindow.IsToolWindow || newWindow.IsAppWindow ) && !newWindow.TaskListDeleted &&
if (newWindow.ProcessName.ToLower().Equals("applicationframehost.exe")) newWindow.ClassName != "Windows.UI.Core.CoreWindow")
{
windows.Remove(windows.Where(x => x.Title == newWindow.Title).First());
}
return true;
}
if ((newWindow.Visible && !newWindow.ProcessName.ToLower().Equals("iexplore.exe")) ||
(newWindow.ProcessName.ToLower().Equals("iexplore.exe") && newWindow.ClassName == "TabThumbnailWindow"))
{ {
windows.Add(newWindow); windows.Add(newWindow);
OnOpenWindowsUpdate?.Invoke(this, new SearchController.SearchResultUpdateEventArgs());
} }
return true; return true;

View File

@@ -8,6 +8,7 @@ using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
@@ -71,6 +72,8 @@ namespace WindowWalker.Components
get { return hwnd; } get { return hwnd; }
} }
public uint ProcessID { get; set; }
/// <summary> /// <summary>
/// Gets returns the name of the process /// Gets returns the name of the process
/// </summary> /// </summary>
@@ -88,11 +91,9 @@ namespace WindowWalker.Components
if (!_handlesToProcessCache.ContainsKey(Hwnd)) if (!_handlesToProcessCache.ContainsKey(Hwnd))
{ {
InteropAndHelpers.GetWindowThreadProcessId(Hwnd, out uint processId); var processName = GetProcessNameFromWindowHandle(Hwnd);
IntPtr processHandle = InteropAndHelpers.OpenProcess(InteropAndHelpers.ProcessAccessFlags.AllAccess, true, (int)processId);
StringBuilder processName = new StringBuilder(MaximumFileNameLength);
if (InteropAndHelpers.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0) if (processName.Length != 0)
{ {
_handlesToProcessCache.Add( _handlesToProcessCache.Add(
Hwnd, Hwnd,
@@ -104,6 +105,27 @@ namespace WindowWalker.Components
} }
} }
if (_handlesToProcessCache[hwnd].ToLower() == "applicationframehost.exe")
{
new Task(() =>
{
InteropAndHelpers.CallBackPtr callbackptr = new InteropAndHelpers.CallBackPtr((IntPtr hwnd, IntPtr lParam) =>
{
var childProcessId = GetProcessIDFromWindowHandle(hwnd);
if (childProcessId != ProcessID)
{
_handlesToProcessCache[Hwnd] = GetProcessNameFromWindowHandle(hwnd);
return false;
}
else
{
return true;
}
});
InteropAndHelpers.EnumChildWindows(Hwnd, callbackptr, 0);
}).Start();
}
return _handlesToProcessCache[hwnd]; return _handlesToProcessCache[hwnd];
} }
} }
@@ -168,6 +190,87 @@ namespace WindowWalker.Components
} }
} }
/// <summary>
/// Gets a value indicating whether the specified window handle identifies an existing window.
/// </summary>
public bool IsWindow
{
get
{
return InteropAndHelpers.IsWindow(Hwnd);
}
}
/// <summary>
/// Gets a value indicating whether a value is the window GWL_EX_STYLE is a toolwindow
/// </summary>
public bool IsToolWindow
{
get
{
return (InteropAndHelpers.GetWindowLong(Hwnd, InteropAndHelpers.GWL_EXSTYLE) &
(uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_TOOLWINDOW) ==
(uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_TOOLWINDOW;
}
}
/// <summary>
/// Gets a value indicating whether the window GWL_EX_STYLE is an appwindow
/// </summary>
public bool IsAppWindow
{
get
{
return (InteropAndHelpers.GetWindowLong(Hwnd, InteropAndHelpers.GWL_EXSTYLE) &
(uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_APPWINDOW) ==
(uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_APPWINDOW;
}
}
/// <summary>
/// Gets a value indicating whether the window has ITaskList_Deleted property
/// </summary>
public bool TaskListDeleted
{
get
{
return InteropAndHelpers.GetProp(Hwnd, "ITaskList_Deleted") != IntPtr.Zero;
}
}
/// <summary>
/// Gets a value indicating whether the app is a cloaked UWP app
/// </summary>
public bool IsUWPCloaked
{
get
{
return IsWindowCloaked() && ClassName == "ApplicationFrameWindow";
}
}
/// <summary>
/// Gets a value indicating whether the specified windows is the owner
/// </summary>
public bool IsOwner
{
get
{
return InteropAndHelpers.GetWindow(Hwnd, InteropAndHelpers.GetWindowCmd.GW_OWNER) != null;
}
}
/// <summary>
/// Gets a value indicating whether is the window cloaked. To detect UWP apps in background or win32 apps running in another virtual desktop
/// </summary>
public bool IsWindowCloaked()
{
int isCloaked = 0;
const int DWMWA_CLOAKED = 14;
InteropAndHelpers.DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, out isCloaked, sizeof(int));
return isCloaked != 0;
}
/// <summary> /// <summary>
/// Gets a value indicating whether returns true if the window is minimized /// Gets a value indicating whether returns true if the window is minimized
/// </summary> /// </summary>
@@ -261,5 +364,38 @@ namespace WindowWalker.Components
Maximized, Maximized,
Unknown, Unknown,
} }
/// <summary>
/// Gets the name of the process using the window handle
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>A string representing the process name or an empty string if the function fails</returns>
private string GetProcessNameFromWindowHandle(IntPtr hwnd)
{
uint processId = GetProcessIDFromWindowHandle(hwnd);
ProcessID = processId;
IntPtr processHandle = InteropAndHelpers.OpenProcess(InteropAndHelpers.ProcessAccessFlags.AllAccess, true, (int)processId);
StringBuilder processName = new StringBuilder(MaximumFileNameLength);
if (InteropAndHelpers.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0)
{
return processName.ToString().Split('\\').Reverse().ToArray()[0];
}
else
{
return string.Empty;
}
}
/// <summary>
/// Gets the process ID for the Window handle
/// </summary>
/// <param name="hwnd">The handle to the window</param>
/// <returns>The process ID</returns>
private uint GetProcessIDFromWindowHandle(IntPtr hwnd)
{
InteropAndHelpers.GetWindowThreadProcessId(hwnd, out uint processId);
return processId;
}
} }
} }

View File

@@ -117,7 +117,7 @@ namespace WindowWalker
private void Window_GotFocus(object sender, RoutedEventArgs e) private void Window_GotFocus(object sender, RoutedEventArgs e)
{ {
this.searchBox.Focus(); searchBox.Focus();
} }
} }
} }

View File

@@ -6,7 +6,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows.Interop; using System.Windows.Interop;
using Microsoft.Win32;
using WindowWalker.Components; using WindowWalker.Components;
using WindowWalker.MVVMHelpers; using WindowWalker.MVVMHelpers;
@@ -18,8 +18,6 @@ namespace WindowWalker.ViewModels
private readonly List<string> _hints = new List<string>() private readonly List<string> _hints = new List<string>()
{ {
"search for running processes or windows...", "search for running processes or windows...",
// "you can reinvoke this app using CTRL + WIN",
}; };
private string _searchText = string.Empty; private string _searchText = string.Empty;

View File

@@ -2437,6 +2437,16 @@
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
"dev": true "dev": true
}, },
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"block-stream": { "block-stream": {
"version": "0.0.9", "version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
@@ -2760,6 +2770,21 @@
"y18n": "^4.0.0" "y18n": "^4.0.0"
}, },
"dependencies": { "dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -3272,6 +3297,21 @@
"run-queue": "^1.0.0" "run-queue": "^1.0.0"
}, },
"dependencies": { "dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -4477,6 +4517,15 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -4552,6 +4601,13 @@
"schema-utils": "^1.0.0" "schema-utils": "^1.0.0"
} }
}, },
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
},
"filesize": { "filesize": {
"version": "3.6.1", "version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
@@ -4810,14 +4866,15 @@
"dev": true "dev": true
}, },
"fsevents": { "fsevents": {
"version": "1.2.9", "version": "1.2.12",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1", "nan": "^2.12.1",
"node-pre-gyp": "^0.12.0" "node-pre-gyp": "*"
}, },
"dependencies": { "dependencies": {
"abbrev": { "abbrev": {
@@ -4865,7 +4922,7 @@
} }
}, },
"chownr": { "chownr": {
"version": "1.1.1", "version": "1.1.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@@ -4895,7 +4952,7 @@
"optional": true "optional": true
}, },
"debug": { "debug": {
"version": "4.1.1", "version": "3.2.6",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -4922,12 +4979,12 @@
"optional": true "optional": true
}, },
"fs-minipass": { "fs-minipass": {
"version": "1.2.5", "version": "1.2.7",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"minipass": "^2.2.1" "minipass": "^2.6.0"
} }
}, },
"fs.realpath": { "fs.realpath": {
@@ -4953,7 +5010,7 @@
} }
}, },
"glob": { "glob": {
"version": "7.1.3", "version": "7.1.6",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -4982,7 +5039,7 @@
} }
}, },
"ignore-walk": { "ignore-walk": {
"version": "3.0.1", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -5001,7 +5058,7 @@
} }
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@@ -5037,13 +5094,11 @@
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "1.2.5",
"bundled": true, "bundled": true
"dev": true,
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.3.5", "version": "2.9.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -5053,48 +5108,47 @@
} }
}, },
"minizlib": { "minizlib": {
"version": "1.2.1", "version": "1.3.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"minipass": "^2.2.1" "minipass": "^2.9.0"
} }
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.5",
"bundled": true, "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"dev": true, "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "^1.2.5"
} }
}, },
"ms": { "ms": {
"version": "2.1.1", "version": "2.1.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"needle": { "needle": {
"version": "2.3.0", "version": "2.3.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"debug": "^4.1.0", "debug": "^3.2.6",
"iconv-lite": "^0.4.4", "iconv-lite": "^0.4.4",
"sax": "^1.2.4" "sax": "^1.2.4"
} }
}, },
"node-pre-gyp": { "node-pre-gyp": {
"version": "0.12.0", "version": "0.14.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"detect-libc": "^1.0.2", "detect-libc": "^1.0.2",
"mkdirp": "^0.5.1", "mkdirp": "0.5.5",
"needle": "^2.2.1", "needle": "^2.2.1",
"nopt": "^4.0.1", "nopt": "^4.0.1",
"npm-packlist": "^1.1.6", "npm-packlist": "^1.1.6",
@@ -5102,11 +5156,23 @@
"rc": "^1.2.7", "rc": "^1.2.7",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"semver": "^5.3.0", "semver": "^5.3.0",
"tar": "^4" "tar": "^4.4.2"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
}
} }
}, },
"nopt": { "nopt": {
"version": "4.0.1", "version": "4.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -5116,19 +5182,29 @@
} }
}, },
"npm-bundled": { "npm-bundled": {
"version": "1.0.6", "version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"npm-packlist": { "npm-packlist": {
"version": "1.4.1", "version": "1.4.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"ignore-walk": "^3.0.1", "ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1" "npm-bundled": "^1.0.1",
"npm-normalize-package-bin": "^1.0.1"
} }
}, },
"npmlog": { "npmlog": {
@@ -5193,7 +5269,7 @@
"optional": true "optional": true
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.0", "version": "2.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@@ -5208,18 +5284,10 @@
"ini": "~1.3.0", "ini": "~1.3.0",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"strip-json-comments": "~2.0.1" "strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true
}
} }
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "2.3.7",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -5234,7 +5302,7 @@
} }
}, },
"rimraf": { "rimraf": {
"version": "2.6.3", "version": "2.7.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@@ -5261,7 +5329,7 @@
"optional": true "optional": true
}, },
"semver": { "semver": {
"version": "5.7.0", "version": "5.7.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@@ -5314,18 +5382,30 @@
"optional": true "optional": true
}, },
"tar": { "tar": {
"version": "4.4.8", "version": "4.4.13",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"chownr": "^1.1.1", "chownr": "^1.1.1",
"fs-minipass": "^1.2.5", "fs-minipass": "^1.2.5",
"minipass": "^2.3.4", "minipass": "^2.8.6",
"minizlib": "^1.1.1", "minizlib": "^1.2.1",
"mkdirp": "^0.5.0", "mkdirp": "0.5.5",
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.2" "yallist": "^3.0.3"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
}
} }
}, },
"util-deprecate": { "util-deprecate": {
@@ -5350,7 +5430,7 @@
"optional": true "optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@@ -5369,6 +5449,21 @@
"rimraf": "2" "rimraf": "2"
}, },
"dependencies": { "dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -5609,15 +5704,16 @@
"dev": true "dev": true
}, },
"handlebars": { "handlebars": {
"version": "4.5.3", "version": "4.7.6",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"dev": true, "dev": true,
"requires": { "requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0", "neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1", "source-map": "^0.6.1",
"uglify-js": "^3.1.4" "uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
} }
}, },
"har-schema": { "har-schema": {
@@ -7098,6 +7194,21 @@
"semver": "^6.2.0" "semver": "^6.2.0"
}, },
"dependencies": { "dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"semver": { "semver": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -7124,6 +7235,23 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"slash": "^2.0.0", "slash": "^2.0.0",
"source-map": "^0.6.0" "source-map": "^0.6.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
} }
}, },
"jest-validate": { "jest-validate": {
@@ -8052,9 +8180,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "0.0.10", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true "dev": true
}, },
"minipass": { "minipass": {
@@ -8131,21 +8259,6 @@
} }
} }
}, },
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -8160,6 +8273,21 @@
"run-queue": "^1.0.3" "run-queue": "^1.0.3"
}, },
"dependencies": { "dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -8312,6 +8440,21 @@
"which": "1" "which": "1"
}, },
"dependencies": { "dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -8482,6 +8625,21 @@
"yallist": "^2.1.2" "yallist": "^2.1.2"
} }
}, },
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -8813,16 +8971,6 @@
"is-wsl": "^1.1.0" "is-wsl": "^1.1.0"
} }
}, },
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"dev": true,
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"optionator": { "optionator": {
"version": "0.8.3", "version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -9184,6 +9332,21 @@
"ms": "2.0.0" "ms": "2.0.0"
} }
}, },
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -9782,6 +9945,28 @@
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
"dev": true "dev": true
}, },
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
}
},
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -11415,6 +11600,19 @@
"dom-serializer": "0", "dom-serializer": "0",
"domelementtype": "1" "domelementtype": "1"
} }
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "^1.2.5"
}
} }
} }
}, },
@@ -11443,6 +11641,23 @@
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.3" "yallist": "^3.0.3"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
} }
}, },
"terser": { "terser": {
@@ -11494,7 +11709,24 @@
"ssri": "^6.0.1", "ssri": "^6.0.1",
"unique-filename": "^1.1.1", "unique-filename": "^1.1.1",
"y18n": "^4.0.0" "y18n": "^4.0.0"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
} }
}
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}, },
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
@@ -11727,6 +11959,21 @@
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
"dev": true "dev": true
}, },
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"yargs-parser": { "yargs-parser": {
"version": "10.1.0", "version": "10.1.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
@@ -11815,23 +12062,14 @@
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
"version": "3.7.3", "version": "3.8.1",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.3.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz",
"integrity": "sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==", "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"commander": "~2.20.3", "commander": "~2.20.3",
"source-map": "~0.6.1" "source-map": "~0.6.1"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"optional": true
}
} }
}, },
"undertaker": { "undertaker": {
@@ -12207,6 +12445,23 @@
"terser-webpack-plugin": "^1.1.0", "terser-webpack-plugin": "^1.1.0",
"watchpack": "^1.5.0", "watchpack": "^1.5.0",
"webpack-sources": "^1.3.0" "webpack-sources": "^1.3.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
} }
}, },
"webpack-cli": { "webpack-cli": {
@@ -12537,9 +12792,9 @@
"dev": true "dev": true
}, },
"wordwrap": { "wordwrap": {
"version": "0.0.3", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
"dev": true "dev": true
}, },
"worker-farm": { "worker-farm": {

View File

@@ -12,6 +12,7 @@
"typings": "lib/index.d.ts", "typings": "lib/index.d.ts",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"preinstall": "npx npm-force-resolutions",
"just": "just-scripts", "just": "just-scripts",
"clean": "rimraf build lib lib-commonjs && just-scripts clean", "clean": "rimraf build lib lib-commonjs && just-scripts clean",
"build": "rimraf build && just-scripts build --min --production && copy *.html build && react-snap && xcopy build\\* ..\\settings\\settings-html /sy", "build": "rimraf build && just-scripts build --min --production && copy *.html build && react-snap && xcopy build\\* ..\\settings\\settings-html /sy",
@@ -46,5 +47,8 @@
}, },
"just": { "just": {
"stack": "just-stack-uifabric" "stack": "just-stack-uifabric"
},
"resolutions": {
"mkdirp": "0.5.5"
} }
} }

View File

@@ -567,7 +567,7 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
Trace::RegisterProvider(); Trace::RegisterProvider();
CoInitialize(nullptr); CoInitialize(nullptr);
const bool should_try_drop_privileges = !initialize_com_security_policy_for_webview() && is_process_elevated(); const bool should_try_drop_privileges = !initialize_com_security_policy_for_webview() && is_process_elevated(false);
if (should_try_drop_privileges) if (should_try_drop_privileges)
{ {

View File

@@ -26,13 +26,18 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder); Assert.IsNotNull(topBorder);
Assert.IsNotNull(bottomBorder); Assert.IsNotNull(bottomBorder);
int height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//up //up
new Actions(session).MoveToElement(topBorder).ClickAndHold().MoveByOffset(0, -5000).Release().Perform(); new Actions(session).MoveToElement(topBorder).ClickAndHold().MoveByOffset(0, -5000).Release().Perform();
Assert.IsTrue(topBorder.Rect.Y >= 0); Assert.IsTrue(topBorder.Rect.Y >= 0);
Assert.IsTrue(height < bottomBorder.Rect.Y - topBorder.Rect.Y);
height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//down //down
new Actions(session).MoveToElement(topBorder).ClickAndHold().MoveByOffset(0, 5000).Release().Perform(); new Actions(session).MoveToElement(topBorder).ClickAndHold().MoveByOffset(0, 5000).Release().Perform();
Assert.IsTrue(topBorder.Rect.Y <= bottomBorder.Rect.Y); Assert.IsTrue(topBorder.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(height > bottomBorder.Rect.Y - topBorder.Rect.Y);
} }
[TestMethod] [TestMethod]
@@ -43,13 +48,18 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder); Assert.IsNotNull(topBorder);
Assert.IsNotNull(bottomBorder); Assert.IsNotNull(bottomBorder);
int height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//up //up
new Actions(session).MoveToElement(bottomBorder).ClickAndHold().MoveByOffset(0, -5000).Release().Perform(); new Actions(session).MoveToElement(bottomBorder).ClickAndHold().MoveByOffset(0, -5000).Release().Perform();
Assert.IsTrue(topBorder.Rect.Y <= bottomBorder.Rect.Y); Assert.IsTrue(topBorder.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(height > bottomBorder.Rect.Y - topBorder.Rect.Y);
height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//down //down
new Actions(session).MoveToElement(bottomBorder).ClickAndHold().MoveByOffset(0, 5000).Release().Perform(); new Actions(session).MoveToElement(bottomBorder).ClickAndHold().MoveByOffset(0, 5000).Release().Perform();
Assert.IsTrue(bottomBorder.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom); Assert.IsTrue(bottomBorder.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(height < bottomBorder.Rect.Y - topBorder.Rect.Y);
} }
[TestMethod] [TestMethod]
@@ -60,13 +70,18 @@ namespace PowerToysTests
Assert.IsNotNull(leftBorder); Assert.IsNotNull(leftBorder);
Assert.IsNotNull(rightBorder); Assert.IsNotNull(rightBorder);
int width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the left //to the left
new Actions(session).MoveToElement(leftBorder).ClickAndHold().MoveByOffset(-5000, 0).Release().Perform(); new Actions(session).MoveToElement(leftBorder).ClickAndHold().MoveByOffset(-5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom); Assert.IsTrue(leftBorder.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(width < rightBorder.Rect.X - leftBorder.Rect.X);
width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the right //to the right
new Actions(session).MoveToElement(leftBorder).ClickAndHold().MoveByOffset(5000, 0).Release().Perform(); new Actions(session).MoveToElement(leftBorder).ClickAndHold().MoveByOffset(5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X); Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(width > rightBorder.Rect.X - leftBorder.Rect.X);
} }
[TestMethod] [TestMethod]
@@ -77,13 +92,18 @@ namespace PowerToysTests
Assert.IsNotNull(leftBorder); Assert.IsNotNull(leftBorder);
Assert.IsNotNull(rightBorder); Assert.IsNotNull(rightBorder);
int width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the left //to the left
new Actions(session).MoveToElement(rightBorder).ClickAndHold().MoveByOffset(-5000, 0).Release().Perform(); new Actions(session).MoveToElement(rightBorder).ClickAndHold().MoveByOffset(-5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X); Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(width > rightBorder.Rect.X - leftBorder.Rect.X);
width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the right //to the right
new Actions(session).MoveToElement(rightBorder).ClickAndHold().MoveByOffset(5000, 0).Release().Perform(); new Actions(session).MoveToElement(rightBorder).ClickAndHold().MoveByOffset(5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right); Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
Assert.IsTrue(width < rightBorder.Rect.X - leftBorder.Rect.X);
} }
[TestMethod] [TestMethod]
@@ -96,41 +116,32 @@ namespace PowerToysTests
Assert.IsNotNull(bottomBorder); Assert.IsNotNull(bottomBorder);
Assert.IsNotNull(rightBorder); Assert.IsNotNull(rightBorder);
//up int expectedWidth = rightBorder.Rect.X - topLeftCorner.Rect.X;
MoveCorner(topLeftCorner, true, true, 0, -5000); int expectedHeight = bottomBorder.Rect.Y - topLeftCorner.Rect.Y;
Assert.IsTrue(topLeftCorner.Rect.Y >= 0); int actualWidth, actualHeight;
//down
MoveCorner(topLeftCorner, true, true, 0, 5000);
Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y);
//up-left //up-left
MoveCorner(topLeftCorner, true, true, -5000, -5000); MoveCorner(topLeftCorner, true, true, -5000, -5000);
actualHeight = bottomBorder.Rect.Y - topLeftCorner.Rect.Y;
actualWidth = rightBorder.Rect.X - topLeftCorner.Rect.X;
Assert.IsTrue(topLeftCorner.Rect.Y >= 0); Assert.IsTrue(topLeftCorner.Rect.Y >= 0);
Assert.IsTrue(topLeftCorner.Rect.X >= 0); Assert.IsTrue(topLeftCorner.Rect.X >= 0);
Assert.IsTrue(actualHeight > expectedHeight);
Assert.IsTrue(actualWidth > expectedWidth);
//up-right expectedHeight = actualHeight;
MoveCorner(topLeftCorner, true, true, 5000, -5000); expectedWidth = actualWidth;
Assert.IsTrue(topLeftCorner.Rect.Y >= 0);
Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X);
//to the left
MoveCorner(topLeftCorner, true, true, -5000, 0);
Assert.IsTrue(topLeftCorner.Rect.X >= 0);
//to the right
MoveCorner(topLeftCorner, true, true, 5000, 0);
Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X);
//down-left
MoveCorner(topLeftCorner, true, true, -5000, 5000);
Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(topLeftCorner.Rect.X >= 0);
//down-right //down-right
MoveCorner(topLeftCorner, true, true, 5000, 5000); MoveCorner(topLeftCorner, true, true, 5000, 5000);
actualHeight = bottomBorder.Rect.Y - topLeftCorner.Rect.Y;
actualWidth = rightBorder.Rect.X - topLeftCorner.Rect.X;
Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y); Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X); Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
} }
[TestMethod] [TestMethod]
@@ -143,41 +154,32 @@ namespace PowerToysTests
Assert.IsNotNull(bottomBorder); Assert.IsNotNull(bottomBorder);
Assert.IsNotNull(leftBorder); Assert.IsNotNull(leftBorder);
//up int expectedWidth = topRightCorner.Rect.X - leftBorder.Rect.X;
MoveCorner(topRightCorner, false, true, 0, -5000); int expectedHeight = bottomBorder.Rect.Y - topRightCorner.Rect.Y;
Assert.IsTrue(topRightCorner.Rect.Y >= 0); int actualWidth, actualHeight;
//down
MoveCorner(topRightCorner, false, true, 0, 5000);
Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y);
//up-left
MoveCorner(topRightCorner, false, true, -5000, -5000);
Assert.IsTrue(topRightCorner.Rect.Y >= 0);
Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X);
//up-right //up-right
MoveCorner(topRightCorner, false, true, 5000, -5000); MoveCorner(topRightCorner, false, true, 5000, -5000);
actualHeight = bottomBorder.Rect.Y - topRightCorner.Rect.Y;
actualWidth = topRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(topRightCorner.Rect.Y >= 0); Assert.IsTrue(topRightCorner.Rect.Y >= 0);
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right); Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
Assert.IsTrue(actualHeight > expectedHeight);
Assert.IsTrue(actualWidth > expectedWidth);
//to the left expectedHeight = actualHeight;
MoveCorner(topRightCorner, false, true, -5000, 0); expectedWidth = actualWidth;
Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X);
//to the right
MoveCorner(topRightCorner, false, true, 5000, 0);
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
//down-right
MoveCorner(topRightCorner, false, true, 5000, 5000);
Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
//down-left //down-left
MoveCorner(topRightCorner, false, true, -5000, 5000); MoveCorner(topRightCorner, false, true, -5000, 5000);
actualHeight = bottomBorder.Rect.Y - topRightCorner.Rect.Y;
actualWidth = topRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y); Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X); Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
} }
[TestMethod] [TestMethod]
@@ -190,41 +192,32 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder); Assert.IsNotNull(topBorder);
Assert.IsNotNull(rightBorder); Assert.IsNotNull(rightBorder);
//down int expectedWidth = rightBorder.Rect.X - bottomLeftCorner.Rect.X;
MoveCorner(bottomLeftCorner, true, false, 0, 5000); int expectedHeight = bottomLeftCorner.Rect.Y - topBorder.Rect.Y;
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom); int actualWidth, actualHeight;
//up
MoveCorner(bottomLeftCorner, true, false, 0, -5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y);
//down-right
MoveCorner(bottomLeftCorner, true, false, 5000, 5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X);
//down-left
MoveCorner(bottomLeftCorner, true, false, -5000, 5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
//to the right
MoveCorner(bottomLeftCorner, true, false, 5000, 0);
Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X);
//to the left
MoveCorner(bottomLeftCorner, true, false, -5000, 0);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
//up-left //up-left
MoveCorner(bottomLeftCorner, true, false, -5000, -5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
//up-right
MoveCorner(bottomLeftCorner, true, false, 5000, -5000); MoveCorner(bottomLeftCorner, true, false, 5000, -5000);
actualHeight = bottomLeftCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = rightBorder.Rect.X - bottomLeftCorner.Rect.X;
Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y); Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X); Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
expectedHeight = actualHeight;
expectedWidth = actualWidth;
//down-right
MoveCorner(bottomLeftCorner, true, false, -5000, 5000);
actualHeight = bottomLeftCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = rightBorder.Rect.X - bottomLeftCorner.Rect.X;
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
Assert.IsTrue(actualHeight > expectedHeight);
Assert.IsTrue(actualWidth > expectedWidth);
} }
[TestMethod] [TestMethod]
@@ -237,41 +230,31 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder); Assert.IsNotNull(topBorder);
Assert.IsNotNull(leftBorder); Assert.IsNotNull(leftBorder);
//to the right int expectedWidth = bottomRightCorner.Rect.X - leftBorder.Rect.X;
MoveCorner(bottomRightCorner, false, false, 5000, 0); int expectedHeight = bottomRightCorner.Rect.Y - topBorder.Rect.Y;
Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right); int actualWidth, actualHeight;
//to the left
MoveCorner(bottomRightCorner, false, false, -5000, 0);
Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X);
//down
MoveCorner(bottomRightCorner, false, false, 0, 5000);
Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
//up
MoveCorner(bottomRightCorner, false, false, 0, -5000);
Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y);
//up-left //up-left
MoveCorner(bottomRightCorner, false, false, -5000, -5000); MoveCorner(bottomRightCorner, false, false, -5000, -5000);
actualHeight = bottomRightCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = bottomRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y); Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X); Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
//up-right expectedHeight = actualHeight;
MoveCorner(bottomRightCorner, false, false, 5000, -5000); expectedWidth = actualWidth;
Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
//down-right //down-right
MoveCorner(bottomRightCorner, false, false, 5000, 5000); MoveCorner(bottomRightCorner, false, false, 5000, 5000);
actualHeight = bottomRightCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = bottomRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom); Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right); Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
Assert.IsTrue(actualHeight > expectedHeight);
//down-left
MoveCorner(bottomRightCorner, false, false, -5000, 5000);
Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X);
} }
[ClassInitialize] [ClassInitialize]
@@ -286,16 +269,11 @@ namespace PowerToysTests
} }
OpenEditor(); OpenEditor();
OpenCustomLayouts(); OpenCustomLayouts();
//create canvas zone
OpenCreatorWindow("Create new custom", "Custom layout creator");
session.FindElementByAccessibilityId("newZoneButton").Click();
} }
[ClassCleanup] [ClassCleanup]
public static void ClassCleanup() public static void ClassCleanup()
{ {
new Actions(session).MoveToElement(session.FindElementByXPath("//Button[@Name=\"Cancel\"]")).Click().Perform();
CloseEditor(); CloseEditor();
TearDown(); TearDown();
} }
@@ -303,13 +281,15 @@ namespace PowerToysTests
[TestInitialize] [TestInitialize]
public void TestInitialize() public void TestInitialize()
{ {
//create canvas zone
OpenCreatorWindow("Create new custom", "Custom layout creator");
session.FindElementByAccessibilityId("newZoneButton").Click();
} }
[TestCleanup] [TestCleanup]
public void TestCleanup() public void TestCleanup()
{ {
new Actions(session).MoveToElement(session.FindElementByXPath("//Button[@Name=\"Cancel\"]")).Click().Perform();
} }
} }
} }

View File

@@ -23,7 +23,7 @@ namespace PowerToysTests
{ {
WindowsElement cancelButton = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]/Window/Button[@Name=\"Cancel\"]"); WindowsElement cancelButton = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]/Window/Button[@Name=\"Cancel\"]");
new Actions(session).MoveToElement(cancelButton).Click().Perform(); new Actions(session).MoveToElement(cancelButton).Click().Perform();
ShortWait(); WaitSeconds(1);
Assert.AreEqual(_initialZoneSettings, File.ReadAllText(_zoneSettingsPath), "Settings were changed"); Assert.AreEqual(_initialZoneSettings, File.ReadAllText(_zoneSettingsPath), "Settings were changed");
} }
@@ -31,7 +31,7 @@ namespace PowerToysTests
private void SaveTest(string type, string name, int zoneCount) private void SaveTest(string type, string name, int zoneCount)
{ {
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait(); WaitSeconds(1);
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
Assert.AreEqual(name, settings["custom-zone-sets"][0]["name"]); Assert.AreEqual(name, settings["custom-zone-sets"][0]["name"]);
@@ -149,7 +149,7 @@ namespace PowerToysTests
string name = "My custom zone layout name"; string name = "My custom zone layout name";
SetLayoutName(name); SetLayoutName(name);
SaveTest("canvas", name, 0); SaveTest("canvas", name, 0);
ShortWait(); WaitSeconds(1);
//rename layout //rename layout
OpenEditor(); OpenEditor();
@@ -168,7 +168,7 @@ namespace PowerToysTests
string name = "Name"; string name = "Name";
SetLayoutName(name); SetLayoutName(name);
SaveTest("canvas", name, 0); SaveTest("canvas", name, 0);
ShortWait(); WaitSeconds(1);
//save layout id //save layout id
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@@ -183,7 +183,7 @@ namespace PowerToysTests
//settings are saved on window closing //settings are saved on window closing
new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform();
ShortWait(); WaitSeconds(1);
//check settings //check settings
settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@@ -206,7 +206,7 @@ namespace PowerToysTests
SetLayoutName(name); SetLayoutName(name);
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait(); WaitSeconds(1);
//remove layout //remove layout
OpenEditor(); OpenEditor();
@@ -217,7 +217,7 @@ namespace PowerToysTests
//settings are saved on window closing //settings are saved on window closing
new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform();
ShortWait(); WaitSeconds(1);
//check settings //check settings
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@@ -236,7 +236,6 @@ namespace PowerToysTests
SetLayoutName(name); SetLayoutName(name);
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait();
//remove layout //remove layout
OpenEditor(); OpenEditor();
@@ -247,7 +246,7 @@ namespace PowerToysTests
//settings are saved on window closing //settings are saved on window closing
new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform();
ShortWait(); WaitSeconds(1);
//check settings //check settings
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@@ -263,7 +262,7 @@ namespace PowerToysTests
OpenCreatorWindow("Create new custom", "Custom layout creator"); OpenCreatorWindow("Create new custom", "Custom layout creator");
SetLayoutName(name); SetLayoutName(name);
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait(); WaitSeconds(1);
//save layout id //save layout id
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@@ -278,7 +277,7 @@ namespace PowerToysTests
//apply //apply
new Actions(session).MoveToElement(session.FindElementByName("Apply")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByName("Apply")).Click().Perform();
ShortWait(); WaitSeconds(1);
//check settings //check settings
settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));

View File

@@ -59,11 +59,10 @@ namespace PowerToysTests
WindowsElement errorMessage = null; WindowsElement errorMessage = null;
try try
{ {
errorMessage = session.FindElementByName("FancyZones Editor Exception Handler"); errorMessage = WaitElementByName("FancyZones Editor Exception Handler");
if (errorMessage != null) if (errorMessage != null)
{ {
errorMessage.FindElementByName("OK").Click(); errorMessage.FindElementByName("OK").Click();
ShortWait();
} }
} }
catch (OpenQA.Selenium.WebDriverException) catch (OpenQA.Selenium.WebDriverException)
@@ -92,16 +91,12 @@ namespace PowerToysTests
Assert.IsNotNull(editorButton); Assert.IsNotNull(editorButton);
editorButton.Click(); editorButton.Click();
ShortWait();
TestEditorOpened(); TestEditorOpened();
} }
void OpenEditorByHotkey() void OpenEditorByHotkey()
{ {
new Actions(session).KeyDown(OpenQA.Selenium.Keys.Command).SendKeys("`").KeyUp(OpenQA.Selenium.Keys.Command).Perform(); new Actions(session).KeyDown(OpenQA.Selenium.Keys.Command).SendKeys("`").KeyUp(OpenQA.Selenium.Keys.Command).Perform();
ShortWait();
TestEditorOpened(); TestEditorOpened();
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OpenQA.Selenium; using OpenQA.Selenium;
@@ -8,71 +9,177 @@ using OpenQA.Selenium.Appium.Windows;
namespace PowerToysTests namespace PowerToysTests
{ {
[TestClass] [TestClass]
public class FancyZonesEditorSettingsTests : PowerToysSession public class FancyZonesEditorSettingsTests : FancyZonesEditor
{ {
private const string editorZoneCount = "editor-zone-count";
private const string editorShowSpacing = "editor-show-spacing";
private const string editorSpacing = "editor-spacing";
[TestMethod] [TestMethod]
public void ZoneCount() public void ZoneCount()
{ {
ShortWait(); OpenEditor();
OpenFancyZonesSettings();
WindowsElement editorButton = WaitElementByXPath("//Button[@Name=\"Edit zones\"]"); WindowsElement minusButton = session.FindElementByAccessibilityId("decrementZones");
editorButton.Click(); WindowsElement zoneCount = session.FindElementByAccessibilityId("zoneCount");
WindowsElement minusButton = WaitElementByAccessibilityId("decrementZones"); int editorZoneCountValue;
WindowsElement zoneCount = WaitElementByAccessibilityId("zoneCount"); Assert.IsTrue(Int32.TryParse(zoneCount.Text, out editorZoneCountValue));
WindowsElement applyButton;
int zoneCountQty; for (int i = editorZoneCountValue - 1, j = 0; i > -5; --i, ++j)
Assert.IsTrue(Int32.TryParse(zoneCount.Text, out zoneCountQty));
for (int i = zoneCountQty - 1, j = 0; i > -5; --i, ++j)
{ {
minusButton.Click(); minusButton.Click();
Assert.IsTrue(Int32.TryParse(zoneCount.Text, out zoneCountQty)); Assert.IsTrue(Int32.TryParse(zoneCount.Text, out editorZoneCountValue));
Assert.AreEqual(Math.Max(i, 1), zoneCountQty); Assert.AreEqual(Math.Max(i, 1), editorZoneCountValue);
if (j == 0 || i == -4) if (j == 0 || i == -4)
{ {
applyButton = WaitElementByAccessibilityId("ApplyTemplateButton"); session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
applyButton.Click();
ShortWait(); WaitSeconds(1);
Assert.AreEqual(zoneCountQty, getSavedZoneCount()); Assert.AreEqual(editorZoneCountValue, GetEditZonesSetting<int>(editorZoneCount));
editorButton.Click(); OpenEditor();
minusButton = WaitElementByAccessibilityId("decrementZones");
zoneCount = WaitElementByAccessibilityId("zoneCount"); minusButton = session.FindElementByAccessibilityId("decrementZones");
zoneCount = session.FindElementByAccessibilityId("zoneCount");
} }
} }
WindowsElement plusButton = WaitElementByAccessibilityId("incrementZones"); WindowsElement plusButton = session.FindElementByAccessibilityId("incrementZones");
for (int i = 2; i < 45; ++i) for (int i = 2; i < 45; ++i)
{ {
plusButton.Click(); plusButton.Click();
Assert.IsTrue(Int32.TryParse(zoneCount.Text, out zoneCountQty)); Assert.IsTrue(Int32.TryParse(zoneCount.Text, out editorZoneCountValue));
Assert.AreEqual(Math.Min(i, 40), zoneCountQty); Assert.AreEqual(Math.Min(i, 40), editorZoneCountValue);
} }
applyButton = WaitElementByAccessibilityId("ApplyTemplateButton"); session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
applyButton.Click(); WaitSeconds(1);
ShortWait(); Assert.AreEqual(editorZoneCountValue, GetEditZonesSetting<int>(editorZoneCount));
Assert.AreEqual(zoneCountQty, getSavedZoneCount());
} }
private int getSavedZoneCount() [TestMethod]
public void ShowSpacingTest()
{
for (int i = 0; i < 2; ++i)
{
OpenEditor();
WindowsElement spaceAroundSetting = session.FindElementByAccessibilityId("spaceAroundSetting");
bool spaceAroundSettingValue = spaceAroundSetting.Selected;
spaceAroundSetting.Click();
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
WaitSeconds(1);
Assert.AreNotEqual(spaceAroundSettingValue, GetEditZonesSetting<bool>(editorShowSpacing));
}
}
[TestMethod]
public void SpacingTestsValid()
{
OpenEditor();
WindowsElement spaceAroundSetting = session.FindElementByAccessibilityId("spaceAroundSetting");
bool editorShowSpacingValue = spaceAroundSetting.Selected;
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
WaitSeconds(1);
string[] validValues = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
foreach (string editorSpacingValue in validValues)
{
OpenEditor();
WindowsElement paddingValue = WaitElementByAccessibilityId("paddingValue");
ClearText(paddingValue);
paddingValue.SendKeys(editorSpacingValue);
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
WaitSeconds(1);
Assert.AreEqual(editorShowSpacingValue, GetEditZonesSetting<bool>(editorShowSpacing));
Assert.AreEqual(editorSpacingValue, GetEditZonesSetting<string>(editorSpacing));
}
}
[TestMethod]
public void SpacingTestsInvalid()
{
OpenEditor();
WindowsElement spaceAroundSetting = session.FindElementByAccessibilityId("spaceAroundSetting");
bool editorShowSpacingValue = spaceAroundSetting.Selected;
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
WaitSeconds(1);
string[] invalidValues = { "!", "/", "<", "?", "D", "Z", "]", "m", "}", "1.5", "2,5" };
string editorSpacingValue = GetEditZonesSetting<string>(editorSpacing);
foreach (string value in invalidValues)
{
OpenEditor();
WindowsElement paddingValue = WaitElementByAccessibilityId("paddingValue");
ClearText(paddingValue);
paddingValue.SendKeys(value);
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
WaitSeconds(1);
Assert.AreEqual(editorShowSpacingValue, GetEditZonesSetting<bool>(editorShowSpacing));
Assert.AreEqual(editorSpacingValue, GetEditZonesSetting<string>(editorSpacing));
}
}
[TestMethod]
public void SpacingTestLargeValue()
{
OpenEditor();
session.FindElementByXPath("//Text[@Name=\"Grid\"]").Click();
WindowsElement paddingValue = session.FindElementByAccessibilityId("paddingValue");
ClearText(paddingValue);
paddingValue.SendKeys("1000");
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
editorWindow = null;
try
{
OpenEditor();
}
catch { }
Assert.AreNotEqual(editorWindow, null, "Editor Zones Window is not starting after setting large padding value");
}
private T GetEditZonesSetting<T>(string value)
{ {
JObject zoneSettings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject zoneSettings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
int editorZoneCount = (int)zoneSettings["devices"][0]["editor-zone-count"]; T result = zoneSettings["devices"][0][value].ToObject<T>();
return editorZoneCount; return result;
}
private void ClearText(WindowsElement windowsElement)
{
windowsElement.SendKeys(Keys.Home);
windowsElement.SendKeys(Keys.Control + Keys.Delete);
} }
[ClassInitialize] [ClassInitialize]
public static void ClassInitialize(TestContext context) public static void ClassInitialize(TestContext context)
{ {
Setup(context); Setup(context, false);
OpenSettings(); ResetSettings();
} }
[ClassCleanup] [ClassCleanup]
@@ -91,7 +198,7 @@ namespace PowerToysTests
[TestCleanup] [TestCleanup]
public void TestCleanup() public void TestCleanup()
{ {
ResetSettings();
} }
} }
} }

View File

@@ -18,7 +18,7 @@ namespace PowerToysTests
{ {
WindowsElement cancelButton = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]/Window/Button[@Name=\"Cancel\"]"); WindowsElement cancelButton = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]/Window/Button[@Name=\"Cancel\"]");
new Actions(session).MoveToElement(cancelButton).Click().Perform(); new Actions(session).MoveToElement(cancelButton).Click().Perform();
ShortWait(); WaitSeconds(1);
Assert.AreEqual(_defaultZoneSettings, File.ReadAllText(_zoneSettingsPath), "Settings were changed"); Assert.AreEqual(_defaultZoneSettings, File.ReadAllText(_zoneSettingsPath), "Settings were changed");
} }
@@ -26,7 +26,7 @@ namespace PowerToysTests
private void SaveTest() private void SaveTest()
{ {
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform(); new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait(); WaitSeconds(1);
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath)); JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
Assert.AreEqual("Custom Layout 1", settings["custom-zone-sets"][0]["name"]); Assert.AreEqual("Custom Layout 1", settings["custom-zone-sets"][0]["name"]);
@@ -188,7 +188,6 @@ namespace PowerToysTests
if (editorWindow != null) if (editorWindow != null)
{ {
editorWindow.SendKeys(OpenQA.Selenium.Keys.Alt + OpenQA.Selenium.Keys.F4); editorWindow.SendKeys(OpenQA.Selenium.Keys.Alt + OpenQA.Selenium.Keys.F4);
ShortWait();
} }
} }
catch(OpenQA.Selenium.WebDriverException) catch(OpenQA.Selenium.WebDriverException)

View File

@@ -1,4 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows; using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Interactions;
@@ -17,8 +17,10 @@ namespace PowerToysTests
protected static void OpenEditor() protected static void OpenEditor()
{ {
new Actions(session).KeyDown(OpenQA.Selenium.Keys.Command).SendKeys("`").KeyUp(OpenQA.Selenium.Keys.Command).Perform(); new Actions(session).KeyDown(OpenQA.Selenium.Keys.Command).SendKeys("`").KeyUp(OpenQA.Selenium.Keys.Command).Perform();
ShortWait(); //editorWindow = WaitElementByXPath("//Window[@Name=\"FancyZones Editor\"]");
editorWindow = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]"); //may not find editor by name in 0.16.1
editorWindow = WaitElementByAccessibilityId("MainWindow1");
Assert.IsNotNull(editorWindow, "Couldn't find editor window");
} }
protected static void CloseEditor() protected static void CloseEditor()
@@ -28,7 +30,6 @@ namespace PowerToysTests
if (editorWindow != null) if (editorWindow != null)
{ {
editorWindow.SendKeys(OpenQA.Selenium.Keys.Alt + OpenQA.Selenium.Keys.F4); editorWindow.SendKeys(OpenQA.Selenium.Keys.Alt + OpenQA.Selenium.Keys.F4);
ShortWait();
} }
} }
catch (OpenQA.Selenium.WebDriverException) catch (OpenQA.Selenium.WebDriverException)

View File

@@ -25,8 +25,6 @@ namespace PowerToysTests
private static void Init() private static void Init()
{ {
OpenSettings(); OpenSettings();
ShortWait();
OpenFancyZonesSettings(); OpenFancyZonesSettings();
_saveButton = session.FindElementByName("Save"); _saveButton = session.FindElementByName("Save");
@@ -94,7 +92,7 @@ namespace PowerToysTests
Assert.AreEqual(expected.ToString() + "\r\n", editor.Text); Assert.AreEqual(expected.ToString() + "\r\n", editor.Text);
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
int value = GetPropertyValue<int>("fancyzones_highlight_opacity"); int value = GetPropertyValue<int>("fancyzones_highlight_opacity");
Assert.AreEqual(expected, value); Assert.AreEqual(expected, value);
@@ -215,7 +213,7 @@ namespace PowerToysTests
action.Perform(); action.Perform();
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
//Assert.AreEqual(expectedText, input.Text); //Assert.AreEqual(expectedText, input.Text);
@@ -235,7 +233,7 @@ namespace PowerToysTests
//black on the bottom //black on the bottom
new Actions(session).MoveToElement(saturationAndBrightness).ClickAndHold().MoveByOffset(0, satRect.Height).Release().Perform(); new Actions(session).MoveToElement(saturationAndBrightness).ClickAndHold().MoveByOffset(0, satRect.Height).Release().Perform();
ShortWait(); WaitSeconds(1);
Assert.AreEqual("0\r\n", red.Text); Assert.AreEqual("0\r\n", red.Text);
Assert.AreEqual("0\r\n", green.Text); Assert.AreEqual("0\r\n", green.Text);
@@ -243,7 +241,7 @@ namespace PowerToysTests
Assert.AreEqual("000000\r\n", hex.Text); Assert.AreEqual("000000\r\n", hex.Text);
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
Assert.AreEqual("#000000", GetPropertyValue<string>(propertyName)); Assert.AreEqual("#000000", GetPropertyValue<string>(propertyName));
//white in left corner //white in left corner
@@ -254,7 +252,7 @@ namespace PowerToysTests
Assert.AreEqual("ffffff\r\n", hex.Text); Assert.AreEqual("ffffff\r\n", hex.Text);
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
Assert.AreEqual("#ffffff", GetPropertyValue<string>(propertyName)); Assert.AreEqual("#ffffff", GetPropertyValue<string>(propertyName));
//color in right corner //color in right corner
@@ -266,7 +264,7 @@ namespace PowerToysTests
Assert.AreEqual("ff0000\r\n", hex.Text); Assert.AreEqual("ff0000\r\n", hex.Text);
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
Assert.AreEqual("#ff0000", GetPropertyValue<string>(propertyName)); Assert.AreEqual("#ff0000", GetPropertyValue<string>(propertyName));
} }
@@ -299,9 +297,10 @@ namespace PowerToysTests
toggle.Click(); toggle.Click();
SaveChanges(); SaveChanges();
ShortWait();
} }
WaitSeconds(1);
//check saved settings //check saved settings
JObject savedProps = GetProperties(); JObject savedProps = GetProperties();
Assert.AreNotEqual(toggleValues[0], GetPropertyValue<bool>(savedProps, "fancyzones_shiftDrag")); Assert.AreNotEqual(toggleValues[0], GetPropertyValue<bool>(savedProps, "fancyzones_shiftDrag"));
@@ -339,7 +338,7 @@ namespace PowerToysTests
} }
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
JObject savedProps = GetProperties(); JObject savedProps = GetProperties();
Assert.AreEqual(toggleValues[0], GetPropertyValue<bool>(savedProps, "fancyzones_shiftDrag")); Assert.AreEqual(toggleValues[0], GetPropertyValue<bool>(savedProps, "fancyzones_shiftDrag"));
@@ -396,7 +395,7 @@ namespace PowerToysTests
Actions action = new Actions(session); Actions action = new Actions(session);
action.MoveToElement(editor).MoveByOffset(editorRect.Width / 2 + 10, -editorRect.Height / 4).Perform(); action.MoveToElement(editor).MoveByOffset(editorRect.Width / 2 + 10, -editorRect.Height / 4).Perform();
ShortWait(); WaitSeconds(1);
action.Click().Perform(); action.Click().Perform();
Assert.AreEqual("100\r\n", editor.Text); Assert.AreEqual("100\r\n", editor.Text);
@@ -421,7 +420,7 @@ namespace PowerToysTests
Actions action = new Actions(session); Actions action = new Actions(session);
action.MoveToElement(editor).MoveByOffset(editorRect.Width / 2 + 10, editorRect.Height / 4).Perform(); action.MoveToElement(editor).MoveByOffset(editorRect.Width / 2 + 10, editorRect.Height / 4).Perform();
ShortWait(); WaitSeconds(1);
action.Click().Perform(); action.Click().Perform();
Assert.AreEqual("0\r\n", editor.Text); Assert.AreEqual("0\r\n", editor.Text);
@@ -494,7 +493,7 @@ namespace PowerToysTests
Assert.AreEqual("152", hue.Text); Assert.AreEqual("152", hue.Text);
SaveChanges(); SaveChanges();
ShortWait(); WaitSeconds(1);
Assert.AreEqual("#63c99a", GetPropertyValue<string>("fancyzones_zoneHighlightColor")); Assert.AreEqual("#63c99a", GetPropertyValue<string>("fancyzones_zoneHighlightColor"));
} }
@@ -565,7 +564,7 @@ namespace PowerToysTests
input.SendKeys(inputValue); input.SendKeys(inputValue);
SaveChanges(); SaveChanges();
ClearInput(input); ClearInput(input);
ShortWait(); WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps")); Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
//invalid //invalid
@@ -573,28 +572,28 @@ namespace PowerToysTests
input.SendKeys(inputValue); input.SendKeys(inputValue);
SaveChanges(); SaveChanges();
ClearInput(input); ClearInput(input);
ShortWait(); WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps")); Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
inputValue = "Notepad,Chrome"; inputValue = "Notepad,Chrome";
input.SendKeys(inputValue); input.SendKeys(inputValue);
SaveChanges(); SaveChanges();
ClearInput(input); ClearInput(input);
ShortWait(); WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps")); Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
inputValue = "Note*"; inputValue = "Note*";
input.SendKeys(inputValue); input.SendKeys(inputValue);
SaveChanges(); SaveChanges();
ClearInput(input); ClearInput(input);
ShortWait(); WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps")); Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
inputValue = "Кириллица"; inputValue = "Кириллица";
input.SendKeys(inputValue); input.SendKeys(inputValue);
SaveChanges(); SaveChanges();
ClearInput(input); ClearInput(input);
ShortWait(); WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps")); Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
} }

View File

@@ -13,6 +13,8 @@ namespace PowerToysTests
public class PowerToysSession public class PowerToysSession
{ {
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723"; protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
protected const string AppPath = "C:\\Program Files\\PowerToys\\PowerToys.exe";
protected static WindowsDriver<WindowsElement> session; protected static WindowsDriver<WindowsElement> session;
protected static bool isPowerToysLaunched = false; protected static bool isPowerToysLaunched = false;
protected static WindowsElement trayButton; protected static WindowsElement trayButton;
@@ -67,13 +69,26 @@ namespace PowerToysTests
Thread.Sleep(TimeSpan.FromSeconds(seconds)); Thread.Sleep(TimeSpan.FromSeconds(seconds));
} }
public static void ShortWait() //Trying to find element by XPath
protected static WindowsElement WaitElementByName(string name, double maxTime = 10)
{ {
Thread.Sleep(TimeSpan.FromSeconds(0.5)); WindowsElement result = null;
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.Elapsed < TimeSpan.FromSeconds(maxTime))
{
try
{
result = session.FindElementByName(name);
}
catch { }
return result;
}
return null;
} }
//Trying to find element by XPath //Trying to find element by XPath
protected WindowsElement WaitElementByXPath(string xPath, double maxTime = 10) protected static WindowsElement WaitElementByXPath(string xPath, double maxTime = 10)
{ {
WindowsElement result = null; WindowsElement result = null;
Stopwatch timer = new Stopwatch(); Stopwatch timer = new Stopwatch();
@@ -85,17 +100,13 @@ namespace PowerToysTests
result = session.FindElementByXPath(xPath); result = session.FindElementByXPath(xPath);
} }
catch { } catch { }
if (result != null)
{
return result; return result;
} }
}
Assert.IsNotNull(result);
return null; return null;
} }
//Trying to find element by AccessibilityId //Trying to find element by AccessibilityId
protected WindowsElement WaitElementByAccessibilityId(string accessibilityId, double maxTime = 10) protected static WindowsElement WaitElementByAccessibilityId(string accessibilityId, double maxTime = 10)
{ {
WindowsElement result = null; WindowsElement result = null;
Stopwatch timer = new Stopwatch(); Stopwatch timer = new Stopwatch();
@@ -107,12 +118,8 @@ namespace PowerToysTests
result = session.FindElementByAccessibilityId(accessibilityId); result = session.FindElementByAccessibilityId(accessibilityId);
} }
catch { } catch { }
if (result != null)
{
return result; return result;
} }
}
Assert.IsNotNull(result);
return null; return null;
} }
@@ -125,13 +132,11 @@ namespace PowerToysTests
public static void OpenFancyZonesSettings() public static void OpenFancyZonesSettings()
{ {
WindowsElement fzNavigationButton = session.FindElementByXPath("//Button[@Name=\"FancyZones\"]"); WindowsElement fzNavigationButton = WaitElementByXPath("//Button[@Name=\"FancyZones\"]");
Assert.IsNotNull(fzNavigationButton); Assert.IsNotNull(fzNavigationButton);
fzNavigationButton.Click(); fzNavigationButton.Click();
fzNavigationButton.Click(); fzNavigationButton.Click();
ShortWait();
} }
public static void CloseSettings() public static void CloseSettings()
@@ -157,7 +162,7 @@ namespace PowerToysTests
try try
{ {
WindowsElement pt = session.FindElementByXPath("//Button[@Name=\"PowerToys\"]"); WindowsElement pt = WaitElementByXPath("//Button[@Name=\"PowerToys\"]");
isLaunched = (pt != null); isLaunched = (pt != null);
} }
catch(OpenQA.Selenium.WebDriverException) catch(OpenQA.Selenium.WebDriverException)
@@ -175,9 +180,7 @@ namespace PowerToysTests
{ {
AppiumOptions opts = new AppiumOptions(); AppiumOptions opts = new AppiumOptions();
opts.PlatformName = "Windows"; opts.PlatformName = "Windows";
opts.AddAdditionalCapability("platformVersion", "10"); opts.AddAdditionalCapability("app", AppPath);
opts.AddAdditionalCapability("deviceName", "WindowsPC");
opts.AddAdditionalCapability("app", "C:/Program Files/PowerToys/PowerToys.exe");
WindowsDriver<WindowsElement> driver = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), opts); WindowsDriver<WindowsElement> driver = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), opts);
Assert.IsNotNull(driver); Assert.IsNotNull(driver);
@@ -195,13 +198,12 @@ namespace PowerToysTests
public static void ExitPowerToys() public static void ExitPowerToys()
{ {
trayButton.Click(); trayButton.Click();
ShortWait();
WindowsElement pt = session.FindElementByXPath("//Button[@Name=\"PowerToys\"]"); WindowsElement pt = WaitElementByXPath("//Button[@Name=\"PowerToys\"]");
Assert.IsNotNull(pt, "Couldn't find \'PowerToys\' button");
new Actions(session).MoveToElement(pt).ContextClick().Perform(); new Actions(session).MoveToElement(pt).ContextClick().Perform();
ShortWait();
session.FindElementByXPath("//MenuItem[@Name=\"Exit\"]").Click(); WaitElementByXPath("//MenuItem[@Name=\"Exit\"]").Click();
trayButton.Click(); //close tray trayButton.Click(); //close tray
isPowerToysLaunched = false; isPowerToysLaunched = false;
} }

View File

@@ -15,10 +15,9 @@ namespace PowerToysTests
public void SettingsOpen() public void SettingsOpen()
{ {
OpenSettings(); OpenSettings();
ShortWait();
//check settings window opened //check settings window opened
WindowsElement settingsWindow = session.FindElementByName("PowerToys Settings"); WindowsElement settingsWindow = WaitElementByName("PowerToys Settings");
Assert.IsNotNull(settingsWindow); Assert.IsNotNull(settingsWindow);
isSettingsOpened = true; isSettingsOpened = true;
@@ -36,14 +35,12 @@ namespace PowerToysTests
Assert.IsNotNull(pt); Assert.IsNotNull(pt);
new Actions(session).MoveToElement(pt).ContextClick().Perform(); new Actions(session).MoveToElement(pt).ContextClick().Perform();
ShortWait();
//open settings //open settings
session.FindElementByXPath("//MenuItem[@Name=\"Settings\"]").Click(); WaitElementByXPath("//MenuItem[@Name=\"Settings\"]").Click();
ShortWait();
//check settings window opened //check settings window opened
WindowsElement settingsWindow = session.FindElementByName("PowerToys Settings"); WindowsElement settingsWindow = WaitElementByName("PowerToys Settings");
Assert.IsNotNull(settingsWindow); Assert.IsNotNull(settingsWindow);
isSettingsOpened = true; isSettingsOpened = true;
@@ -62,11 +59,9 @@ namespace PowerToysTests
Assert.IsNotNull(powerToys); Assert.IsNotNull(powerToys);
new Actions(session).MoveToElement(powerToys).ContextClick().Perform(); new Actions(session).MoveToElement(powerToys).ContextClick().Perform();
ShortWait();
//exit //exit
session.FindElementByXPath("//MenuItem[@Name=\"Exit\"]").Click(); WaitElementByXPath("//MenuItem[@Name=\"Exit\"]").Click();
ShortWait();
//check PowerToys exited //check PowerToys exited
powerToys = null; powerToys = null;
@@ -82,8 +77,6 @@ namespace PowerToysTests
} }
LaunchPowerToys(); LaunchPowerToys();
ShortWait();
Assert.IsNull(powerToys); Assert.IsNull(powerToys);
} }