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

View File

@@ -15,13 +15,5 @@ namespace FancyZonesEditor
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
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 "lib/Settings.h"
#include "lib/ZoneWindow.h"
#include "lib/JsonHelpers.h"
#include "lib/FancyZonesData.h"
#include "lib/ZoneSet.h"
#include "lib/WindowMoveHandler.h"
#include "lib/FancyZonesWinHookEventIDs.h"
@@ -373,7 +373,7 @@ std::vector<int> FancyZones::GetZoneIndexSetFromWorkAreaHistory(
wil::unique_cotaskmem_string 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 {};
@@ -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
{
auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
auto& fancyZonesData = FancyZonesDataInstance();
if (!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId()))
{
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
@@ -650,17 +650,13 @@ void FancyZones::ToggleEditor() noexcept
std::to_wstring(width) + L"_" +
std::to_wstring(height);
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
const auto& fancyZonesData = FancyZonesDataInstance();
const auto deviceInfo = fancyZonesData.FindDeviceInfo(zoneWindow->UniqueId());
if (!deviceInfo.has_value())
if (!fancyZonesData.SerializeDeviceInfoToTmpFile(zoneWindow->UniqueId()))
{
return;
}
JSONHelpers::DeviceInfoJSON deviceInfoJson{ zoneWindow->UniqueId(), *deviceInfo };
fancyZonesData.SerializeDeviceInfoToTmpFile(deviceInfoJson, ZoneWindowUtils::GetActiveZoneSetTmpPath());
const std::wstring params =
/*1*/ editorLocation + L" " +
/*2*/ L"\"" + std::to_wstring(GetCurrentProcessId()) + L"\"";
@@ -842,8 +838,8 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
std::vector<std::wstring> ids{};
if (VirtualDesktopUtils::GetVirtualDesktopIds(ids) && !ids.empty())
{
JSONHelpers::FancyZonesDataInstance().UpdatePrimaryDesktopData(ids[0]);
JSONHelpers::FancyZonesDataInstance().RemoveDeletedDesktops(ids);
FancyZonesDataInstance().UpdatePrimaryDesktopData(ids[0]);
FancyZonesDataInstance().RemoveDeletedDesktops(ids);
}
}
}
@@ -884,7 +880,7 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
if (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{};
if (VirtualDesktopUtils::GetVirtualDesktopIds(active))
{
JSONHelpers::FancyZonesDataInstance().RemoveDeletedDesktops(active);
FancyZonesDataInstance().RemoveDeletedDesktops(active);
}
}
@@ -1084,10 +1080,7 @@ bool FancyZones::IsSplashScreen(HWND window)
void FancyZones::OnEditorExitEvent() noexcept
{
// Collect information about changes in zone layout after editor exited.
JSONHelpers::FancyZonesDataInstance().ParseDeviceInfoFromTmpFile(ZoneWindowUtils::GetActiveZoneSetTmpPath());
JSONHelpers::FancyZonesDataInstance().ParseDeletedCustomZoneSetsFromTmpFile(ZoneWindowUtils::GetDeletedCustomZoneSetsTmpPath());
JSONHelpers::FancyZonesDataInstance().ParseCustomZoneSetFromTmpFile(ZoneWindowUtils::GetAppliedZoneSetTmpPath());
JSONHelpers::FancyZonesDataInstance().SaveFancyZonesData();
FancyZonesDataInstance().ParseDataFromTmpFiles();
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>
<ItemGroup>
<ClInclude Include="FancyZones.h" />
<ClInclude Include="FancyZonesDataTypes.h" />
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
<ClInclude Include="GenericKeyHook.h" />
<ClInclude Include="FancyZonesData.h" />
<ClInclude Include="JsonHelpers.h" />
<ClInclude Include="MonitorWorkAreaHandler.h" />
<ClInclude Include="pch.h" />
@@ -117,8 +119,10 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="FancyZones.cpp" />
<ClCompile Include="FancyZonesDataTypes.cpp" />
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
<ClCompile Include="GenericKeyHook.cpp" />
<ClCompile Include="FancyZonesData.cpp" />
<ClCompile Include="JsonHelpers.cpp" />
<ClCompile Include="MonitorWorkAreaHandler.cpp" />
<ClCompile Include="pch.cpp">

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,278 +1,83 @@
#pragma once
#include <common/settings_helpers.h>
#include "FancyZonesDataTypes.h"
#include <common/json.h>
#include <mutex>
#include <string>
#include <strsafe.h>
#include <unordered_map>
#include <variant>
#include <optional>
#include <vector>
#include <winnt.h>
#include <unordered_map>
namespace JSONHelpers
{
constexpr int MAX_ZONE_COUNT = 50;
#if defined(UNIT_TESTS)
bool isValidGuid(const std::wstring& str);
bool isValidDeviceId(const std::wstring& str);
#endif
enum class ZoneSetLayoutType : int
namespace CanvasLayoutInfoJSON
{
Blank = -1,
Focus,
Columns,
Rows,
Grid,
PriorityGrid,
Custom
};
json::JsonObject ToJson(const FancyZonesDataTypes::CanvasLayoutInfo& canvasInfo);
std::optional<FancyZonesDataTypes::CanvasLayoutInfo> FromJson(const json::JsonObject& infoJson);
}
enum class CustomLayoutType : int
namespace GridLayoutInfoJSON
{
Grid = 0,
Canvas
};
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;
};
json::JsonObject ToJson(const FancyZonesDataTypes::GridLayoutInfo& gridInfo);
std::optional<FancyZonesDataTypes::GridLayoutInfo> FromJson(const json::JsonObject& infoJson);
}
struct CustomZoneSetJSON
{
std::wstring uuid;
CustomZoneSetData data;
FancyZonesDataTypes::CustomZoneSetData data;
static json::JsonObject ToJson(const CustomZoneSetJSON& device);
static std::optional<CustomZoneSetJSON> FromJson(const json::JsonObject& customZoneSet);
};
// TODO(stefan): This needs to be moved to ZoneSet.h (probably)
struct ZoneSetData
namespace ZoneSetDataJSON
{
std::wstring uuid;
ZoneSetLayoutType type;
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;
json::JsonObject ToJson(const FancyZonesDataTypes::ZoneSetData& zoneSet);
std::optional<FancyZonesDataTypes::ZoneSetData> FromJson(const json::JsonObject& zoneSet);
};
struct AppZoneHistoryJSON
{
std::wstring appPath;
std::vector<AppZoneHistoryData> data;
std::vector<FancyZonesDataTypes::AppZoneHistoryData> data;
static json::JsonObject ToJson(const AppZoneHistoryJSON& appZoneHistory);
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
};
struct DeviceInfoData
{
ZoneSetData activeZoneSet;
bool showSpacing;
int spacing;
int zoneCount;
};
struct DeviceInfoJSON
{
std::wstring deviceId;
DeviceInfoData data;
FancyZonesDataTypes::DeviceInfoData data;
static json::JsonObject ToJson(const DeviceInfoJSON& device);
static std::optional<DeviceInfoJSON> FromJson(const json::JsonObject& device);
};
class FancyZonesData
{
mutable std::recursive_mutex dataLock;
using TAppZoneHistoryMap = std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>;
using TDeviceInfoMap = std::unordered_map<std::wstring, FancyZonesDataTypes::DeviceInfoData>;
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomZoneSetData>;
public:
FancyZonesData();
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
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
{
return jsonFilePath;
}
TAppZoneHistoryMap ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeAppZoneHistory(const TAppZoneHistoryMap& appZoneHistoryMap);
inline const std::wstring& GetPersistAppZoneHistoryFilePath() const
{
return appZoneHistoryFilePath;
}
TDeviceInfoMap ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeDeviceInfos(const TDeviceInfoMap& deviceInfoMap);
json::JsonObject GetPersistFancyZonesJSON();
TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
std::optional<DeviceInfoData> FindDeviceInfo(const std::wstring& zoneWindowId) const;
std::optional<CustomZoneSetData> FindCustomZoneSet(const std::wstring& guid) const;
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();
void SerializeDeviceInfoToTmpFile(const JSONHelpers::DeviceInfoJSON& deviceInfo, std::wstring_view tmpFilePath);
std::optional<DeviceInfoJSON> ParseDeviceInfoFromTmpFile(std::wstring_view tmpFilePath);
std::optional<CustomZoneSetJSON> ParseCustomZoneSetFromTmpFile(std::wstring_view tmpFilePath);
std::vector<std::wstring> ParseDeletedCustomZoneSetsFromTmpFile(std::wstring_view tmpFilePath);
}

View File

@@ -12,6 +12,7 @@
#include "VirtualDesktopUtils.h"
#include "lib/SecondaryMouseButtonsHook.h"
#include "lib/GenericKeyHook.h"
#include "lib/FancyZonesData.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -337,7 +338,7 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
wil::unique_cotaskmem_string 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 "lib/ZoneSet.h"
#include "Settings.h"
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include <common/dpi_aware.h>
#include <utility>
namespace
{
constexpr int C_MULTIPLIER = 10000;
constexpr int MAX_ZONE_COUNT = 50;
/*
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
JSONHelpers::GridLayoutInfo predefinedPriorityGridLayouts[11] = {
FancyZonesDataTypes::GridLayoutInfo predefinedPriorityGridLayouts[11] = {
/* 1 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 1,
.rowsPercents = { 10000 },
.columnsPercents = { 10000 },
.cellChildMap = { { 0 } } }),
/* 2 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 2,
.rowsPercents = { 10000 },
.columnsPercents = { 6667, 3333 },
.cellChildMap = { { 0, 1 } } }),
/* 3 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 3,
.rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }),
/* 4 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2,
.columns = 3,
.rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 } } }),
/* 5 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2,
.columns = 3,
.rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 } } }),
/* 6 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 3,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } } }),
/* 7 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 3,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } } }),
/* 8 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } } }),
/* 9 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } } }),
/* 10 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } } }),
/* 11 */
JSONHelpers::GridLayoutInfo(JSONHelpers::GridLayoutInfo::Full{
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
@@ -119,7 +124,7 @@ public:
IFACEMETHODIMP_(GUID)
Id() noexcept { return m_config.Id; }
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
IFACEMETHODIMP_(FancyZonesDataTypes::ZoneSetLayoutType)
LayoutType() noexcept { return m_config.LayoutType; }
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
IFACEMETHODIMP_(std::vector<int>)
@@ -143,11 +148,11 @@ public:
private:
bool CalculateFocusLayout(Rect workArea, int zoneCount) noexcept;
bool CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
bool CalculateGridLayout(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, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, 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;
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
if (zoneCount <= 0 && m_config.LayoutType != JSONHelpers::ZoneSetLayoutType::Custom)
if (zoneCount <= 0 && m_config.LayoutType != FancyZonesDataTypes::ZoneSetLayoutType::Custom)
{
return false;
}
@@ -382,18 +387,18 @@ ZoneSet::CalculateZones(MONITORINFO monitorInfo, int zoneCount, int spacing) noe
bool success = true;
switch (m_config.LayoutType)
{
case JSONHelpers::ZoneSetLayoutType::Focus:
case FancyZonesDataTypes::ZoneSetLayoutType::Focus:
success = CalculateFocusLayout(workArea, zoneCount);
break;
case JSONHelpers::ZoneSetLayoutType::Columns:
case JSONHelpers::ZoneSetLayoutType::Rows:
case FancyZonesDataTypes::ZoneSetLayoutType::Columns:
case FancyZonesDataTypes::ZoneSetLayoutType::Rows:
success = CalculateColumnsAndRowsLayout(workArea, m_config.LayoutType, zoneCount, spacing);
break;
case JSONHelpers::ZoneSetLayoutType::Grid:
case JSONHelpers::ZoneSetLayoutType::PriorityGrid:
case FancyZonesDataTypes::ZoneSetLayoutType::Grid:
case FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid:
success = CalculateGridLayout(workArea, m_config.LayoutType, zoneCount, spacing);
break;
case JSONHelpers::ZoneSetLayoutType::Custom:
case FancyZonesDataTypes::ZoneSetLayoutType::Custom:
success = CalculateCustomLayout(workArea, spacing);
break;
}
@@ -445,14 +450,14 @@ bool ZoneSet::CalculateFocusLayout(Rect workArea, int zoneCount) noexcept
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;
long totalWidth;
long totalHeight;
if (type == JSONHelpers::ZoneSetLayoutType::Columns)
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{
totalWidth = workArea.width() - (spacing * (zoneCount + 1));
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}.
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;
bottom = totalHeight + spacing;
@@ -491,7 +496,7 @@ bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetL
RECT focusZoneRect{ left, top, right, bottom };
AddZone(MakeZone(focusZoneRect));
if (type == JSONHelpers::ZoneSetLayoutType::Columns)
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{
left = right + spacing;
}
@@ -504,10 +509,10 @@ bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, JSONHelpers::ZoneSetL
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);
if (type == JSONHelpers::ZoneSetLayoutType::PriorityGrid && zoneCount < count)
const auto count = sizeof(predefinedPriorityGridLayouts) / sizeof(FancyZonesDataTypes::GridLayoutInfo);
if (type == FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid && zoneCount < count)
{
return CalculateUniquePriorityGridLayout(workArea, zoneCount, spacing);
}
@@ -528,7 +533,7 @@ bool ZoneSet::CalculateGridLayout(Rect workArea, JSONHelpers::ZoneSetLayoutType
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
// 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 auto zoneSetSearchResult = JSONHelpers::FancyZonesDataInstance().FindCustomZoneSet(guid);
const auto zoneSetSearchResult = FancyZonesDataInstance().FindCustomZoneSet(guid);
if (!zoneSetSearchResult.has_value())
{
@@ -586,9 +591,9 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
}
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)
{
int x = zone.x;
@@ -609,9 +614,9 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
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);
}
}
@@ -619,7 +624,7 @@ bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
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;

View File

@@ -1,7 +1,11 @@
#pragma once
#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
@@ -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.
*/
IFACEMETHOD_(JSONHelpers::ZoneSetLayoutType, LayoutType)() = 0;
IFACEMETHOD_(FancyZonesDataTypes::ZoneSetLayoutType, LayoutType)() = 0;
/**
* 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;
};
#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
{
ZoneSetConfig(
GUID id,
JSONHelpers::ZoneSetLayoutType layoutType,
FancyZonesDataTypes::ZoneSetLayoutType layoutType,
HMONITOR monitor,
PCWSTR resolutionKey) noexcept :
Id(id),
@@ -129,7 +121,7 @@ struct ZoneSetConfig
}
GUID Id{};
JSONHelpers::ZoneSetLayoutType LayoutType{};
FancyZonesDataTypes::ZoneSetLayoutType LayoutType{};
HMONITOR Monitor{};
PCWSTR ResolutionKey{};
};

View File

@@ -2,6 +2,8 @@
#include <common/common.h>
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include "ZoneWindow.h"
#include "trace.h"
#include "util.h"
@@ -14,48 +16,6 @@
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)
{
wchar_t uniqueId[256]{}; // Parsed deviceId + resolution + virtualDesktopId
@@ -546,7 +506,7 @@ ZoneWindow::SaveWindowProcessToZoneIndex(HWND window) noexcept
OLECHAR* guidString;
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);
@@ -619,17 +579,17 @@ ZoneWindow::ClearSelectedZones() noexcept
void ZoneWindow::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept
{
// 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())
{
JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
}
CalculateZoneSet();
}
void ZoneWindow::CalculateZoneSet() noexcept
{
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
const auto& fancyZonesData = FancyZonesDataInstance();
const auto deviceInfoData = fancyZonesData.FindDeviceInfo(m_uniqueId);
if (!deviceInfoData.has_value())
@@ -639,7 +599,7 @@ void ZoneWindow::CalculateZoneSet() noexcept
const auto& activeZoneSet = deviceInfoData->activeZoneSet;
if (activeZoneSet.uuid.empty() || activeZoneSet.type == JSONHelpers::ZoneSetLayoutType::Blank)
if (activeZoneSet.uuid.empty() || activeZoneSet.type == FancyZonesDataTypes::ZoneSetLayoutType::Blank)
{
return;
}
@@ -674,11 +634,11 @@ void ZoneWindow::UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept
wil::unique_cotaskmem_string zoneSetId;
if (SUCCEEDED_LOG(StringFromCLSID(m_activeZoneSet->Id(), &zoneSetId)))
{
JSONHelpers::ZoneSetData data{
FancyZonesDataTypes::ZoneSetData data{
.uuid = zoneSetId.get(),
.type = m_activeZoneSet->LayoutType()
};
JSONHelpers::FancyZonesDataInstance().SetActiveZoneSet(m_uniqueId, data);
FancyZonesDataInstance().SetActiveZoneSet(m_uniqueId, data);
}
}
}

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,8 @@
#include <common/common.h>
#include <common/dpi_aware.h>
#include <sstream>
namespace
{
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);
}
}
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 RestoreWindowSize(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 <lib/FancyZones.h>
#include <lib/FancyZonesData.h>
#include <lib/Settings.h>
#include <common/common.h>
#include "util.h"
@@ -271,7 +271,7 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
winrt::com_ptr<IFancyZonesCallback> m_fzCallback = nullptr;
JSONHelpers::FancyZonesData& m_fancyZonesData = JSONHelpers::FancyZonesDataInstance();
FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
std::wstring serializedPowerToySettings(const Settings& settings)
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
#pragma once
#include "lib/JsonHelpers.h"
#include "lib/FancyZonesDataTypes.h"
namespace CustomAssert
{
@@ -15,7 +15,7 @@ namespace CustomAssert
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);
}

View File

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

View File

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