[FancyZones] Split and reorganize FancyZonesData and JSON Helpers (#5028)

* Rename JsonHelpers to FancyZonesData
Add new JsonHelpers.[h|cpp] files

* Introduce FancyZonesDataTypes

* Move first part of JSON related stuff to JsonHelpers files

* Small refactor

* Move all json related stuff to JsonHelpers

* Minor refactoring

* Fix formating

* Remove GetPersistFancyZonesJSONPath() and GetPersistAppZoneHistoryFilePath()
Remove GetActiveZoneSetTmpPath(), GetDeletedCustomZoneSetsTmpPath and GetAppliedZoneSetTmpPath()
Simplify tests

* Address PR comment - Rename FancyZonesDataNS to FancyZonesData

* Address PR comment - Rename local var

* Delete obsolete stuff

* Remove double and uneeded includes
Introduce const non-localizable string variables
Address all othe PR comments

* Add comments to explain hardcoded values

* Remove FancyZonesData namespace

* Introduce const non-localizable string variables in FancyZonesDataTypes

* Add comments to explain FancyZonesData maps

Co-authored-by: Clint Rutkas <clint@rutkas.com>
This commit is contained in:
stefansjfw
2020-07-22 10:39:13 +02:00
committed by GitHub
parent 4f45cf1386
commit 0027a0af40
25 changed files with 1924 additions and 1934 deletions

View File

@@ -11,6 +11,7 @@
#include <lib/trace.h> #include <lib/trace.h>
#include <lib/Settings.h> #include <lib/Settings.h>
#include <lib/FancyZones.h> #include <lib/FancyZones.h>
#include <lib/FancyZonesData.h>
#include <lib/FancyZonesWinHookEventIDs.h> #include <lib/FancyZonesWinHookEventIDs.h>
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -153,7 +154,7 @@ public:
{ {
app_name = GET_RESOURCE_STRING(IDS_FANCYZONES); app_name = GET_RESOURCE_STRING(IDS_FANCYZONES);
m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), FancyZonesModule::get_name()); m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), FancyZonesModule::get_name());
JSONHelpers::FancyZonesDataInstance().LoadFancyZonesData(); FancyZonesDataInstance().LoadFancyZonesData();
s_instance = this; s_instance = this;
} }

View File

@@ -15,13 +15,5 @@ namespace FancyZonesEditor
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
internal delegate int PersistZoneSet(
[MarshalAs(UnmanagedType.LPWStr)] string activeKey,
[MarshalAs(UnmanagedType.LPWStr)] string resolutionKey,
uint monitor,
ushort layoutId,
int zoneCount,
[MarshalAs(UnmanagedType.LPArray)] int[] zoneArray);
} }
} }

View File

@@ -7,7 +7,7 @@
#include "FancyZones.h" #include "FancyZones.h"
#include "lib/Settings.h" #include "lib/Settings.h"
#include "lib/ZoneWindow.h" #include "lib/ZoneWindow.h"
#include "lib/JsonHelpers.h" #include "lib/FancyZonesData.h"
#include "lib/ZoneSet.h" #include "lib/ZoneSet.h"
#include "lib/WindowMoveHandler.h" #include "lib/WindowMoveHandler.h"
#include "lib/FancyZonesWinHookEventIDs.h" #include "lib/FancyZonesWinHookEventIDs.h"
@@ -373,7 +373,7 @@ std::vector<int> FancyZones::GetZoneIndexSetFromWorkAreaHistory(
wil::unique_cotaskmem_string zoneSetId; wil::unique_cotaskmem_string zoneSetId;
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &zoneSetId))) if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &zoneSetId)))
{ {
return JSONHelpers::FancyZonesDataInstance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), zoneSetId.get()); return FancyZonesDataInstance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), zoneSetId.get());
} }
} }
return {}; return {};
@@ -419,7 +419,7 @@ std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> FancyZones::GetAppZoneH
void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zoneWindow, const std::vector<int>& zoneIndexSet) noexcept void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zoneWindow, const std::vector<int>& zoneIndexSet) noexcept
{ {
auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance(); auto& fancyZonesData = FancyZonesDataInstance();
if (!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId())) if (!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId()))
{ {
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow); m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
@@ -650,17 +650,13 @@ void FancyZones::ToggleEditor() noexcept
std::to_wstring(width) + L"_" + std::to_wstring(width) + L"_" +
std::to_wstring(height); std::to_wstring(height);
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance(); const auto& fancyZonesData = FancyZonesDataInstance();
const auto deviceInfo = fancyZonesData.FindDeviceInfo(zoneWindow->UniqueId()); if (!fancyZonesData.SerializeDeviceInfoToTmpFile(zoneWindow->UniqueId()))
if (!deviceInfo.has_value())
{ {
return; return;
} }
JSONHelpers::DeviceInfoJSON deviceInfoJson{ zoneWindow->UniqueId(), *deviceInfo };
fancyZonesData.SerializeDeviceInfoToTmpFile(deviceInfoJson, ZoneWindowUtils::GetActiveZoneSetTmpPath());
const std::wstring params = const std::wstring params =
/*1*/ editorLocation + L" " + /*1*/ editorLocation + L" " +
/*2*/ L"\"" + std::to_wstring(GetCurrentProcessId()) + L"\""; /*2*/ L"\"" + std::to_wstring(GetCurrentProcessId()) + L"\"";
@@ -842,8 +838,8 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
std::vector<std::wstring> ids{}; std::vector<std::wstring> ids{};
if (VirtualDesktopUtils::GetVirtualDesktopIds(ids) && !ids.empty()) if (VirtualDesktopUtils::GetVirtualDesktopIds(ids) && !ids.empty())
{ {
JSONHelpers::FancyZonesDataInstance().UpdatePrimaryDesktopData(ids[0]); FancyZonesDataInstance().UpdatePrimaryDesktopData(ids[0]);
JSONHelpers::FancyZonesDataInstance().RemoveDeletedDesktops(ids); FancyZonesDataInstance().RemoveDeletedDesktops(ids);
} }
} }
} }
@@ -884,7 +880,7 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
if (workArea) if (workArea)
{ {
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea); m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData(); FancyZonesDataInstance().SaveFancyZonesData();
} }
} }
} }
@@ -1065,7 +1061,7 @@ void FancyZones::RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept
std::vector<std::wstring> active{}; std::vector<std::wstring> active{};
if (VirtualDesktopUtils::GetVirtualDesktopIds(active)) if (VirtualDesktopUtils::GetVirtualDesktopIds(active))
{ {
JSONHelpers::FancyZonesDataInstance().RemoveDeletedDesktops(active); FancyZonesDataInstance().RemoveDeletedDesktops(active);
} }
} }
@@ -1084,10 +1080,7 @@ bool FancyZones::IsSplashScreen(HWND window)
void FancyZones::OnEditorExitEvent() noexcept void FancyZones::OnEditorExitEvent() noexcept
{ {
// Collect information about changes in zone layout after editor exited. // Collect information about changes in zone layout after editor exited.
JSONHelpers::FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(ZoneWindowUtils::GetActiveZoneSetTmpPath()); FancyZonesDataInstance().ParseDataFromTmpFiles();
JSONHelpers::FancyZonesDataInstance().ParseDeletedCustomZoneSetsFromTmpFile(ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath());
JSONHelpers::FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
for (auto workArea : m_workAreaHandler.GetAllWorkAreas()) for (auto workArea : m_workAreaHandler.GetAllWorkAreas())
{ {

View File

@@ -0,0 +1,604 @@
#include "pch.h"
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include "JsonHelpers.h"
#include "ZoneSet.h"
#include "Settings.h"
#include <common/common.h>
#include <common/json.h>
#include <shlwapi.h>
#include <filesystem>
#include <fstream>
#include <optional>
#include <regex>
#include <sstream>
#include <unordered_set>
// Non-localizable strings
namespace NonLocalizable
{
const wchar_t FancyZonesStr[] = L"FancyZones";
const wchar_t LayoutsStr[] = L"Layouts";
const wchar_t NullStr[] = L"null";
}
namespace
{
const wchar_t* FANCY_ZONES_DATA_FILE = L"zones-settings.json";
const wchar_t* FANCY_ZONES_APP_ZONE_HISTORY_FILE = L"app-zone-history.json";
const wchar_t* DEFAULT_GUID = L"{00000000-0000-0000-0000-000000000000}";
const wchar_t* REG_SETTINGS = L"Software\\SuperFancyZones";
const wchar_t ActiveZoneSetsTmpFileName[] = L"FancyZonesActiveZoneSets.json";
const wchar_t AppliedZoneSetsTmpFileName[] = L"FancyZonesAppliedZoneSets.json";
const wchar_t DeletedCustomZoneSetsTmpFileName[] = L"FancyZonesDeletedCustomZoneSets.json";
std::wstring ExtractVirtualDesktopId(const std::wstring& deviceId)
{
// Format: <device-id>_<resolution>_<virtual-desktop-id>
return deviceId.substr(deviceId.rfind('_') + 1);
}
const std::wstring& GetTempDirPath()
{
static std::wstring tmpDirPath;
static std::once_flag flag;
std::call_once(flag, []() {
wchar_t buffer[MAX_PATH];
auto charsWritten = GetTempPath(MAX_PATH, buffer);
if (charsWritten > MAX_PATH || (charsWritten == 0))
{
abort();
}
tmpDirPath = std::wstring{ buffer };
});
return tmpDirPath;
}
}
FancyZonesData& FancyZonesDataInstance()
{
static FancyZonesData instance;
return instance;
}
FancyZonesData::FancyZonesData()
{
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::FancyZonesStr);
zonesSettingsFileName = saveFolderPath + L"\\" + std::wstring(FANCY_ZONES_DATA_FILE);
appZoneHistoryFileName = saveFolderPath + L"\\" + std::wstring(FANCY_ZONES_APP_ZONE_HISTORY_FILE);
activeZoneSetTmpFileName = GetTempDirPath() + ActiveZoneSetsTmpFileName;
appliedZoneSetTmpFileName = GetTempDirPath() + AppliedZoneSetsTmpFileName;
deletedCustomZoneSetsTmpFileName = GetTempDirPath() + DeletedCustomZoneSetsTmpFileName;
}
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const std::wstring& zoneWindowId) const
{
std::scoped_lock lock{ dataLock };
auto it = deviceInfoMap.find(zoneWindowId);
return it != end(deviceInfoMap) ? std::optional{ it->second } : std::nullopt;
}
std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustomZoneSet(const std::wstring& guid) const
{
std::scoped_lock lock{ dataLock };
auto it = customZoneSetsMap.find(guid);
return it != end(customZoneSetsMap) ? std::optional{ it->second } : std::nullopt;
}
void FancyZonesData::AddDevice(const std::wstring& deviceId)
{
std::scoped_lock lock{ dataLock };
if (!deviceInfoMap.contains(deviceId))
{
// Creates default entry in map when ZoneWindow is created
deviceInfoMap[deviceId] = FancyZonesDataTypes::DeviceInfoData{ FancyZonesDataTypes::ZoneSetData{ NonLocalizable::NullStr, FancyZonesDataTypes::ZoneSetLayoutType::Blank } };
}
}
void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstring& destination)
{
if (source == destination)
{
return;
}
std::scoped_lock lock{ dataLock };
// The source virtual desktop is deleted, simply ignore it.
if (!deviceInfoMap.contains(source))
{
return;
}
// Clone information from source device if destination device is uninitialized (Blank).
auto& destInfo = deviceInfoMap[destination];
if (destInfo.activeZoneSet.type == FancyZonesDataTypes::ZoneSetLayoutType::Blank)
{
destInfo = deviceInfoMap[source];
}
}
void FancyZonesData::UpdatePrimaryDesktopData(const std::wstring& desktopId)
{
// Explorer persists current virtual desktop identifier to registry on a per session basis,
// but only after first virtual desktop switch happens. If the user hasn't switched virtual
// desktops in this session value in registry will be empty and we will use default GUID in
// that case (00000000-0000-0000-0000-000000000000).
// This method will go through all our persisted data with default GUID and update it with
// valid one.
auto replaceDesktopId = [&desktopId](const std::wstring& deviceId) {
return deviceId.substr(0, deviceId.rfind('_') + 1) + desktopId;
};
std::scoped_lock lock{ dataLock };
for (auto& [path, perDesktopData] : appZoneHistoryMap)
{
for (auto& data : perDesktopData)
{
if (ExtractVirtualDesktopId(data.deviceId) == DEFAULT_GUID)
{
data.deviceId = replaceDesktopId(data.deviceId);
}
}
}
std::vector<std::wstring> toReplace{};
for (const auto& [id, data] : deviceInfoMap)
{
if (ExtractVirtualDesktopId(id) == DEFAULT_GUID)
{
toReplace.push_back(id);
}
}
for (const auto& id : toReplace)
{
auto mapEntry = deviceInfoMap.extract(id);
mapEntry.key() = replaceDesktopId(id);
deviceInfoMap.insert(std::move(mapEntry));
}
SaveFancyZonesData();
}
void FancyZonesData::RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops)
{
std::unordered_set<std::wstring> active(std::begin(activeDesktops), std::end(activeDesktops));
std::scoped_lock lock{ dataLock };
for (auto it = std::begin(deviceInfoMap); it != std::end(deviceInfoMap);)
{
std::wstring desktopId = ExtractVirtualDesktopId(it->first);
auto foundId = active.find(desktopId);
if (foundId == std::end(active))
{
RemoveDesktopAppZoneHistory(desktopId);
it = deviceInfoMap.erase(it);
}
else
{
++it;
}
}
SaveFancyZonesData();
}
bool FancyZonesData::IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
if (!processPath.empty())
{
auto history = appZoneHistoryMap.find(processPath);
if (history != std::end(appZoneHistoryMap))
{
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId == deviceId)
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
auto processIdIt = data.processIdToHandleMap.find(processId);
if (processIdIt == std::end(data.processIdToHandleMap))
{
return false;
}
else if (processIdIt->second != window && IsWindow(processIdIt->second))
{
return true;
}
}
}
}
}
return false;
}
void FancyZonesData::UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId)
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
if (!processPath.empty())
{
auto history = appZoneHistoryMap.find(processPath);
if (history != std::end(appZoneHistoryMap))
{
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId == deviceId)
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
data.processIdToHandleMap[processId] = window;
break;
}
}
}
}
}
std::vector<int> FancyZonesData::GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
if (!processPath.empty())
{
auto history = appZoneHistoryMap.find(processPath);
if (history != std::end(appZoneHistoryMap))
{
const auto& perDesktopData = history->second;
for (const auto& data : perDesktopData)
{
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
{
return data.zoneIndexSet;
}
}
}
}
return {};
}
bool FancyZonesData::RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId)
{
std::scoped_lock lock{ dataLock };
auto processPath = get_process_path(window);
if (!processPath.empty())
{
auto history = appZoneHistoryMap.find(processPath);
if (history != std::end(appZoneHistoryMap))
{
auto& perDesktopData = history->second;
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
{
if (data->deviceId == deviceId && data->zoneSetUuid == zoneSetId)
{
if (!IsAnotherWindowOfApplicationInstanceZoned(window, deviceId))
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
data->processIdToHandleMap.erase(processId);
}
// if there is another instance of same application placed in the same zone don't erase history
size_t windowZoneStamp = reinterpret_cast<size_t>(::GetProp(window, MULTI_ZONE_STAMP));
for (auto placedWindow : data->processIdToHandleMap)
{
size_t placedWindowZoneStamp = reinterpret_cast<size_t>(::GetProp(placedWindow.second, MULTI_ZONE_STAMP));
if (IsWindow(placedWindow.second) && (windowZoneStamp == placedWindowZoneStamp))
{
return false;
}
}
data = perDesktopData.erase(data);
if (perDesktopData.empty())
{
appZoneHistoryMap.erase(processPath);
}
SaveFancyZonesData();
return true;
}
else
{
++data;
}
}
}
}
return false;
}
bool FancyZonesData::SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<int>& zoneIndexSet)
{
std::scoped_lock lock{ dataLock };
if (IsAnotherWindowOfApplicationInstanceZoned(window, deviceId))
{
return false;
}
auto processPath = get_process_path(window);
if (processPath.empty())
{
return false;
}
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
auto history = appZoneHistoryMap.find(processPath);
if (history != std::end(appZoneHistoryMap))
{
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId == deviceId)
{
// application already has history on this work area, update it with new window position
data.processIdToHandleMap[processId] = window;
data.zoneSetUuid = zoneSetId;
data.zoneIndexSet = zoneIndexSet;
SaveFancyZonesData();
return true;
}
}
}
std::unordered_map<DWORD, HWND> processIdToHandleMap{};
processIdToHandleMap[processId] = window;
FancyZonesDataTypes::AppZoneHistoryData data{ .processIdToHandleMap = processIdToHandleMap,
.zoneSetUuid = zoneSetId,
.deviceId = deviceId,
.zoneIndexSet = zoneIndexSet };
if (appZoneHistoryMap.contains(processPath))
{
// application already has history but on other desktop, add with new desktop info
appZoneHistoryMap[processPath].push_back(data);
}
else
{
// new application, create entry in app zone history map
appZoneHistoryMap[processPath] = std::vector<FancyZonesDataTypes::AppZoneHistoryData>{ data };
}
SaveFancyZonesData();
return true;
}
void FancyZonesData::SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
{
std::scoped_lock lock{ dataLock };
auto it = deviceInfoMap.find(deviceId);
if (it != deviceInfoMap.end())
{
it->second.activeZoneSet = data;
}
}
bool FancyZonesData::SerializeDeviceInfoToTmpFile(const std::wstring& uniqueId) const
{
const auto deviceInfo = FindDeviceInfo(uniqueId);
if (!deviceInfo.has_value())
{
return false;
}
JSONHelpers::DeviceInfoJSON deviceInfoJson{ uniqueId, *deviceInfo };
JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfoJson, activeZoneSetTmpFileName);
return true;
}
void FancyZonesData::ParseDataFromTmpFiles()
{
ParseDeviceInfoFromTmpFile(activeZoneSetTmpFileName);
ParseDeletedCustomZoneSetsFromTmpFile(deletedCustomZoneSetsTmpFileName);
ParseCustomZoneSetFromTmpFile(appliedZoneSetTmpFileName);
SaveFancyZonesData();
}
void FancyZonesData::ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath)
{
std::scoped_lock lock{ dataLock };
const auto& deviceInfo = JSONHelpers::ParseDeviceInfoFromTmpFile(tmpFilePath);
if (deviceInfo)
{
deviceInfoMap[deviceInfo->deviceId] = std::move(deviceInfo->data);
}
}
void FancyZonesData::ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath)
{
std::scoped_lock lock{ dataLock };
const auto& customZoneSet = JSONHelpers::ParseCustomZoneSetFromTmpFile(tmpFilePath);
if (customZoneSet)
{
customZoneSetsMap[customZoneSet->uuid] = std::move(customZoneSet->data);
}
}
void FancyZonesData::ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath)
{
std::scoped_lock lock{ dataLock };
const auto& deletedCustomZoneSets = JSONHelpers::ParseDeletedCustomZoneSetsFromTmpFile(tmpFilePath);
for (const auto& zoneSet : deletedCustomZoneSets)
{
customZoneSetsMap.erase(zoneSet);
}
}
json::JsonObject FancyZonesData::GetPersistFancyZonesJSON()
{
return JSONHelpers::GetPersistFancyZonesJSON(zonesSettingsFileName, appZoneHistoryFileName);
}
void FancyZonesData::LoadFancyZonesData()
{
if (!std::filesystem::exists(zonesSettingsFileName))
{
MigrateCustomZoneSetsFromRegistry();
SaveFancyZonesData();
}
else
{
json::JsonObject fancyZonesDataJSON = GetPersistFancyZonesJSON();
appZoneHistoryMap = JSONHelpers::ParseAppZoneHistory(fancyZonesDataJSON);
deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
customZoneSetsMap = JSONHelpers::ParseCustomZoneSets(fancyZonesDataJSON);
}
}
void FancyZonesData::SaveFancyZonesData() const
{
std::scoped_lock lock{ dataLock };
JSONHelpers::SaveFancyZonesData(zonesSettingsFileName,
appZoneHistoryFileName,
deviceInfoMap,
customZoneSetsMap,
appZoneHistoryMap);
}
void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
{
std::scoped_lock lock{ dataLock };
wchar_t key[256];
StringCchPrintf(key, ARRAYSIZE(key), L"%s\\%s", REG_SETTINGS, NonLocalizable::LayoutsStr);
HKEY hkey;
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
{
BYTE data[256];
DWORD dataSize = ARRAYSIZE(data);
wchar_t value[256]{};
DWORD valueLength = ARRAYSIZE(value);
DWORD i = 0;
while (RegEnumValueW(hkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
{
FancyZonesDataTypes::CustomZoneSetData zoneSetData;
zoneSetData.name = std::wstring{ value };
zoneSetData.type = static_cast<FancyZonesDataTypes::CustomLayoutType>(data[2]);
GUID guid;
auto result = CoCreateGuid(&guid);
if (result != S_OK)
{
continue;
}
wil::unique_cotaskmem_string guidString;
if (!SUCCEEDED(StringFromCLSID(guid, &guidString)))
{
continue;
}
std::wstring uuid = guidString.get();
switch (zoneSetData.type)
{
case FancyZonesDataTypes::CustomLayoutType::Grid:
{
// Visit https://github.com/microsoft/PowerToys/blob/v0.14.0/src/modules/fancyzones/editor/FancyZonesEditor/Models/GridLayoutModel.cs#L183
// To see how custom Grid layout was packed in registry
int j = 5;
FancyZonesDataTypes::GridLayoutInfo zoneSetInfo(FancyZonesDataTypes::GridLayoutInfo::Minimal{ .rows = data[j++], .columns = data[j++] });
for (int row = 0; row < zoneSetInfo.rows(); row++, j += 2)
{
zoneSetInfo.rowsPercents()[row] = data[j] * 256 + data[j + 1];
}
for (int col = 0; col < zoneSetInfo.columns(); col++, j += 2)
{
zoneSetInfo.columnsPercents()[col] = data[j] * 256 + data[j + 1];
}
for (int row = 0; row < zoneSetInfo.rows(); row++)
{
for (int col = 0; col < zoneSetInfo.columns(); col++)
{
zoneSetInfo.cellChildMap()[row][col] = data[j++];
}
}
zoneSetData.info = zoneSetInfo;
break;
}
case FancyZonesDataTypes::CustomLayoutType::Canvas:
{
// Visit https://github.com/microsoft/PowerToys/blob/v0.14.0/src/modules/fancyzones/editor/FancyZonesEditor/Models/CanvasLayoutModel.cs#L128
// To see how custom Canvas layout was packed in registry
int j = 5;
FancyZonesDataTypes::CanvasLayoutInfo info;
info.lastWorkAreaWidth = data[j] * 256 + data[j + 1];
j += 2;
info.lastWorkAreaHeight = data[j] * 256 + data[j + 1];
j += 2;
int count = data[j++];
info.zones.reserve(count);
while (count-- > 0)
{
int x = data[j] * 256 + data[j + 1];
j += 2;
int y = data[j] * 256 + data[j + 1];
j += 2;
int width = data[j] * 256 + data[j + 1];
j += 2;
int height = data[j] * 256 + data[j + 1];
j += 2;
info.zones.push_back(FancyZonesDataTypes::CanvasLayoutInfo::Rect{
x, y, width, height });
}
zoneSetData.info = info;
break;
}
default:
continue;
}
customZoneSetsMap[uuid] = zoneSetData;
valueLength = ARRAYSIZE(value);
dataSize = ARRAYSIZE(data);
}
}
}
void FancyZonesData::RemoveDesktopAppZoneHistory(const std::wstring& desktopId)
{
for (auto it = std::begin(appZoneHistoryMap); it != std::end(appZoneHistoryMap);)
{
auto& perDesktopData = it->second;
for (auto desktopIt = std::begin(perDesktopData); desktopIt != std::end(perDesktopData);)
{
if (ExtractVirtualDesktopId(desktopIt->deviceId) == desktopId)
{
desktopIt = perDesktopData.erase(desktopIt);
}
else
{
++desktopIt;
}
}
if (perDesktopData.empty())
{
it = appZoneHistoryMap.erase(it);
}
else
{
++it;
}
}
}

View File

@@ -0,0 +1,137 @@
#pragma once
#include "JsonHelpers.h"
#include <common/settings_helpers.h>
#include <common/json.h>
#include <mutex>
#include <string>
#include <unordered_map>
#include <optional>
#include <vector>
#include <winnt.h>
namespace FancyZonesDataTypes
{
struct ZoneSetData;
struct DeviceInfoData;
struct CustomZoneSetData;
struct AppZoneHistoryData;
}
#if defined(UNIT_TESTS)
namespace FancyZonesUnitTests
{
class FancyZonesDataUnitTests;
class FancyZonesIFancyZonesCallbackUnitTests;
class ZoneSetCalculateZonesUnitTests;
class ZoneWindowUnitTests;
}
#endif
class FancyZonesData
{
public:
FancyZonesData();
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const std::wstring& zoneWindowId) const;
std::optional<FancyZonesDataTypes::CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const;
inline const std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>& GetDeviceInfoMap() const
{
std::scoped_lock lock{ dataLock };
return deviceInfoMap;
}
inline const std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>& GetCustomZoneSetsMap() const
{
std::scoped_lock lock{ dataLock };
return customZoneSetsMap;
}
inline const std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>& GetAppZoneHistoryMap() const
{
std::scoped_lock lock{ dataLock };
return appZoneHistoryMap;
}
void AddDevice(const std::wstring& deviceId);
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const;
void UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId);
std::vector<int> GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
bool RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId);
bool SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<int>& zoneIndexSet);
void SetActiveZoneSet(const std::wstring& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
bool SerializeDeviceInfoToTmpFile(const std::wstring& uniqueId) const;
void ParseDataFromTmpFiles();
json::JsonObject GetPersistFancyZonesJSON();
void LoadFancyZonesData();
void SaveFancyZonesData() const;
private:
#if defined(UNIT_TESTS)
friend class FancyZonesUnitTests::FancyZonesDataUnitTests;
friend class FancyZonesUnitTests::FancyZonesIFancyZonesCallbackUnitTests;
friend class FancyZonesUnitTests::ZoneWindowUnitTests;
friend class FancyZonesUnitTests::ZoneSetCalculateZonesUnitTests;
inline void SetDeviceInfo(const std::wstring& deviceId, FancyZonesDataTypes::DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
inline bool ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
{
deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
return !deviceInfoMap.empty();
}
inline void clear_data()
{
appZoneHistoryMap.clear();
deviceInfoMap.clear();
customZoneSetsMap.clear();
}
inline void SetSettingsModulePath(std::wstring_view moduleName)
{
std::wstring result = PTSettingsHelper::get_module_save_folder_location(moduleName);
zonesSettingsFileName = result + L"\\" + std::wstring(L"zones-settings.json");
appZoneHistoryFileName = result + L"\\" + std::wstring(L"app-zone-history.json");
}
#endif
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
void ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
void ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
void MigrateCustomZoneSetsFromRegistry();
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
// Maps app path to app's zone history data
std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>> appZoneHistoryMap{};
// Maps device unique ID to device data
std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData> deviceInfoMap{};
// Maps custom zoneset UUID to it's data
std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData> customZoneSetsMap{};
std::wstring zonesSettingsFileName;
std::wstring appZoneHistoryFileName;
std::wstring activeZoneSetTmpFileName;
std::wstring appliedZoneSetTmpFileName;
std::wstring deletedCustomZoneSetsTmpFileName;
mutable std::recursive_mutex dataLock;
};
FancyZonesData& FancyZonesDataInstance();

View File

@@ -0,0 +1,115 @@
#include "pch.h"
#include "util.h"
#include "FancyZonesDataTypes.h"
// Non-Localizable strings
namespace NonLocalizable
{
const wchar_t BlankStr[] = L"blank";
const wchar_t ColumnsStr[] = L"columns";
const wchar_t CustomStr[] = L"custom";
const wchar_t FocusStr[] = L"focus";
const wchar_t GridStr[] = L"grid";
const wchar_t PriorityGridStr[] = L"priority-grid";
const wchar_t RowsStr[] = L"rows";
const wchar_t TypeToStringErrorStr[] = L"TypeToString_ERROR";
}
namespace
{
// From Settings.cs
constexpr int c_focusModelId = 0xFFFF;
constexpr int c_rowsModelId = 0xFFFE;
constexpr int c_columnsModelId = 0xFFFD;
constexpr int c_gridModelId = 0xFFFC;
constexpr int c_priorityGridModelId = 0xFFFB;
constexpr int c_blankCustomModelId = 0xFFFA;
}
namespace FancyZonesDataTypes
{
std::wstring TypeToString(ZoneSetLayoutType type)
{
switch (type)
{
case ZoneSetLayoutType::Blank:
return NonLocalizable::BlankStr;
case ZoneSetLayoutType::Focus:
return NonLocalizable::FocusStr;
case ZoneSetLayoutType::Columns:
return NonLocalizable::ColumnsStr;
case ZoneSetLayoutType::Rows:
return NonLocalizable::RowsStr;
case ZoneSetLayoutType::Grid:
return NonLocalizable::GridStr;
case ZoneSetLayoutType::PriorityGrid:
return NonLocalizable::PriorityGridStr;
case ZoneSetLayoutType::Custom:
return NonLocalizable::CustomStr;
default:
return NonLocalizable::TypeToStringErrorStr;
}
}
ZoneSetLayoutType TypeFromString(const std::wstring& typeStr)
{
if (typeStr == NonLocalizable::FocusStr)
{
return ZoneSetLayoutType::Focus;
}
else if (typeStr == NonLocalizable::ColumnsStr)
{
return ZoneSetLayoutType::Columns;
}
else if (typeStr == NonLocalizable::RowsStr)
{
return ZoneSetLayoutType::Rows;
}
else if (typeStr == NonLocalizable::GridStr)
{
return ZoneSetLayoutType::Grid;
}
else if (typeStr == NonLocalizable::PriorityGridStr)
{
return ZoneSetLayoutType::PriorityGrid;
}
else if (typeStr == NonLocalizable::CustomStr)
{
return ZoneSetLayoutType::Custom;
}
else
{
return ZoneSetLayoutType::Blank;
}
}
GridLayoutInfo::GridLayoutInfo(const Minimal& info) :
m_rows(info.rows),
m_columns(info.columns)
{
m_rowsPercents.resize(m_rows, 0);
m_columnsPercents.resize(m_columns, 0);
m_cellChildMap.resize(m_rows, {});
for (auto& cellRow : m_cellChildMap)
{
cellRow.resize(m_columns, 0);
}
}
GridLayoutInfo::GridLayoutInfo(const Full& info) :
m_rows(info.rows),
m_columns(info.columns),
m_rowsPercents(info.rowsPercents),
m_columnsPercents(info.columnsPercents),
m_cellChildMap(info.cellChildMap)
{
m_rowsPercents.resize(m_rows, 0);
m_columnsPercents.resize(m_columns, 0);
m_cellChildMap.resize(m_rows, {});
for (auto& cellRow : m_cellChildMap)
{
cellRow.resize(m_columns, 0);
}
}
}

View File

@@ -0,0 +1,118 @@
#pragma once
#include <common/json.h>
#include <string>
#include <vector>
#include <optional>
#include <variant>
#include <unordered_map>
#include <windef.h>
namespace FancyZonesDataTypes
{
enum class ZoneSetLayoutType : int
{
Blank = -1,
Focus,
Columns,
Rows,
Grid,
PriorityGrid,
Custom
};
std::wstring TypeToString(ZoneSetLayoutType type);
ZoneSetLayoutType TypeFromString(const std::wstring& typeStr);
enum class CustomLayoutType : int
{
Grid = 0,
Canvas
};
struct CanvasLayoutInfo
{
int lastWorkAreaWidth;
int lastWorkAreaHeight;
struct Rect
{
int x;
int y;
int width;
int height;
};
std::vector<CanvasLayoutInfo::Rect> zones;
};
struct GridLayoutInfo
{
struct Minimal
{
int rows;
int columns;
};
struct Full
{
int rows;
int columns;
const std::vector<int>& rowsPercents;
const std::vector<int>& columnsPercents;
const std::vector<std::vector<int>>& cellChildMap;
};
GridLayoutInfo(const Minimal& info);
GridLayoutInfo(const Full& info);
~GridLayoutInfo() = default;
inline std::vector<int>& rowsPercents() { return m_rowsPercents; };
inline std::vector<int>& columnsPercents() { return m_columnsPercents; };
inline std::vector<std::vector<int>>& cellChildMap() { return m_cellChildMap; };
inline int rows() const { return m_rows; }
inline int columns() const { return m_columns; }
inline const std::vector<int>& rowsPercents() const { return m_rowsPercents; };
inline const std::vector<int>& columnsPercents() const { return m_columnsPercents; };
inline const std::vector<std::vector<int>>& cellChildMap() const { return m_cellChildMap; };
int m_rows;
int m_columns;
std::vector<int> m_rowsPercents;
std::vector<int> m_columnsPercents;
std::vector<std::vector<int>> m_cellChildMap;
};
struct CustomZoneSetData
{
std::wstring name;
CustomLayoutType type;
std::variant<CanvasLayoutInfo, GridLayoutInfo> info;
};
struct ZoneSetData
{
std::wstring uuid;
ZoneSetLayoutType type;
};
struct AppZoneHistoryData
{
std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)
std::wstring zoneSetUuid;
std::wstring deviceId;
std::vector<int> zoneIndexSet;
};
struct DeviceInfoData
{
ZoneSetData activeZoneSet;
bool showSpacing;
int spacing;
int zoneCount;
};
}

View File

@@ -99,8 +99,10 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="FancyZones.h" /> <ClInclude Include="FancyZones.h" />
<ClInclude Include="FancyZonesDataTypes.h" />
<ClInclude Include="FancyZonesWinHookEventIDs.h" /> <ClInclude Include="FancyZonesWinHookEventIDs.h" />
<ClInclude Include="GenericKeyHook.h" /> <ClInclude Include="GenericKeyHook.h" />
<ClInclude Include="FancyZonesData.h" />
<ClInclude Include="JsonHelpers.h" /> <ClInclude Include="JsonHelpers.h" />
<ClInclude Include="MonitorWorkAreaHandler.h" /> <ClInclude Include="MonitorWorkAreaHandler.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
@@ -117,8 +119,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="FancyZones.cpp" /> <ClCompile Include="FancyZones.cpp" />
<ClCompile Include="FancyZonesDataTypes.cpp" />
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" /> <ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
<ClCompile Include="GenericKeyHook.cpp" /> <ClCompile Include="GenericKeyHook.cpp" />
<ClCompile Include="FancyZonesData.cpp" />
<ClCompile Include="JsonHelpers.cpp" /> <ClCompile Include="JsonHelpers.cpp" />
<ClCompile Include="MonitorWorkAreaHandler.cpp" /> <ClCompile Include="MonitorWorkAreaHandler.cpp" />
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">

View File

@@ -42,9 +42,6 @@
<ClInclude Include="trace.h"> <ClInclude Include="trace.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="JsonHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VirtualDesktopUtils.h"> <ClInclude Include="VirtualDesktopUtils.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@@ -63,6 +60,15 @@
<ClInclude Include="GenericKeyHook.h"> <ClInclude Include="GenericKeyHook.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="FancyZonesData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="JsonHelpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesDataTypes.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="pch.cpp"> <ClCompile Include="pch.cpp">
@@ -86,9 +92,6 @@
<ClCompile Include="trace.cpp"> <ClCompile Include="trace.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="JsonHelpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="util.cpp"> <ClCompile Include="util.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -110,6 +113,15 @@
<ClCompile Include="GenericKeyHook.cpp"> <ClCompile Include="GenericKeyHook.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="FancyZonesData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="JsonHelpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesDataTypes.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="fancyzones.rc"> <ResourceCompile Include="fancyzones.rc">

File diff suppressed because it is too large Load Diff

View File

@@ -1,278 +1,83 @@
#pragma once #pragma once
#include <common/settings_helpers.h> #include "FancyZonesDataTypes.h"
#include <common/json.h> #include <common/json.h>
#include <mutex>
#include <string> #include <string>
#include <strsafe.h>
#include <unordered_map>
#include <variant>
#include <optional>
#include <vector> #include <vector>
#include <winnt.h> #include <unordered_map>
namespace JSONHelpers namespace JSONHelpers
{ {
constexpr int MAX_ZONE_COUNT = 50; namespace CanvasLayoutInfoJSON
#if defined(UNIT_TESTS)
bool isValidGuid(const std::wstring& str);
bool isValidDeviceId(const std::wstring& str);
#endif
enum class ZoneSetLayoutType : int
{ {
Blank = -1, json::JsonObject ToJson(const FancyZonesDataTypes::CanvasLayoutInfo& canvasInfo);
Focus, std::optional<FancyZonesDataTypes::CanvasLayoutInfo> FromJson(const json::JsonObject& infoJson);
Columns, }
Rows,
Grid,
PriorityGrid,
Custom
};
enum class CustomLayoutType : int namespace GridLayoutInfoJSON
{ {
Grid = 0, json::JsonObject ToJson(const FancyZonesDataTypes::GridLayoutInfo& gridInfo);
Canvas std::optional<FancyZonesDataTypes::GridLayoutInfo> FromJson(const json::JsonObject& infoJson);
}; }
std::wstring TypeToString(ZoneSetLayoutType type);
ZoneSetLayoutType TypeFromString(const std::wstring& typeStr);
ZoneSetLayoutType TypeFromLayoutId(int layoutID);
struct CanvasLayoutInfo
{
int lastWorkAreaWidth;
int lastWorkAreaHeight;
struct Rect
{
int x;
int y;
int width;
int height;
};
std::vector<CanvasLayoutInfo::Rect> zones;
static json::JsonObject ToJson(const CanvasLayoutInfo& canvasInfo);
static std::optional<CanvasLayoutInfo> FromJson(const json::JsonObject& infoJson);
};
class GridLayoutInfo
{
public:
struct Minimal
{
int rows;
int columns;
};
struct Full
{
int rows;
int columns;
const std::vector<int>& rowsPercents;
const std::vector<int>& columnsPercents;
const std::vector<std::vector<int>>& cellChildMap;
};
GridLayoutInfo(const Minimal& info);
GridLayoutInfo(const Full& info);
~GridLayoutInfo() = default;
static json::JsonObject ToJson(const GridLayoutInfo& gridInfo);
static std::optional<GridLayoutInfo> FromJson(const json::JsonObject& infoJson);
inline std::vector<int>& rowsPercents() { return m_rowsPercents; };
inline std::vector<int>& columnsPercents() { return m_columnsPercents; };
inline std::vector<std::vector<int>>& cellChildMap() { return m_cellChildMap; };
inline int rows() const { return m_rows; }
inline int columns() const { return m_columns; }
inline const std::vector<int>& rowsPercents() const { return m_rowsPercents; };
inline const std::vector<int>& columnsPercents() const { return m_columnsPercents; };
inline const std::vector<std::vector<int>>& cellChildMap() const { return m_cellChildMap; };
private:
int m_rows;
int m_columns;
std::vector<int> m_rowsPercents;
std::vector<int> m_columnsPercents;
std::vector<std::vector<int>> m_cellChildMap;
};
struct CustomZoneSetData
{
std::wstring name;
CustomLayoutType type;
std::variant<CanvasLayoutInfo, GridLayoutInfo> info;
};
struct CustomZoneSetJSON struct CustomZoneSetJSON
{ {
std::wstring uuid; std::wstring uuid;
CustomZoneSetData data; FancyZonesDataTypes::CustomZoneSetData data;
static json::JsonObject ToJson(const CustomZoneSetJSON& device); static json::JsonObject ToJson(const CustomZoneSetJSON& device);
static std::optional<CustomZoneSetJSON> FromJson(const json::JsonObject& customZoneSet); static std::optional<CustomZoneSetJSON> FromJson(const json::JsonObject& customZoneSet);
}; };
// TODO(stefan): This needs to be moved to ZoneSet.h (probably) namespace ZoneSetDataJSON
struct ZoneSetData
{ {
std::wstring uuid; json::JsonObject ToJson(const FancyZonesDataTypes::ZoneSetData& zoneSet);
ZoneSetLayoutType type; std::optional<FancyZonesDataTypes::ZoneSetData> FromJson(const json::JsonObject& zoneSet);
static json::JsonObject ToJson(const ZoneSetData& zoneSet);
static std::optional<ZoneSetData> FromJson(const json::JsonObject& zoneSet);
};
struct AppZoneHistoryData
{
std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)
std::wstring zoneSetUuid;
std::wstring deviceId;
std::vector<int> zoneIndexSet;
}; };
struct AppZoneHistoryJSON struct AppZoneHistoryJSON
{ {
std::wstring appPath; std::wstring appPath;
std::vector<AppZoneHistoryData> data; std::vector<FancyZonesDataTypes::AppZoneHistoryData> data;
static json::JsonObject ToJson(const AppZoneHistoryJSON& appZoneHistory); static json::JsonObject ToJson(const AppZoneHistoryJSON& appZoneHistory);
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet); static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
}; };
struct DeviceInfoData
{
ZoneSetData activeZoneSet;
bool showSpacing;
int spacing;
int zoneCount;
};
struct DeviceInfoJSON struct DeviceInfoJSON
{ {
std::wstring deviceId; std::wstring deviceId;
DeviceInfoData data; FancyZonesDataTypes::DeviceInfoData data;
static json::JsonObject ToJson(const DeviceInfoJSON& device); static json::JsonObject ToJson(const DeviceInfoJSON& device);
static std::optional<DeviceInfoJSON> FromJson(const json::JsonObject& device); static std::optional<DeviceInfoJSON> FromJson(const json::JsonObject& device);
}; };
class FancyZonesData using TAppZoneHistoryMap = std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>;
{ using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
mutable std::recursive_mutex dataLock; using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
public: json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
FancyZonesData(); void SaveFancyZonesData(const std::wstring& zonesSettingsFileName,
const std::wstring& appZoneHistoryFileName,
const TDeviceInfoMap& deviceInfoMap,
const TCustomZoneSetsMap& customZoneSetsMap,
const TAppZoneHistoryMap& appZoneHistoryMap);
inline const std::wstring& GetPersistFancyZonesJSONPath() const TAppZoneHistoryMap ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON);
{ json::JsonArray SerializeAppZoneHistory(const TAppZoneHistoryMap& appZoneHistoryMap);
return jsonFilePath;
}
inline const std::wstring& GetPersistAppZoneHistoryFilePath() const TDeviceInfoMap ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
{ json::JsonArray SerializeDeviceInfos(const TDeviceInfoMap& deviceInfoMap);
return appZoneHistoryFilePath;
}
json::JsonObject GetPersistFancyZonesJSON(); TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
std::optional<DeviceInfoData> FindDeviceInfo(const std::wstring& zoneWindowId) const; void SerializeDeviceInfoToTmpFile(const JSONHelpers::DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath);
std::optional<DeviceInfoJSON> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
std::optional<CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const; std::optional<CustomZoneSetJSON> ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
std::vector<std::wstring> ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
inline const std::unordered_map<std::wstring, DeviceInfoData>& GetDeviceInfoMap() const
{
std::scoped_lock lock{ dataLock };
return deviceInfoMap;
}
inline const std::unordered_map<std::wstring, CustomZoneSetData>& GetCustomZoneSetsMap() const
{
std::scoped_lock lock{ dataLock };
return customZoneSetsMap;
}
inline const std::unordered_map<std::wstring, std::vector<AppZoneHistoryData>>& GetAppZoneHistoryMap() const
{
std::scoped_lock lock{ dataLock };
return appZoneHistoryMap;
}
#if defined(UNIT_TESTS)
inline void clear_data()
{
appZoneHistoryMap.clear();
deviceInfoMap.clear();
customZoneSetsMap.clear();
}
inline void SetDeviceInfo(const std::wstring& deviceId, DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
inline void SetSettingsModulePath(std::wstring_view moduleName)
{
std::wstring result = PTSettingsHelper::get_module_save_folder_location(moduleName);
jsonFilePath = result + L"\\" + std::wstring(L"zones-settings.json");
appZoneHistoryFilePath = result + L"\\" + std::wstring(L"app-zone-history.json");
}
#endif
inline bool DeleteTmpFile(std::wstring_view tmpFilePath) const
{
return DeleteFileW(tmpFilePath.data());
}
void AddDevice(const std::wstring& deviceId);
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const std::wstring_view& deviceId) const;
void UpdateProcessIdToHandleMap(HWND window, const std::wstring_view& deviceId);
std::vector<int> GetAppLastZoneIndexSet(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId) const;
bool RemoveAppLastZone(HWND window, const std::wstring_view& deviceId, const std::wstring_view& zoneSetId);
bool SetAppLastZones(HWND window, const std::wstring& deviceId, const std::wstring& zoneSetId, const std::vector<int>& zoneIndexSet);
void SetActiveZoneSet(const std::wstring& deviceId, const ZoneSetData& zoneSet);
void SerializeDeviceInfoToTmpFile(const DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath) const;
void ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
bool ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
bool ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
bool ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeAppZoneHistory() const;
bool ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeDeviceInfos() const;
bool ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeCustomZoneSets() const;
void LoadFancyZonesData();
void SaveFancyZonesData() const;
private:
void MigrateCustomZoneSetsFromRegistry();
void RemoveDesktopAppZoneHistory(const std::wstring& desktopId);
std::unordered_map<std::wstring, std::vector<AppZoneHistoryData>> appZoneHistoryMap{};
std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{};
std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{};
std::wstring jsonFilePath;
std::wstring appZoneHistoryFilePath;
};
FancyZonesData& FancyZonesDataInstance();
} }

View File

@@ -12,6 +12,7 @@
#include "VirtualDesktopUtils.h" #include "VirtualDesktopUtils.h"
#include "lib/SecondaryMouseButtonsHook.h" #include "lib/SecondaryMouseButtonsHook.h"
#include "lib/GenericKeyHook.h" #include "lib/GenericKeyHook.h"
#include "lib/FancyZonesData.h"
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -337,7 +338,7 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
wil::unique_cotaskmem_string guidString; wil::unique_cotaskmem_string guidString;
if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString))) if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString)))
{ {
JSONHelpers::FancyZonesDataInstance().RemoveAppLastZone(window, zoneWindowPtr->UniqueId(), guidString.get()); FancyZonesDataInstance().RemoveAppLastZone(window, zoneWindowPtr->UniqueId(), guidString.get());
} }
} }
} }

View File

@@ -3,12 +3,17 @@
#include "util.h" #include "util.h"
#include "lib/ZoneSet.h" #include "lib/ZoneSet.h"
#include "Settings.h" #include "Settings.h"
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include <common/dpi_aware.h> #include <common/dpi_aware.h>
#include <utility>
namespace namespace
{ {
constexpr int C_MULTIPLIER = 10000; constexpr int C_MULTIPLIER = 10000;
constexpr int MAX_ZONE_COUNT = 50;
/* /*
struct GridLayoutInfo { struct GridLayoutInfo {
@@ -20,81 +25,81 @@ namespace
}; };
*/ */
auto l = JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Minimal{ .rows = 1, .columns = 1 }); auto l = FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Minimal{ .rows = 1, .columns = 1 });
// PriorityGrid layout is unique for zoneCount <= 11. For zoneCount > 11 PriorityGrid is same as Grid // PriorityGrid layout is unique for zoneCount <= 11. For zoneCount > 11 PriorityGrid is same as Grid
JSONHelpers::GridLayoutInfo predefinedPriorityGridLayouts[11] = { FancyZonesDataTypes::GridLayoutInfo predefinedPriorityGridLayouts[11] = {
/* 1 */ /* 1 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1, .rows = 1,
.columns = 1, .columns = 1,
.rowsPercents = { 10000 }, .rowsPercents = { 10000 },
.columnsPercents = { 10000 }, .columnsPercents = { 10000 },
.cellChildMap = { { 0 } } }), .cellChildMap = { { 0 } } }),
/* 2 */ /* 2 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1, .rows = 1,
.columns = 2, .columns = 2,
.rowsPercents = { 10000 }, .rowsPercents = { 10000 },
.columnsPercents = { 6667, 3333 }, .columnsPercents = { 6667, 3333 },
.cellChildMap = { { 0, 1 } } }), .cellChildMap = { { 0, 1 } } }),
/* 3 */ /* 3 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1, .rows = 1,
.columns = 3, .columns = 3,
.rowsPercents = { 10000 }, .rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 }, .columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }), .cellChildMap = { { 0, 1, 2 } } }),
/* 4 */ /* 4 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2, .rows = 2,
.columns = 3, .columns = 3,
.rowsPercents = { 5000, 5000 }, .rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 }, .columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 } } }), .cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 } } }),
/* 5 */ /* 5 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2, .rows = 2,
.columns = 3, .columns = 3,
.rowsPercents = { 5000, 5000 }, .rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 }, .columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 } } }), .cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 } } }),
/* 6 */ /* 6 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3, .rows = 3,
.columns = 3, .columns = 3,
.rowsPercents = { 3333, 3334, 3333 }, .rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 }, .columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } } }), .cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } } }),
/* 7 */ /* 7 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3, .rows = 3,
.columns = 3, .columns = 3,
.rowsPercents = { 3333, 3334, 3333 }, .rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 }, .columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } } }), .cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } } }),
/* 8 */ /* 8 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3, .rows = 3,
.columns = 4, .columns = 4,
.rowsPercents = { 3333, 3334, 3333 }, .rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 }, .columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } } }), .cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } } }),
/* 9 */ /* 9 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3, .rows = 3,
.columns = 4, .columns = 4,
.rowsPercents = { 3333, 3334, 3333 }, .rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 }, .columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } } }), .cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } } }),
/* 10 */ /* 10 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3, .rows = 3,
.columns = 4, .columns = 4,
.rowsPercents = { 3333, 3334, 3333 }, .rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 }, .columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } } }), .cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } } }),
/* 11 */ /* 11 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3, .rows = 3,
.columns = 4, .columns = 4,
.rowsPercents = { 3333, 3334, 3333 }, .rowsPercents = { 3333, 3334, 3333 },
@@ -119,7 +124,7 @@ public:
IFACEMETHODIMP_(GUID) IFACEMETHODIMP_(GUID)
Id() noexcept { return m_config.Id; } Id() noexcept { return m_config.Id; }
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType) IFACEMETHODIMP_(FancyZonesDataTypes::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_(std::vector<int>) IFACEMETHODIMP_(std::vector<int>)
@@ -143,11 +148,11 @@ public:
private: private:
bool CalculateFocusLayout(Rect workArea, int zoneCount) noexcept; bool CalculateFocusLayout(Rect workArea, int zoneCount) noexcept;
bool CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept; bool CalculateColumnsAndRowsLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
bool CalculateGridLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept; bool CalculateGridLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept; bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept;
bool CalculateCustomLayout(Rect workArea, int spacing) noexcept; bool CalculateCustomLayout(Rect workArea, int spacing) noexcept;
bool CalculateGridZones(Rect workArea, JSONHelpers::GridLayoutInfo gridLayoutInfo, int spacing); bool CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing);
void StampWindow(HWND window, size_t bitmask) noexcept; void StampWindow(HWND window, size_t bitmask) noexcept;
std::vector<winrt::com_ptr<IZone>> m_zones; std::vector<winrt::com_ptr<IZone>> m_zones;
@@ -374,7 +379,7 @@ ZoneSet::CalculateZones(MONITORINFO monitorInfo, int zoneCount, int spacing) noe
} }
//invalid zoneCount, may cause division by zero //invalid zoneCount, may cause division by zero
if (zoneCount <= 0 && m_config.LayoutType != JSONHelpers::ZoneSetLayoutType::Custom) if (zoneCount <= 0 && m_config.LayoutType != FancyZonesDataTypes::ZoneSetLayoutType::Custom)
{ {
return false; return false;
} }
@@ -382,18 +387,18 @@ ZoneSet::CalculateZones(MONITORINFO monitorInfo, int zoneCount, int spacing) noe
bool success = true; bool success = true;
switch (m_config.LayoutType) switch (m_config.LayoutType)
{ {
case JSONHelpers::ZoneSetLayoutType::Focus: case FancyZonesDataTypes::ZoneSetLayoutType::Focus:
success = CalculateFocusLayout(workArea, zoneCount); success = CalculateFocusLayout(workArea, zoneCount);
break; break;
case JSONHelpers::ZoneSetLayoutType::Columns: case FancyZonesDataTypes::ZoneSetLayoutType::Columns:
case JSONHelpers::ZoneSetLayoutType::Rows: case FancyZonesDataTypes::ZoneSetLayoutType::Rows:
success = CalculateColumnsAndRowsLayout(workArea, m_config.LayoutType, zoneCount, spacing); success = CalculateColumnsAndRowsLayout(workArea, m_config.LayoutType, zoneCount, spacing);
break; break;
case JSONHelpers::ZoneSetLayoutType::Grid: case FancyZonesDataTypes::ZoneSetLayoutType::Grid:
case JSONHelpers::ZoneSetLayoutType::PriorityGrid: case FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid:
success = CalculateGridLayout(workArea, m_config.LayoutType, zoneCount, spacing); success = CalculateGridLayout(workArea, m_config.LayoutType, zoneCount, spacing);
break; break;
case JSONHelpers::ZoneSetLayoutType::Custom: case FancyZonesDataTypes::ZoneSetLayoutType::Custom:
success = CalculateCustomLayout(workArea, spacing); success = CalculateCustomLayout(workArea, spacing);
break; break;
} }
@@ -445,14 +450,14 @@ bool ZoneSet::CalculateFocusLayout(Rect workArea, int zoneCount) noexcept
return success; return success;
} }
bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept
{ {
bool success = true; bool success = true;
long totalWidth; long totalWidth;
long totalHeight; long totalHeight;
if (type == JSONHelpers::ZoneSetLayoutType::Columns) if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{ {
totalWidth = workArea.width() - (spacing * (zoneCount + 1)); totalWidth = workArea.width() - (spacing * (zoneCount + 1));
totalHeight = workArea.height() - (spacing * 2); totalHeight = workArea.height() - (spacing * 2);
@@ -472,7 +477,7 @@ bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetL
// like this to make the sum of all zones' sizes exactly total{Width|Height}. // like this to make the sum of all zones' sizes exactly total{Width|Height}.
for (int zone = 0; zone < zoneCount; zone++) for (int zone = 0; zone < zoneCount; zone++)
{ {
if (type == JSONHelpers::ZoneSetLayoutType::Columns) if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{ {
right = left + (zone + 1) * totalWidth / zoneCount - zone * totalWidth / zoneCount; right = left + (zone + 1) * totalWidth / zoneCount - zone * totalWidth / zoneCount;
bottom = totalHeight + spacing; bottom = totalHeight + spacing;
@@ -491,7 +496,7 @@ bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetL
RECT focusZoneRect{ left, top, right, bottom }; RECT focusZoneRect{ left, top, right, bottom };
AddZone(MakeZone(focusZoneRect)); AddZone(MakeZone(focusZoneRect));
if (type == JSONHelpers::ZoneSetLayoutType::Columns) if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{ {
left = right + spacing; left = right + spacing;
} }
@@ -504,10 +509,10 @@ bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetL
return success; return success;
} }
bool ZoneSet::CalculateGridLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept bool ZoneSet::CalculateGridLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept
{ {
const auto count = sizeof(predefinedPriorityGridLayouts) / sizeof(JSONHelpers::GridLayoutInfo); const auto count = sizeof(predefinedPriorityGridLayouts) / sizeof(FancyZonesDataTypes::GridLayoutInfo);
if (type == JSONHelpers::ZoneSetLayoutType::PriorityGrid && zoneCount < count) if (type == FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid && zoneCount < count)
{ {
return CalculateUniquePriorityGridLayout(workArea, zoneCount, spacing); return CalculateUniquePriorityGridLayout(workArea, zoneCount, spacing);
} }
@@ -528,7 +533,7 @@ bool ZoneSet::CalculateGridLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType
columns++; columns++;
} }
JSONHelpers::GridLayoutInfo gridLayoutInfo(JSONHelpers::GridLayoutInfo::Minimal{ .rows = rows, .columns = columns }); FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Minimal{ .rows = rows, .columns = columns });
// Note: The expressions below are NOT equal to C_MULTIPLIER / {rows|columns} and are done // Note: The expressions below are NOT equal to C_MULTIPLIER / {rows|columns} and are done
// like this to make the sum of all percents exactly C_MULTIPLIER // like this to make the sum of all percents exactly C_MULTIPLIER
@@ -578,7 +583,7 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
{ {
const std::wstring guid = guidStr.get(); const std::wstring guid = guidStr.get();
const auto zoneSetSearchResult = JSONHelpers::FancyZonesDataInstance().FindCustomZoneSet(guid); const auto zoneSetSearchResult = FancyZonesDataInstance().FindCustomZoneSet(guid);
if (!zoneSetSearchResult.has_value()) if (!zoneSetSearchResult.has_value())
{ {
@@ -586,9 +591,9 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
} }
const auto& zoneSet = *zoneSetSearchResult; const auto& zoneSet = *zoneSetSearchResult;
if (zoneSet.type == JSONHelpers::CustomLayoutType::Canvas && std::holds_alternative<JSONHelpers::CanvasLayoutInfo>(zoneSet.info)) if (zoneSet.type == FancyZonesDataTypes::CustomLayoutType::Canvas && std::holds_alternative<FancyZonesDataTypes::CanvasLayoutInfo>(zoneSet.info))
{ {
const auto& zoneSetInfo = std::get<JSONHelpers::CanvasLayoutInfo>(zoneSet.info); const auto& zoneSetInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(zoneSet.info);
for (const auto& zone : zoneSetInfo.zones) for (const auto& zone : zoneSetInfo.zones)
{ {
int x = zone.x; int x = zone.x;
@@ -609,9 +614,9 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
return true; return true;
} }
else if (zoneSet.type == JSONHelpers::CustomLayoutType::Grid && std::holds_alternative<JSONHelpers::GridLayoutInfo>(zoneSet.info)) else if (zoneSet.type == FancyZonesDataTypes::CustomLayoutType::Grid && std::holds_alternative<FancyZonesDataTypes::GridLayoutInfo>(zoneSet.info))
{ {
const auto& info = std::get<JSONHelpers::GridLayoutInfo>(zoneSet.info); const auto& info = std::get<FancyZonesDataTypes::GridLayoutInfo>(zoneSet.info);
return CalculateGridZones(workArea, info, spacing); return CalculateGridZones(workArea, info, spacing);
} }
} }
@@ -619,7 +624,7 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
return false; return false;
} }
bool ZoneSet::CalculateGridZones(Rect workArea, JSONHelpers::GridLayoutInfo gridLayoutInfo, int spacing) bool ZoneSet::CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing)
{ {
bool success = true; bool success = true;

View File

@@ -1,7 +1,11 @@
#pragma once #pragma once
#include "Zone.h" #include "Zone.h"
#include "JsonHelpers.h"
namespace FancyZonesDataTypes
{
enum class ZoneSetLayoutType;
}
/** /**
* Class representing single zone layout. ZoneSet is responsible for actual calculation of rectangle coordinates * Class representing single zone layout. ZoneSet is responsible for actual calculation of rectangle coordinates
@@ -16,7 +20,7 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
/** /**
* @returns Type of the zone layout. Layout type can be focus, columns, rows, grid, priority grid or custom. * @returns Type of the zone layout. Layout type can be focus, columns, rows, grid, priority grid or custom.
*/ */
IFACEMETHOD_(JSONHelpers::ZoneSetLayoutType, LayoutType)() = 0; IFACEMETHOD_(FancyZonesDataTypes::ZoneSetLayoutType, LayoutType)() = 0;
/** /**
* Add zone to the zone layout. * Add zone to the zone layout.
* *
@@ -102,23 +106,11 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
IFACEMETHOD_(bool, IsZoneEmpty)(int zoneIndex) = 0; IFACEMETHOD_(bool, IsZoneEmpty)(int zoneIndex) = 0;
}; };
#define VERSION_PERSISTEDDATA 0x0000F00D
struct ZoneSetPersistedData
{
static constexpr inline size_t MAX_ZONES = 40;
DWORD Version{VERSION_PERSISTEDDATA};
WORD LayoutId{};
DWORD ZoneCount{};
JSONHelpers::ZoneSetLayoutType Layout{};
RECT Zones[MAX_ZONES]{};
};
struct ZoneSetConfig struct ZoneSetConfig
{ {
ZoneSetConfig( ZoneSetConfig(
GUID id, GUID id,
JSONHelpers::ZoneSetLayoutType layoutType, FancyZonesDataTypes::ZoneSetLayoutType layoutType,
HMONITOR monitor, HMONITOR monitor,
PCWSTR resolutionKey) noexcept : PCWSTR resolutionKey) noexcept :
Id(id), Id(id),
@@ -129,7 +121,7 @@ struct ZoneSetConfig
} }
GUID Id{}; GUID Id{};
JSONHelpers::ZoneSetLayoutType LayoutType{}; FancyZonesDataTypes::ZoneSetLayoutType LayoutType{};
HMONITOR Monitor{}; HMONITOR Monitor{};
PCWSTR ResolutionKey{}; PCWSTR ResolutionKey{};
}; };

View File

@@ -2,6 +2,8 @@
#include <common/common.h> #include <common/common.h>
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include "ZoneWindow.h" #include "ZoneWindow.h"
#include "trace.h" #include "trace.h"
#include "util.h" #include "util.h"
@@ -14,48 +16,6 @@
namespace ZoneWindowUtils namespace ZoneWindowUtils
{ {
const wchar_t ActiveZoneSetsTmpFileName[] = L"FancyZonesActiveZoneSets.json";
const wchar_t AppliedZoneSetsTmpFileName[] = L"FancyZonesAppliedZoneSets.json";
const wchar_t DeletedCustomZoneSetsTmpFileName[] = L"FancyZonesDeletedCustomZoneSets.json";
const std::wstring& GetTempDirPath()
{
static std::wstring tmpDirPath;
static std::once_flag flag;
std::call_once(flag, []() {
wchar_t buffer[MAX_PATH];
auto charsWritten = GetTempPath(MAX_PATH, buffer);
if (charsWritten > MAX_PATH || (charsWritten == 0))
{
abort();
}
tmpDirPath = std::wstring{ buffer };
});
return tmpDirPath;
}
const std::wstring& GetActiveZoneSetTmpPath()
{
static std::wstring activeZoneSetTmpFileName = GetTempDirPath() + ActiveZoneSetsTmpFileName;
return activeZoneSetTmpFileName;
}
const std::wstring& GetAppliedZoneSetTmpPath()
{
static std::wstring appliedZoneSetTmpFileName = GetTempDirPath() + AppliedZoneSetsTmpFileName;
return appliedZoneSetTmpFileName;
}
const std::wstring& GetDeletedCustomZoneSetsTmpPath()
{
static std::wstring deletedCustomZoneSetsTmpFileName = GetTempDirPath() + DeletedCustomZoneSetsTmpFileName;
return deletedCustomZoneSetsTmpFileName;
}
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId) std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId)
{ {
wchar_t uniqueId[256]{}; // Parsed deviceId + resolution + virtualDesktopId wchar_t uniqueId[256]{}; // Parsed deviceId + resolution + virtualDesktopId
@@ -546,7 +506,7 @@ ZoneWindow::SaveWindowProcessToZoneIndex(HWND window) noexcept
OLECHAR* guidString; OLECHAR* guidString;
if (StringFromCLSID(m_activeZoneSet->Id(), &guidString) == S_OK) if (StringFromCLSID(m_activeZoneSet->Id(), &guidString) == S_OK)
{ {
JSONHelpers::FancyZonesDataInstance().SetAppLastZones(window, m_uniqueId, guidString, zoneIndexSet); FancyZonesDataInstance().SetAppLastZones(window, m_uniqueId, guidString, zoneIndexSet);
} }
CoTaskMemFree(guidString); CoTaskMemFree(guidString);
@@ -619,17 +579,17 @@ ZoneWindow::ClearSelectedZones() noexcept
void ZoneWindow::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept void ZoneWindow::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept
{ {
// If there is not defined zone layout for this work area, created default entry. // If there is not defined zone layout for this work area, created default entry.
JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId); FancyZonesDataInstance().AddDevice(m_uniqueId);
if (!parentUniqueId.empty()) if (!parentUniqueId.empty())
{ {
JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId); FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
} }
CalculateZoneSet(); CalculateZoneSet();
} }
void ZoneWindow::CalculateZoneSet() noexcept void ZoneWindow::CalculateZoneSet() noexcept
{ {
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance(); const auto& fancyZonesData = FancyZonesDataInstance();
const auto deviceInfoData = fancyZonesData.FindDeviceInfo(m_uniqueId); const auto deviceInfoData = fancyZonesData.FindDeviceInfo(m_uniqueId);
if (!deviceInfoData.has_value()) if (!deviceInfoData.has_value())
@@ -639,7 +599,7 @@ void ZoneWindow::CalculateZoneSet() noexcept
const auto& activeZoneSet = deviceInfoData->activeZoneSet; const auto& activeZoneSet = deviceInfoData->activeZoneSet;
if (activeZoneSet.uuid.empty() || activeZoneSet.type == JSONHelpers::ZoneSetLayoutType::Blank) if (activeZoneSet.uuid.empty() || activeZoneSet.type == FancyZonesDataTypes::ZoneSetLayoutType::Blank)
{ {
return; return;
} }
@@ -674,11 +634,11 @@ void ZoneWindow::UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept
wil::unique_cotaskmem_string zoneSetId; wil::unique_cotaskmem_string zoneSetId;
if (SUCCEEDED_LOG(StringFromCLSID(m_activeZoneSet->Id(), &zoneSetId))) if (SUCCEEDED_LOG(StringFromCLSID(m_activeZoneSet->Id(), &zoneSetId)))
{ {
JSONHelpers::ZoneSetData data{ FancyZonesDataTypes::ZoneSetData data{
.uuid = zoneSetId.get(), .uuid = zoneSetId.get(),
.type = m_activeZoneSet->LayoutType() .type = m_activeZoneSet->LayoutType()
}; };
JSONHelpers::FancyZonesDataInstance().SetActiveZoneSet(m_uniqueId, data); FancyZonesDataInstance().SetActiveZoneSet(m_uniqueId, data);
} }
} }
} }

View File

@@ -4,10 +4,6 @@
namespace ZoneWindowUtils namespace ZoneWindowUtils
{ {
const std::wstring& GetActiveZoneSetTmpPath();
const std::wstring& GetAppliedZoneSetTmpPath();
const std::wstring& GetDeletedCustomZoneSetsTmpPath();
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId); std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId);
} }

View File

@@ -20,6 +20,7 @@
#include <shared_mutex> #include <shared_mutex>
#include <functional> #include <functional>
#include <unordered_set> #include <unordered_set>
#include <ShObjIdl.h>
#pragma comment(lib, "windowsapp") #pragma comment(lib, "windowsapp")

View File

@@ -2,7 +2,8 @@
#include "trace.h" #include "trace.h"
#include "lib/ZoneSet.h" #include "lib/ZoneSet.h"
#include "lib/Settings.h" #include "lib/Settings.h"
#include "lib/JsonHelpers.h" #include "lib/FancyZonesData.h"
#include "lib/FancyZonesDataTypes.h"
TRACELOGGING_DEFINE_PROVIDER( TRACELOGGING_DEFINE_PROVIDER(
g_hProvider, g_hProvider,
@@ -71,7 +72,7 @@ void Trace::FancyZones::OnKeyDown(DWORD vkCode, bool win, bool control, bool inM
void Trace::FancyZones::DataChanged() noexcept void Trace::FancyZones::DataChanged() noexcept
{ {
const JSONHelpers::FancyZonesData& data = JSONHelpers::FancyZonesDataInstance(); const FancyZonesData& data = FancyZonesDataInstance();
int appsHistorySize = static_cast<int>(data.GetAppZoneHistoryMap().size()); int appsHistorySize = static_cast<int>(data.GetAppZoneHistoryMap().size());
const auto& customZones = data.GetCustomZoneSetsMap(); const auto& customZones = data.GetCustomZoneSetsMap();
const auto& devices = data.GetDeviceInfoMap(); const auto& devices = data.GetDeviceInfoMap();
@@ -82,15 +83,15 @@ void Trace::FancyZones::DataChanged() noexcept
return; return;
} }
auto getCustomZoneCount = [&data](const std::variant<JSONHelpers::CanvasLayoutInfo, JSONHelpers::GridLayoutInfo>& layoutInfo) -> int { auto getCustomZoneCount = [&data](const std::variant<FancyZonesDataTypes::CanvasLayoutInfo, FancyZonesDataTypes::GridLayoutInfo>& layoutInfo) -> int {
if (std::holds_alternative<JSONHelpers::GridLayoutInfo>(layoutInfo)) if (std::holds_alternative<FancyZonesDataTypes::GridLayoutInfo>(layoutInfo))
{ {
const auto& info = std::get<JSONHelpers::GridLayoutInfo>(layoutInfo); const auto& info = std::get<FancyZonesDataTypes::GridLayoutInfo>(layoutInfo);
return (info.rows() * info.columns()); return (info.rows() * info.columns());
} }
else if (std::holds_alternative<JSONHelpers::CanvasLayoutInfo>(layoutInfo)) else if (std::holds_alternative<FancyZonesDataTypes::CanvasLayoutInfo>(layoutInfo))
{ {
const auto& info = std::get<JSONHelpers::CanvasLayoutInfo>(layoutInfo); const auto& info = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(layoutInfo);
return static_cast<int>(info.zones.size()); return static_cast<int>(info.zones.size());
} }
return 0; return 0;
@@ -108,15 +109,15 @@ void Trace::FancyZones::DataChanged() noexcept
std::wstring activeZoneSetInfo; std::wstring activeZoneSetInfo;
for (const auto& [id, device] : devices) for (const auto& [id, device] : devices)
{ {
const JSONHelpers::ZoneSetLayoutType type = device.activeZoneSet.type; const FancyZonesDataTypes::ZoneSetLayoutType type = device.activeZoneSet.type;
if (!activeZoneSetInfo.empty()) if (!activeZoneSetInfo.empty())
{ {
activeZoneSetInfo += L"; "; activeZoneSetInfo += L"; ";
} }
activeZoneSetInfo += L"type: " + JSONHelpers::TypeToString(type); activeZoneSetInfo += L"type: " + FancyZonesDataTypes::TypeToString(type);
int zoneCount = -1; int zoneCount = -1;
if (type == JSONHelpers::ZoneSetLayoutType::Custom) if (type == FancyZonesDataTypes::ZoneSetLayoutType::Custom)
{ {
const auto& activeCustomZone = customZones.find(device.activeZoneSet.uuid); const auto& activeCustomZone = customZones.find(device.activeZoneSet.uuid);
if (activeCustomZone != customZones.end()) if (activeCustomZone != customZones.end())

View File

@@ -5,6 +5,8 @@
#include <common/common.h> #include <common/common.h>
#include <common/dpi_aware.h> #include <common/dpi_aware.h>
#include <sstream>
namespace namespace
{ {
const wchar_t POWER_TOYS_APP_POWER_LAUCHER[] = L"POWERLAUNCHER.EXE"; const wchar_t POWER_TOYS_APP_POWER_LAUCHER[] = L"POWERLAUNCHER.EXE";
@@ -255,3 +257,81 @@ void RestoreWindowOrigin(HWND window) noexcept
::RemoveProp(window, RESTORE_ORIGIN_STAMP); ::RemoveProp(window, RESTORE_ORIGIN_STAMP);
} }
} }
bool IsValidGuid(const std::wstring& str)
{
GUID id;
return SUCCEEDED(CLSIDFromString(str.c_str(), &id));
}
bool IsValidDeviceId(const std::wstring& str)
{
std::wstring monitorName;
std::wstring temp;
std::vector<std::wstring> parts;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
monitorName = temp;
if (!std::getline(wss, temp, L'_'))
{
return false;
}
monitorName += L"#" + temp;
parts.push_back(monitorName);
}
// Step 2: parse the rest of the id
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 4)
{
return false;
}
/*
Refer to ZoneWindowUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
//check if resolution contain only digits
for (const auto& c : parts[1])
{
std::stoi(std::wstring(&c));
}
for (const auto& c : parts[2])
{
std::stoi(std::wstring(&c));
}
}
catch (const std::exception&)
{
return false;
}
if (!IsValidGuid(parts[3]) || parts[0].empty())
{
return false;
}
return true;
}

View File

@@ -124,3 +124,6 @@ bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedA
void SaveWindowSizeAndOrigin(HWND window) noexcept; void SaveWindowSizeAndOrigin(HWND window) noexcept;
void RestoreWindowSize(HWND window) noexcept; void RestoreWindowSize(HWND window) noexcept;
void RestoreWindowOrigin(HWND window) noexcept; void RestoreWindowOrigin(HWND window) noexcept;
bool IsValidGuid(const std::wstring& str);
bool IsValidDeviceId(const std::wstring& str);

View File

@@ -3,8 +3,8 @@
#include <filesystem> #include <filesystem>
#include <lib/FancyZones.h> #include <lib/FancyZones.h>
#include <lib/FancyZonesData.h>
#include <lib/Settings.h> #include <lib/Settings.h>
#include <common/common.h>
#include "util.h" #include "util.h"
@@ -271,7 +271,7 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr; winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
winrt::com_ptr<IFancyZonesCallback> m_fzCallback = nullptr; winrt::com_ptr<IFancyZonesCallback> m_fzCallback = nullptr;
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance(); FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
std::wstring serializedPowerToySettings(const Settings& settings) std::wstring serializedPowerToySettings(const Settings& settings)
{ {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "lib/JsonHelpers.h" #include "lib/FancyZonesDataTypes.h"
namespace CustomAssert namespace CustomAssert
{ {
@@ -15,7 +15,7 @@ namespace CustomAssert
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(g1 == g2); Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(g1 == g2);
} }
static void AreEqual(JSONHelpers::ZoneSetLayoutType t1, JSONHelpers::ZoneSetLayoutType t2) static void AreEqual(FancyZonesDataTypes::ZoneSetLayoutType t1, FancyZonesDataTypes::ZoneSetLayoutType t2)
{ {
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(t1 == t2); Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(t1 == t2);
} }

View File

@@ -1,4 +1,6 @@
#include "pch.h" #include "pch.h"
#include "lib\FancyZonesData.h"
#include "lib\FancyZonesDataTypes.h"
#include "lib\JsonHelpers.h" #include "lib\JsonHelpers.h"
#include "lib\ZoneSet.h" #include "lib\ZoneSet.h"
@@ -8,14 +10,14 @@
#include <common/settings_helpers.h> #include <common/settings_helpers.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using TZoneSetLayoutType = JSONHelpers::ZoneSetLayoutType; using namespace FancyZonesDataTypes;
namespace FancyZonesUnitTests namespace FancyZonesUnitTests
{ {
TEST_CLASS (ZoneSetUnitTests) TEST_CLASS (ZoneSetUnitTests)
{ {
GUID m_id; GUID m_id;
const TZoneSetLayoutType m_layoutType = TZoneSetLayoutType::Custom; const ZoneSetLayoutType m_layoutType = ZoneSetLayoutType::Custom;
const PCWSTR m_resolutionKey = L"WorkAreaIn"; const PCWSTR m_resolutionKey = L"WorkAreaIn";
winrt::com_ptr<IZoneSet> m_set; winrt::com_ptr<IZoneSet> m_set;
@@ -490,7 +492,7 @@ namespace FancyZonesUnitTests
TEST_METHOD_INITIALIZE(Initialize) TEST_METHOD_INITIALIZE(Initialize)
{ {
ZoneSetConfig config({}, TZoneSetLayoutType::Custom, Mocks::Monitor(), L"WorkAreaIn"); ZoneSetConfig config({}, ZoneSetLayoutType::Custom, Mocks::Monitor(), L"WorkAreaIn");
m_set = MakeZoneSet(config); m_set = MakeZoneSet(config);
// Add a couple of zones. // Add a couple of zones.
@@ -504,7 +506,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (EmptyZonesLeft) TEST_METHOD (EmptyZonesLeft)
{ {
ZoneSetConfig config({}, TZoneSetLayoutType::Custom, Mocks::Monitor(), L"WorkAreaIn"); ZoneSetConfig config({}, ZoneSetLayoutType::Custom, Mocks::Monitor(), L"WorkAreaIn");
auto set = MakeZoneSet(config); auto set = MakeZoneSet(config);
set->MoveWindowIntoZoneByDirection(Mocks::Window(), Mocks::Window(), VK_LEFT, true); set->MoveWindowIntoZoneByDirection(Mocks::Window(), Mocks::Window(), VK_LEFT, true);
@@ -512,7 +514,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (EmptyZonesRight) TEST_METHOD (EmptyZonesRight)
{ {
ZoneSetConfig config({}, TZoneSetLayoutType::Custom, Mocks::Monitor(), L"WorkAreaIn"); ZoneSetConfig config({}, ZoneSetLayoutType::Custom, Mocks::Monitor(), L"WorkAreaIn");
auto set = MakeZoneSet(config); auto set = MakeZoneSet(config);
set->MoveWindowIntoZoneByDirection(Mocks::Window(), Mocks::Window(), VK_RIGHT, true); set->MoveWindowIntoZoneByDirection(Mocks::Window(), Mocks::Window(), VK_RIGHT, true);
@@ -716,7 +718,7 @@ namespace FancyZonesUnitTests
TEST_CLASS (ZoneSetCalculateZonesUnitTests) TEST_CLASS (ZoneSetCalculateZonesUnitTests)
{ {
GUID m_id; GUID m_id;
const TZoneSetLayoutType m_layoutType = TZoneSetLayoutType::Custom; const ZoneSetLayoutType m_layoutType = ZoneSetLayoutType::Custom;
const PCWSTR m_resolutionKey = L"WorkAreaIn"; const PCWSTR m_resolutionKey = L"WorkAreaIn";
winrt::com_ptr<IZoneSet> m_set; winrt::com_ptr<IZoneSet> m_set;
@@ -783,9 +785,9 @@ namespace FancyZonesUnitTests
const int spacing = 10; const int spacing = 10;
const int zoneCount = 10; const int zoneCount = 10;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
@@ -801,9 +803,9 @@ namespace FancyZonesUnitTests
const int spacing = 10; const int spacing = 10;
const int zoneCount = 10; const int zoneCount = 10;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
MONITORINFO info{}; MONITORINFO info{};
@@ -817,9 +819,9 @@ namespace FancyZonesUnitTests
const int spacing = 0; const int spacing = 0;
const int zoneCount = 10; const int zoneCount = 10;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
@@ -836,15 +838,15 @@ namespace FancyZonesUnitTests
const int spacing = -1; const int spacing = -1;
const int zoneCount = 10; const int zoneCount = 10;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
auto result = set->CalculateZones(monitorInfo, zoneCount, spacing); auto result = set->CalculateZones(monitorInfo, zoneCount, spacing);
if (type == static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus)) if (type == static_cast<int>(ZoneSetLayoutType::Focus))
{ {
//Focus doesn't depends on spacing //Focus doesn't depends on spacing
Assert::IsTrue(result); Assert::IsTrue(result);
@@ -861,16 +863,16 @@ namespace FancyZonesUnitTests
{ {
const int zoneCount = 10; const int zoneCount = 10;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
const int spacing = monitorInfo.rcWork.right; const int spacing = monitorInfo.rcWork.right;
auto result = set->CalculateZones(monitorInfo, zoneCount, spacing); auto result = set->CalculateZones(monitorInfo, zoneCount, spacing);
if (type == static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus)) if (type == static_cast<int>(ZoneSetLayoutType::Focus))
{ {
//Focus doesn't depends on spacing //Focus doesn't depends on spacing
Assert::IsTrue(result); Assert::IsTrue(result);
@@ -887,16 +889,16 @@ namespace FancyZonesUnitTests
{ {
const int zoneCount = 10; const int zoneCount = 10;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
const int spacing = monitorInfo.rcWork.bottom; const int spacing = monitorInfo.rcWork.bottom;
auto result = set->CalculateZones(monitorInfo, zoneCount, spacing); auto result = set->CalculateZones(monitorInfo, zoneCount, spacing);
if (type == static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus)) if (type == static_cast<int>(ZoneSetLayoutType::Focus))
{ {
//Focus doesn't depends on spacing //Focus doesn't depends on spacing
Assert::IsTrue(result); Assert::IsTrue(result);
@@ -914,9 +916,9 @@ namespace FancyZonesUnitTests
const int spacing = 10; const int spacing = 10;
const int zoneCount = 0; const int zoneCount = 0;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
@@ -931,12 +933,12 @@ namespace FancyZonesUnitTests
{ {
const int spacing = 1; const int spacing = 1;
for (int type = static_cast<int>(JSONHelpers::ZoneSetLayoutType::Focus); type < static_cast<int>(JSONHelpers::ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
const int spacing = 10; const int spacing = 10;
const int zoneCount = 40; //editor limit const int zoneCount = 40; //editor limit
ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<TZoneSetLayoutType>(type), m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, static_cast<ZoneSetLayoutType>(type), m_monitor, m_resolutionKey);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
@@ -959,7 +961,7 @@ namespace FancyZonesUnitTests
std::filesystem::remove(m_path); std::filesystem::remove(m_path);
} }
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
@@ -977,7 +979,7 @@ namespace FancyZonesUnitTests
Assert::IsTrue(std::filesystem::create_directories(m_path)); Assert::IsTrue(std::filesystem::create_directories(m_path));
Assert::IsTrue(std::filesystem::exists(m_path)); Assert::IsTrue(std::filesystem::exists(m_path));
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
@@ -989,18 +991,16 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromInvalidCanvasLayoutInfo) TEST_METHOD (CustomZoneFromInvalidCanvasLayoutInfo)
{ {
using namespace JSONHelpers;
const std::wstring uuid = L"uuid"; const std::wstring uuid = L"uuid";
const CanvasLayoutInfo info{ -1, 100, { CanvasLayoutInfo::Rect{ -10, -10, 100, 100 }, CanvasLayoutInfo::Rect{ 50, 50, 150, 150 } } }; const CanvasLayoutInfo info{ -1, 100, { CanvasLayoutInfo::Rect{ -10, -10, 100, 100 }, CanvasLayoutInfo::Rect{ 50, 50, 150, 150 } } };
CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info } }; JSONHelpers::CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info } };
json::to_file(m_path, CustomZoneSetJSON::ToJson(expected)); json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
Assert::IsTrue(std::filesystem::exists(m_path)); Assert::IsTrue(std::filesystem::exists(m_path));
const int spacing = 10; const int spacing = 10;
const int zoneCount = static_cast<int>(info.zones.size()); const int zoneCount = static_cast<int>(info.zones.size());
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
@@ -1012,23 +1012,21 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromInvalidGridLayoutInfo) TEST_METHOD (CustomZoneFromInvalidGridLayoutInfo)
{ {
using namespace JSONHelpers;
const std::wstring uuid = L"uuid"; const std::wstring uuid = L"uuid";
const GridLayoutInfo grid(GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ const GridLayoutInfo grid(GridLayoutInfo(GridLayoutInfo::Full{
.rows = 1, .rows = 1,
.columns = 3, .columns = 3,
.rowsPercents = { -100 }, //rows percents are negative .rowsPercents = { -100 }, //rows percents are negative
.columnsPercents = { 2500, 2500 }, //column percents count is invalid .columnsPercents = { 2500, 2500 }, //column percents count is invalid
.cellChildMap = { { 0, 1, 2 } } })); .cellChildMap = { { 0, 1, 2 } } }));
CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } }; JSONHelpers::CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } };
json::to_file(m_path, CustomZoneSetJSON::ToJson(expected)); json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
Assert::IsTrue(std::filesystem::exists(m_path)); Assert::IsTrue(std::filesystem::exists(m_path));
const int spacing = 0; const int spacing = 0;
const int zoneCount = grid.rows() * grid.columns(); const int zoneCount = grid.rows() * grid.columns();
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
@@ -1040,14 +1038,12 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidCanvasLayoutInfo) TEST_METHOD (CustomZoneFromValidCanvasLayoutInfo)
{ {
using namespace JSONHelpers;
//prepare device data //prepare device data
{ {
const std::wstring zoneUuid = L"default_device_id"; const std::wstring zoneUuid = L"default_device_id";
DeviceInfoJSON deviceInfo{ zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } }; JSONHelpers::DeviceInfoJSON deviceInfo{ zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
const std::wstring deviceInfoPath = FancyZonesDataInstance().GetPersistFancyZonesJSONPath() + L".device_info_tmp"; const std::wstring deviceInfoPath = FancyZonesDataInstance().zonesSettingsFileName + L".device_info_tmp";
FancyZonesDataInstance().SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath); JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath);
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath); FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
std::filesystem::remove(deviceInfoPath); std::filesystem::remove(deviceInfoPath);
@@ -1057,15 +1053,15 @@ namespace FancyZonesUnitTests
wil::unique_cotaskmem_string uuid; wil::unique_cotaskmem_string uuid;
Assert::AreEqual(S_OK, StringFromCLSID(m_id, &uuid)); Assert::AreEqual(S_OK, StringFromCLSID(m_id, &uuid));
const CanvasLayoutInfo info{ 123, 321, { CanvasLayoutInfo::Rect{ 0, 0, 100, 100 }, CanvasLayoutInfo::Rect{ 50, 50, 150, 150 } } }; const CanvasLayoutInfo info{ 123, 321, { CanvasLayoutInfo::Rect{ 0, 0, 100, 100 }, CanvasLayoutInfo::Rect{ 50, 50, 150, 150 } } };
CustomZoneSetJSON expected{ uuid.get(), CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info } }; JSONHelpers::CustomZoneSetJSON expected{ uuid.get(), CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info } };
json::to_file(m_path, CustomZoneSetJSON::ToJson(expected)); json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
Assert::IsTrue(std::filesystem::exists(m_path)); Assert::IsTrue(std::filesystem::exists(m_path));
FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(m_path); FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(m_path);
//test //test
const int spacing = 10; const int spacing = 10;
const int zoneCount = static_cast<int>(info.zones.size()); const int zoneCount = static_cast<int>(info.zones.size());
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
@@ -1077,14 +1073,12 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidGridFullLayoutInfo) TEST_METHOD (CustomZoneFromValidGridFullLayoutInfo)
{ {
using namespace JSONHelpers;
//prepare device data //prepare device data
{ {
const std::wstring zoneUuid = L"default_device_id"; const std::wstring zoneUuid = L"default_device_id";
DeviceInfoJSON deviceInfo{ zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } }; JSONHelpers::DeviceInfoJSON deviceInfo{ zoneUuid, DeviceInfoData{ ZoneSetData{ L"uuid", ZoneSetLayoutType::Custom }, true, 16, 3 } };
const std::wstring deviceInfoPath = FancyZonesDataInstance().GetPersistFancyZonesJSONPath() + L".device_info_tmp"; const std::wstring deviceInfoPath = FancyZonesDataInstance().zonesSettingsFileName + L".device_info_tmp";
FancyZonesDataInstance().SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath); JSONHelpers::SerializeDeviceInfoToTmpFile(deviceInfo, deviceInfoPath);
FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath); FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(deviceInfoPath);
std::filesystem::remove(deviceInfoPath); std::filesystem::remove(deviceInfoPath);
@@ -1093,21 +1087,21 @@ namespace FancyZonesUnitTests
//prepare expected data //prepare expected data
wil::unique_cotaskmem_string uuid; wil::unique_cotaskmem_string uuid;
Assert::AreEqual(S_OK, StringFromCLSID(m_id, &uuid)); Assert::AreEqual(S_OK, StringFromCLSID(m_id, &uuid));
const GridLayoutInfo grid(GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{ const GridLayoutInfo grid(GridLayoutInfo(GridLayoutInfo::Full{
.rows = 1, .rows = 1,
.columns = 3, .columns = 3,
.rowsPercents = { 10000 }, .rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 }, .columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } })); .cellChildMap = { { 0, 1, 2 } } }));
CustomZoneSetJSON expected{ uuid.get(), CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } }; JSONHelpers::CustomZoneSetJSON expected{ uuid.get(), CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } };
json::to_file(m_path, CustomZoneSetJSON::ToJson(expected)); json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
Assert::IsTrue(std::filesystem::exists(m_path)); Assert::IsTrue(std::filesystem::exists(m_path));
FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(m_path); FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(m_path);
const int spacing = 10; const int spacing = 10;
const int zoneCount = grid.rows() * grid.columns(); const int zoneCount = grid.rows() * grid.columns();
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)
{ {
@@ -1120,20 +1114,18 @@ namespace FancyZonesUnitTests
TEST_METHOD (CustomZoneFromValidGridMinimalLayoutInfo) TEST_METHOD (CustomZoneFromValidGridMinimalLayoutInfo)
{ {
using namespace JSONHelpers;
const std::wstring uuid = L"uuid"; const std::wstring uuid = L"uuid";
const GridLayoutInfo grid(GridLayoutInfo(JSONHelpers::GridLayoutInfo::Minimal{ const GridLayoutInfo grid(GridLayoutInfo(GridLayoutInfo::Minimal{
.rows = 1, .rows = 1,
.columns = 3 })); .columns = 3 }));
CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } }; JSONHelpers::CustomZoneSetJSON expected{ uuid, CustomZoneSetData{ L"name", CustomLayoutType::Grid, grid } };
json::to_file(m_path, CustomZoneSetJSON::ToJson(expected)); json::to_file(m_path, JSONHelpers::CustomZoneSetJSON::ToJson(expected));
Assert::IsTrue(std::filesystem::exists(m_path)); Assert::IsTrue(std::filesystem::exists(m_path));
const int spacing = 0; const int spacing = 0;
const int zoneCount = grid.rows() * grid.columns(); const int zoneCount = grid.rows() * grid.columns();
ZoneSetConfig m_config = ZoneSetConfig(m_id, TZoneSetLayoutType::Custom, m_monitor, m_resolutionKey); ZoneSetConfig m_config = ZoneSetConfig(m_id, ZoneSetLayoutType::Custom, m_monitor, m_resolutionKey);
auto set = MakeZoneSet(m_config); auto set = MakeZoneSet(m_config);
for (const auto& monitorInfo : m_popularMonitors) for (const auto& monitorInfo : m_popularMonitors)

View File

@@ -7,6 +7,9 @@
#include <lib/ZoneSet.h> #include <lib/ZoneSet.h>
#include <lib/ZoneWindow.h> #include <lib/ZoneWindow.h>
#include <lib/FancyZones.h> #include <lib/FancyZones.h>
#include <lib/FancyZonesData.h>
#include <lib/FancyZonesDataTypes.h>
#include <lib/JsonHelpers.h>
#include "Util.h" #include "Util.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@@ -71,7 +74,7 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZoneWindow> m_zoneWindow; winrt::com_ptr<IZoneWindow> m_zoneWindow;
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance(); FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
TEST_METHOD_INITIALIZE(Init) TEST_METHOD_INITIALIZE(Init)
{ {
@@ -84,13 +87,13 @@ namespace FancyZonesUnitTests
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}"; 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(m_fancyZonesData.activeZoneSetTmpFileName.empty());
Assert::IsFalse(ZoneWindowUtils::GetAppliedZoneSetTmpPath().empty()); Assert::IsFalse(m_fancyZonesData.appliedZoneSetTmpFileName.empty());
Assert::IsFalse(ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath().empty()); Assert::IsFalse(m_fancyZonesData.deletedCustomZoneSetsTmpFileName.empty());
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetActiveZoneSetTmpPath())); Assert::IsFalse(std::filesystem::exists(m_fancyZonesData.activeZoneSetTmpFileName));
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetAppliedZoneSetTmpPath())); Assert::IsFalse(std::filesystem::exists(m_fancyZonesData.appliedZoneSetTmpFileName));
Assert::IsFalse(std::filesystem::exists(ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath())); Assert::IsFalse(std::filesystem::exists(m_fancyZonesData.deletedCustomZoneSetsTmpFileName));
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests"); m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
m_fancyZonesData.clear_data(); m_fancyZonesData.clear_data();
@@ -99,21 +102,21 @@ namespace FancyZonesUnitTests
TEST_METHOD_CLEANUP(Cleanup) TEST_METHOD_CLEANUP(Cleanup)
{ {
//cleanup temp files if were created //cleanup temp files if were created
std::filesystem::remove(ZoneWindowUtils::GetActiveZoneSetTmpPath()); std::filesystem::remove(m_fancyZonesData.activeZoneSetTmpFileName);
std::filesystem::remove(ZoneWindowUtils::GetAppliedZoneSetTmpPath()); std::filesystem::remove(m_fancyZonesData.appliedZoneSetTmpFileName);
std::filesystem::remove(ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath()); std::filesystem::remove(m_fancyZonesData.deletedCustomZoneSetsTmpFileName);
m_zoneWindow = nullptr; m_zoneWindow = nullptr;
} }
winrt::com_ptr<IZoneWindow> InitZoneWindowWithActiveZoneSet() winrt::com_ptr<IZoneWindow> InitZoneWindowWithActiveZoneSet()
{ {
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath(); const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
Assert::IsFalse(std::filesystem::exists(activeZoneSetTempPath)); Assert::IsFalse(std::filesystem::exists(activeZoneSetTempPath));
const auto type = JSONHelpers::ZoneSetLayoutType::Columns; const auto type = FancyZonesDataTypes::ZoneSetLayoutType::Columns;
const auto expectedZoneSet = JSONHelpers::ZoneSetData{ Helpers::CreateGuidString(), type }; const auto expectedZoneSet = FancyZonesDataTypes::ZoneSetData{ Helpers::CreateGuidString(), type };
const auto data = JSONHelpers::DeviceInfoData{ expectedZoneSet, true, 16, 3 }; const auto data = FancyZonesDataTypes::DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data }; const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo); const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json); json::to_file(activeZoneSetTempPath, json);
@@ -203,16 +206,16 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowWithActiveZoneTmpFile) TEST_METHOD(CreateZoneWindowWithActiveZoneTmpFile)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath(); const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++) for (int type = static_cast<int>(ZoneSetLayoutType::Focus); type < static_cast<int>(ZoneSetLayoutType::Custom); type++)
{ {
const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), static_cast<ZoneSetLayoutType>(type) }; const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), static_cast<ZoneSetLayoutType>(type) };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 }; const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data }; const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo); const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json); json::to_file(activeZoneSetTempPath, json);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath); m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
@@ -228,15 +231,15 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneTmpFile) TEST_METHOD(CreateZoneWindowWithActiveCustomZoneTmpFile)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath(); const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom; const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), type }; const auto expectedZoneSet = ZoneSetData{ Helpers::CreateGuidString(), type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 }; const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data }; const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo); const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json); json::to_file(activeZoneSetTempPath, json);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath); m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
@@ -254,25 +257,25 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFile) TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFile)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
//save required data //save required data
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath(); const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath(); const auto appliedZoneSetTempPath = m_fancyZonesData.appliedZoneSetTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom; const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = Helpers::CreateGuidString(); const auto customSetGuid = Helpers::CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type }; const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 }; const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data }; const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo); const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json); json::to_file(activeZoneSetTempPath, json);
const auto info = CanvasLayoutInfo{ const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } } 100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
}; };
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info }; const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
auto customZoneJson = CustomZoneSetJSON::ToJson(CustomZoneSetJSON{ customSetGuid, customZoneData }); auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData });
json::to_file(appliedZoneSetTempPath, customZoneJson); json::to_file(appliedZoneSetTempPath, customZoneJson);
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath); m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath); m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
@@ -290,27 +293,27 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithDeletedCustomZones) TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithDeletedCustomZones)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
//save required data //save required data
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath(); const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath(); const auto appliedZoneSetTempPath = m_fancyZonesData.appliedZoneSetTmpFileName;
const auto deletedZonesTempPath = ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath(); const auto deletedZonesTempPath = m_fancyZonesData.deletedCustomZoneSetsTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom; const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = Helpers::CreateGuidString(); const auto customSetGuid = Helpers::CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type }; const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 }; const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data }; const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo); const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json); json::to_file(activeZoneSetTempPath, json);
const auto info = CanvasLayoutInfo{ const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } } 100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
}; };
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info }; const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
const auto customZoneSet = CustomZoneSetJSON{ customSetGuid, customZoneData }; const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
auto customZoneJson = CustomZoneSetJSON::ToJson(customZoneSet); auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
json::to_file(appliedZoneSetTempPath, customZoneJson); json::to_file(appliedZoneSetTempPath, customZoneJson);
//save same zone as deleted //save same zone as deleted
@@ -336,27 +339,27 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithUnusedDeletedCustomZones) TEST_METHOD(CreateZoneWindowWithActiveCustomZoneAppliedTmpFileWithUnusedDeletedCustomZones)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
//save required data //save required data
const auto activeZoneSetTempPath = ZoneWindowUtils::GetActiveZoneSetTmpPath(); const auto activeZoneSetTempPath = m_fancyZonesData.activeZoneSetTmpFileName;
const auto appliedZoneSetTempPath = ZoneWindowUtils::GetAppliedZoneSetTmpPath(); const auto appliedZoneSetTempPath = m_fancyZonesData.appliedZoneSetTmpFileName;
const auto deletedZonesTempPath = ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath(); const auto deletedZonesTempPath = m_fancyZonesData.deletedCustomZoneSetsTmpFileName;
const ZoneSetLayoutType type = ZoneSetLayoutType::Custom; const ZoneSetLayoutType type = ZoneSetLayoutType::Custom;
const auto customSetGuid = Helpers::CreateGuidString(); const auto customSetGuid = Helpers::CreateGuidString();
const auto expectedZoneSet = ZoneSetData{ customSetGuid, type }; const auto expectedZoneSet = ZoneSetData{ customSetGuid, type };
const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 }; const auto data = DeviceInfoData{ expectedZoneSet, true, 16, 3 };
const auto deviceInfo = DeviceInfoJSON{ m_uniqueId.str(), data }; const auto deviceInfo = JSONHelpers::DeviceInfoJSON{ m_uniqueId.str(), data };
const auto json = DeviceInfoJSON::ToJson(deviceInfo); const auto json = JSONHelpers::DeviceInfoJSON::ToJson(deviceInfo);
json::to_file(activeZoneSetTempPath, json); json::to_file(activeZoneSetTempPath, json);
const auto info = CanvasLayoutInfo{ const auto info = CanvasLayoutInfo{
100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } } 100, 100, std::vector{ CanvasLayoutInfo::Rect{ 0, 0, 100, 100 } }
}; };
const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info }; const auto customZoneData = CustomZoneSetData{ L"name", CustomLayoutType::Canvas, info };
const auto customZoneSet = CustomZoneSetJSON{ customSetGuid, customZoneData }; const auto customZoneSet = JSONHelpers::CustomZoneSetJSON{ customSetGuid, customZoneData };
auto customZoneJson = CustomZoneSetJSON::ToJson(customZoneSet); auto customZoneJson = JSONHelpers::CustomZoneSetJSON::ToJson(customZoneSet);
json::to_file(appliedZoneSetTempPath, customZoneJson); json::to_file(appliedZoneSetTempPath, customZoneJson);
//save different zone as deleted //save different zone as deleted
@@ -383,7 +386,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateZoneWindowClonedFromParent) TEST_METHOD (CreateZoneWindowClonedFromParent)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid; const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10; const int spacing = 10;
@@ -412,7 +415,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (CreateZoneWindowNotClonedFromParent) TEST_METHOD (CreateZoneWindowNotClonedFromParent)
{ {
using namespace JSONHelpers; using namespace FancyZonesDataTypes;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid; const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10; const int spacing = 10;