mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
[FancyZones] Split zones-settings: layout templates (#15588)
This commit is contained in:
@@ -170,6 +170,12 @@ void FancyZonesData::ReplaceZoneSettingsFileFromOlderVersions()
|
||||
//deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
|
||||
//customZoneSetsMap = JSONHelpers::ParseCustomZoneSets(fancyZonesDataJSON);
|
||||
|
||||
auto templates = JSONHelpers::ParseLayoutTemplates(fancyZonesDataJSON);
|
||||
if (templates)
|
||||
{
|
||||
JSONHelpers::SaveLayoutTemplates(templates.value());
|
||||
}
|
||||
|
||||
auto quickKeysMap = JSONHelpers::ParseQuickKeys(fancyZonesDataJSON);
|
||||
if (quickKeysMap)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace FancyZonesUnitTests
|
||||
class WorkAreaUnitTests;
|
||||
class WorkAreaCreationUnitTests;
|
||||
class LayoutHotkeysUnitTests;
|
||||
class LayoutTemplatesUnitTests;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -90,6 +91,7 @@ private:
|
||||
friend class FancyZonesUnitTests::WorkAreaCreationUnitTests;
|
||||
friend class FancyZonesUnitTests::ZoneSetCalculateZonesUnitTests;
|
||||
friend class FancyZonesUnitTests::LayoutHotkeysUnitTests;
|
||||
friend class FancyZonesUnitTests::LayoutTemplatesUnitTests;
|
||||
|
||||
inline void SetDeviceInfo(const FancyZonesDataTypes::DeviceIdData& deviceId, FancyZonesDataTypes::DeviceInfoData data)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <FancyZonesLib/ModuleConstants.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
namespace NonLocalizable
|
||||
{
|
||||
namespace LayoutTemplatesIds
|
||||
{
|
||||
const static wchar_t* LayoutTemplatesArrayID = L"layout-templates";
|
||||
}
|
||||
}
|
||||
|
||||
class LayoutTemplates
|
||||
{
|
||||
public:
|
||||
inline static std::wstring LayoutTemplatesFileName()
|
||||
{
|
||||
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
|
||||
#if defined(UNIT_TESTS)
|
||||
return saveFolderPath + L"\\test-layout-templates.json";
|
||||
#endif
|
||||
return saveFolderPath + L"\\layout-templates.json";
|
||||
}
|
||||
};
|
||||
@@ -39,6 +39,7 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="FancyZones.h" />
|
||||
<ClInclude Include="FancyZonesDataTypes.h" />
|
||||
<ClInclude Include="FancyZonesData\LayoutTemplates.h" />
|
||||
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
|
||||
<ClInclude Include="GenericKeyHook.h" />
|
||||
<ClInclude Include="FancyZonesData.h" />
|
||||
|
||||
@@ -96,6 +96,9 @@
|
||||
<ClInclude Include="ModuleConstants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FancyZonesData\LayoutTemplates.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "util.h"
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutHotkeys.h>
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutTemplates.h>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
|
||||
@@ -583,23 +584,9 @@ namespace JSONHelpers
|
||||
auto before = json::from_file(zonesSettingsFileName);
|
||||
|
||||
json::JsonObject root{};
|
||||
json::JsonArray templates{};
|
||||
|
||||
try
|
||||
{
|
||||
if (before.has_value() && before->HasKey(NonLocalizable::Templates))
|
||||
{
|
||||
templates = before->GetNamedArray(NonLocalizable::Templates);
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
root.SetNamedValue(NonLocalizable::DevicesStr, JSONHelpers::SerializeDeviceInfos(deviceInfoMap));
|
||||
root.SetNamedValue(NonLocalizable::CustomZoneSetsStr, JSONHelpers::SerializeCustomZoneSets(customZoneSetsMap));
|
||||
root.SetNamedValue(NonLocalizable::Templates, templates);
|
||||
|
||||
if (!before.has_value() || before.value().Stringify() != root.Stringify())
|
||||
{
|
||||
@@ -769,4 +756,25 @@ namespace JSONHelpers
|
||||
root.SetNamedValue(NonLocalizable::LayoutHotkeysIds::LayoutHotkeysArrayID, keysArray);
|
||||
json::to_file(LayoutHotkeys::LayoutHotkeysFileName(), root);
|
||||
}
|
||||
|
||||
std::optional<json::JsonArray> ParseLayoutTemplates(const json::JsonObject& fancyZonesDataJSON)
|
||||
{
|
||||
try
|
||||
{
|
||||
return fancyZonesDataJSON.GetNamedArray(NonLocalizable::Templates);
|
||||
}
|
||||
catch (const winrt::hresult_error& e)
|
||||
{
|
||||
Logger::error(L"Parsing layout templates error: {}", e.message());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SaveLayoutTemplates(const json::JsonArray& templates)
|
||||
{
|
||||
json::JsonObject root{};
|
||||
root.SetNamedValue(NonLocalizable::LayoutTemplatesIds::LayoutTemplatesArrayID, templates);
|
||||
json::to_file(LayoutTemplates::LayoutTemplatesFileName(), root);
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,11 @@ namespace JSONHelpers
|
||||
TCustomZoneSetsMap ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
|
||||
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap);
|
||||
|
||||
// replace zone-settings: layout hotkeys
|
||||
// replace zones-settings: layout hotkeys
|
||||
std::optional<TLayoutQuickKeysMap> ParseQuickKeys(const json::JsonObject& fancyZonesDataJSON);
|
||||
void SaveLayoutHotkeys(const TLayoutQuickKeysMap& quickKeysMap);
|
||||
|
||||
// replace zones-settings: layout templates
|
||||
std::optional<json::JsonArray> ParseLayoutTemplates(const json::JsonObject& fancyZonesDataJSON);
|
||||
void SaveLayoutTemplates(const json::JsonArray& templates);
|
||||
}
|
||||
|
||||
@@ -19,46 +19,6 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace FancyZonesUnitTests
|
||||
{
|
||||
void compareJsonObjects(const json::JsonObject& expected, const json::JsonObject& actual, bool recursive = true)
|
||||
{
|
||||
auto iter = expected.First();
|
||||
while (iter.HasCurrent())
|
||||
{
|
||||
const auto key = iter.Current().Key();
|
||||
Assert::IsTrue(actual.HasKey(key), key.c_str());
|
||||
|
||||
const std::wstring expectedStringified = iter.Current().Value().Stringify().c_str();
|
||||
const std::wstring actualStringified = actual.GetNamedValue(key).Stringify().c_str();
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
json::JsonObject expectedJson;
|
||||
if (json::JsonObject::TryParse(expectedStringified, expectedJson))
|
||||
{
|
||||
json::JsonObject actualJson;
|
||||
if (json::JsonObject::TryParse(actualStringified, actualJson))
|
||||
{
|
||||
compareJsonObjects(expectedJson, actualJson, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert::IsTrue(false, key.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert::AreEqual(expectedStringified, actualStringified, key.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert::AreEqual(expectedStringified, actualStringified, key.c_str());
|
||||
}
|
||||
|
||||
iter.MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CLASS (IdValidationUnitTest)
|
||||
{
|
||||
TEST_METHOD (GuidValid)
|
||||
@@ -201,7 +161,8 @@ namespace FancyZonesUnitTests
|
||||
info.sensitivityRadius = 50;
|
||||
|
||||
auto actual = CanvasLayoutInfoJSON::ToJson(info);
|
||||
compareJsonObjects(m_json, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(m_json, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJson)
|
||||
@@ -465,7 +426,8 @@ namespace FancyZonesUnitTests
|
||||
GridLayoutInfo info = m_info;
|
||||
|
||||
auto actual = GridLayoutInfoJSON::ToJson(info);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +448,8 @@ namespace FancyZonesUnitTests
|
||||
info.m_spacing = 99;
|
||||
|
||||
auto actual = GridLayoutInfoJSON::ToJson(info);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJson)
|
||||
@@ -605,7 +568,8 @@ namespace FancyZonesUnitTests
|
||||
expected.SetNamedValue(L"info", GridLayoutInfoJSON::ToJson(std::get<GridLayoutInfo>(zoneSet.data.info)));
|
||||
|
||||
auto actual = CustomZoneSetJSON::ToJson(zoneSet);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (ToJsonCanvas)
|
||||
@@ -616,7 +580,8 @@ namespace FancyZonesUnitTests
|
||||
expected.SetNamedValue(L"info", CanvasLayoutInfoJSON::ToJson(std::get<CanvasLayoutInfo>(zoneSet.data.info)));
|
||||
|
||||
auto actual = CustomZoneSetJSON::ToJson(zoneSet);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJsonGrid)
|
||||
@@ -718,7 +683,8 @@ namespace FancyZonesUnitTests
|
||||
json::JsonObject expected = json::JsonObject::Parse(L"{\"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\", \"type\": \"rows\"}");
|
||||
ZoneSetData data{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Rows };
|
||||
const auto actual = ZoneSetDataJSON::ToJson(data);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJsonGeneral)
|
||||
@@ -782,7 +748,8 @@ namespace FancyZonesUnitTests
|
||||
json::JsonObject expected = json::JsonObject::Parse(L"{\"app-path\": \"appPath\", \"history\":[{\"zone-index-set\": [54321], \"device-id\": \"device-id_0_0_{00000000-0000-0000-0000-000000000000}\", \"zoneset-uuid\": \"zoneset-uuid\"}]}");
|
||||
|
||||
auto actual = AppZoneHistoryJSON::ToJson(appZoneHistory);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJson)
|
||||
@@ -860,7 +827,8 @@ namespace FancyZonesUnitTests
|
||||
|
||||
auto actual = AppZoneHistoryJSON::ToJson(appZoneHistory);
|
||||
std::wstring s = actual.Stringify().c_str();
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJsonMultipleDesktopAppHistory)
|
||||
@@ -912,7 +880,8 @@ namespace FancyZonesUnitTests
|
||||
json::JsonObject expected = m_defaultJson;
|
||||
|
||||
auto actual = DeviceInfoJSON::ToJson(deviceInfo);
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (FromJson)
|
||||
@@ -1036,15 +1005,6 @@ namespace FancyZonesUnitTests
|
||||
HINSTANCE m_hInst{};
|
||||
FancyZonesData& m_fzData = FancyZonesDataInstance();
|
||||
|
||||
void compareJsonArrays(const json::JsonArray& expected, const json::JsonArray& actual)
|
||||
{
|
||||
Assert::AreEqual(expected.Size(), actual.Size());
|
||||
for (uint32_t i = 0; i < expected.Size(); i++)
|
||||
{
|
||||
compareJsonObjects(expected.GetObjectAt(i), actual.GetObjectAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
{
|
||||
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
|
||||
@@ -1181,7 +1141,8 @@ namespace FancyZonesUnitTests
|
||||
const auto& deviceInfoMap = ParseDeviceInfos(expected);
|
||||
|
||||
auto actual = SerializeDeviceInfos(deviceInfoMap);
|
||||
compareJsonArrays(expectedDevices, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expectedDevices, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (AppZoneHistoryParseSingle)
|
||||
@@ -1353,7 +1314,8 @@ namespace FancyZonesUnitTests
|
||||
auto appZoneHistoryMap = ParseAppZoneHistory(json);
|
||||
|
||||
const auto& actual = SerializeAppZoneHistory(appZoneHistoryMap);
|
||||
compareJsonArrays(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (AppZoneHistorySerializeMany)
|
||||
@@ -1393,7 +1355,8 @@ namespace FancyZonesUnitTests
|
||||
const auto& appZoneHistoryMap = ParseAppZoneHistory(json);
|
||||
|
||||
const auto& actual = SerializeAppZoneHistory(appZoneHistoryMap);
|
||||
compareJsonArrays(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (AppZoneHistorySerializeEmpty)
|
||||
@@ -1405,7 +1368,8 @@ namespace FancyZonesUnitTests
|
||||
const auto& appZoneHistoryMap = ParseAppZoneHistory(json);
|
||||
|
||||
const auto& actual = SerializeAppZoneHistory(appZoneHistoryMap);
|
||||
compareJsonArrays(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (CustomZoneSetsParseSingle)
|
||||
@@ -1519,7 +1483,8 @@ namespace FancyZonesUnitTests
|
||||
const auto& customZoneSetsMap = ParseCustomZoneSets(json);
|
||||
|
||||
auto actual = SerializeCustomZoneSets(customZoneSetsMap);
|
||||
compareJsonArrays(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (CustomZoneSetsSerializeMany)
|
||||
@@ -1542,7 +1507,8 @@ namespace FancyZonesUnitTests
|
||||
const auto& customZoneSetsMap = ParseCustomZoneSets(json);
|
||||
|
||||
auto actual = SerializeCustomZoneSets(customZoneSetsMap);
|
||||
compareJsonArrays(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (CustomZoneSetsSerializeEmpty)
|
||||
@@ -1554,7 +1520,8 @@ namespace FancyZonesUnitTests
|
||||
const auto& customZoneSetsMap = ParseCustomZoneSets(json);
|
||||
|
||||
auto actual = SerializeCustomZoneSets(customZoneSetsMap);
|
||||
compareJsonArrays(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonArrays(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (SetActiveZoneSet)
|
||||
@@ -1756,36 +1723,6 @@ namespace FancyZonesUnitTests
|
||||
Assert::IsTrue(actual);
|
||||
}
|
||||
|
||||
TEST_METHOD (SaveFancyZonesDataWithTemplates)
|
||||
{
|
||||
FancyZonesData data;
|
||||
data.SetSettingsModulePath(m_moduleName);
|
||||
const auto& jsonPath = data.zonesSettingsFileName;
|
||||
|
||||
// json with templates
|
||||
json::JsonObject expectedJsonObj;
|
||||
json::JsonObject templateObj = json::JsonObject::Parse(L"{\"type\": \"focus\", \"show-spacing\": false, \"spacing\": 15, \"zone-count\": 7, \"sensitivity-radius\": 25}");
|
||||
json::JsonArray templatesArray{};
|
||||
templatesArray.Append(templateObj);
|
||||
expectedJsonObj.SetNamedValue(L"devices", json::JsonArray{});
|
||||
expectedJsonObj.SetNamedValue(L"custom-zone-sets", json::JsonArray{});
|
||||
expectedJsonObj.SetNamedValue(L"templates", templatesArray);
|
||||
|
||||
// write json with templates to file
|
||||
json::to_file(jsonPath, expectedJsonObj);
|
||||
|
||||
data.SaveAppZoneHistoryAndZoneSettings();
|
||||
|
||||
// verify that file was written successfully
|
||||
Assert::IsTrue(std::filesystem::exists(jsonPath));
|
||||
|
||||
// verify that templates were not changed after calling SaveFancyZonesData()
|
||||
std::wstring str;
|
||||
std::wifstream { jsonPath, std::ios::binary } >> str;
|
||||
json::JsonObject actualJson = json::JsonObject::Parse(str);
|
||||
compareJsonObjects(expectedJsonObj, actualJson);
|
||||
}
|
||||
|
||||
TEST_METHOD (AppLastZoneIndex)
|
||||
{
|
||||
const FancyZonesDataTypes::DeviceIdData deviceId{ L"device-id" };
|
||||
@@ -2240,7 +2177,8 @@ namespace FancyZonesUnitTests
|
||||
|
||||
const auto actual = MonitorInfo::ToJson(monitor);
|
||||
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD(EditorArgsToJson)
|
||||
@@ -2258,7 +2196,8 @@ namespace FancyZonesUnitTests
|
||||
const auto expected = json::JsonObject::Parse(expectedStr);
|
||||
const auto actual = EditorArgs::ToJson(args);
|
||||
|
||||
compareJsonObjects(expected, actual);
|
||||
auto res = CustomAssert::CompareJsonObjects(expected, actual);
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "pch.h"
|
||||
#include <filesystem>
|
||||
|
||||
#include <FancyZonesLib/FancyZonesData.h>
|
||||
#include <FancyZonesLib/FancyZonesData/LayoutTemplates.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
|
||||
|
||||
namespace FancyZonesUnitTests
|
||||
{
|
||||
TEST_CLASS (LayoutTemplatesUnitTests)
|
||||
{
|
||||
FancyZonesData& m_fzData = FancyZonesDataInstance();
|
||||
std::wstring m_testFolder = L"FancyZonesUnitTests";
|
||||
|
||||
TEST_METHOD_INITIALIZE(Init)
|
||||
{
|
||||
m_fzData.clear_data();
|
||||
m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests");
|
||||
}
|
||||
|
||||
TEST_METHOD_CLEANUP(CleanUp)
|
||||
{
|
||||
std::filesystem::remove_all(LayoutTemplates::LayoutTemplatesFileName());
|
||||
std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_testFolder));
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveLayoutHotkeysFromZonesSettings)
|
||||
{
|
||||
// prepare
|
||||
json::JsonObject root{};
|
||||
json::JsonArray devicesArray{}, customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{};
|
||||
root.SetNamedValue(L"devices", devicesArray);
|
||||
root.SetNamedValue(L"custom-zone-sets", customLayoutsArray);
|
||||
root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray);
|
||||
|
||||
{
|
||||
json::JsonObject layout{};
|
||||
layout.SetNamedValue(L"type", json::value(L"blank"));
|
||||
layout.SetNamedValue(L"show-spacing", json::value(false));
|
||||
layout.SetNamedValue(L"spacing", json::value(0));
|
||||
layout.SetNamedValue(L"zone-count", json::value(0));
|
||||
layout.SetNamedValue(L"sensitivity-radius", json::value(0));
|
||||
templateLayoutsArray.Append(layout);
|
||||
}
|
||||
{
|
||||
json::JsonObject layout{};
|
||||
layout.SetNamedValue(L"type", json::value(L"grid"));
|
||||
layout.SetNamedValue(L"show-spacing", json::value(true));
|
||||
layout.SetNamedValue(L"spacing", json::value(-10));
|
||||
layout.SetNamedValue(L"zone-count", json::value(4));
|
||||
layout.SetNamedValue(L"sensitivity-radius", json::value(30));
|
||||
templateLayoutsArray.Append(layout);
|
||||
}
|
||||
|
||||
root.SetNamedValue(L"templates", templateLayoutsArray);
|
||||
json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root);
|
||||
|
||||
// test
|
||||
m_fzData.ReplaceZoneSettingsFileFromOlderVersions();
|
||||
|
||||
auto result = json::from_file(LayoutTemplates::LayoutTemplatesFileName());
|
||||
Assert::IsTrue(result.has_value());
|
||||
Assert::IsTrue(result.value().HasKey(NonLocalizable::LayoutTemplatesIds::LayoutTemplatesArrayID));
|
||||
auto res = CustomAssert::CompareJsonArrays(templateLayoutsArray, result.value().GetNamedArray(NonLocalizable::LayoutTemplatesIds::LayoutTemplatesArrayID));
|
||||
Assert::IsTrue(res.first, res.second.c_str());
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveLayoutHotkeysFromZonesSettingsNoTemplates)
|
||||
{
|
||||
// prepare
|
||||
json::JsonObject root{};
|
||||
json::JsonArray devicesArray{}, customLayoutsArray{}, quickLayoutKeysArray{};
|
||||
root.SetNamedValue(L"devices", devicesArray);
|
||||
root.SetNamedValue(L"custom-zone-sets", customLayoutsArray);
|
||||
root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray);
|
||||
json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root);
|
||||
|
||||
// test
|
||||
m_fzData.ReplaceZoneSettingsFileFromOlderVersions();
|
||||
auto result = json::from_file(LayoutTemplates::LayoutTemplatesFileName());
|
||||
Assert::IsFalse(result.has_value());
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveLayoutHotkeysFromZonesSettingsNoFile)
|
||||
{
|
||||
// test
|
||||
m_fzData.ReplaceZoneSettingsFileFromOlderVersions();
|
||||
auto result = json::from_file(LayoutTemplates::LayoutTemplatesFileName());
|
||||
Assert::IsFalse(result.has_value());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -45,6 +45,7 @@
|
||||
<ClCompile Include="FancyZonesSettings.Spec.cpp" />
|
||||
<ClCompile Include="JsonHelpers.Tests.cpp" />
|
||||
<ClCompile Include="LayoutHotkeysTests.Spec.cpp" />
|
||||
<ClCompile Include="LayoutTemplatesTests.Spec.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
<ClCompile Include="LayoutHotkeysTests.Spec.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LayoutTemplatesTests.Spec.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
|
||||
@@ -28,6 +28,76 @@ namespace CustomAssert
|
||||
Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(a1[i].first == a2[i].first);
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<bool, std::wstring> CompareJsonObjects(const json::JsonObject& expected, const json::JsonObject& actual, bool recursive = true)
|
||||
{
|
||||
auto iter = expected.First();
|
||||
while (iter.HasCurrent())
|
||||
{
|
||||
const auto key = iter.Current().Key();
|
||||
if (!actual.HasKey(key))
|
||||
{
|
||||
return std::make_pair(false, key.c_str());
|
||||
}
|
||||
|
||||
const std::wstring expectedStringified = iter.Current().Value().Stringify().c_str();
|
||||
const std::wstring actualStringified = actual.GetNamedValue(key).Stringify().c_str();
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
json::JsonObject expectedJson;
|
||||
if (json::JsonObject::TryParse(expectedStringified, expectedJson))
|
||||
{
|
||||
json::JsonObject actualJson;
|
||||
if (json::JsonObject::TryParse(actualStringified, actualJson))
|
||||
{
|
||||
CompareJsonObjects(expectedJson, actualJson, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(false, key.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expectedStringified != actualStringified)
|
||||
{
|
||||
return std::make_pair(false, key.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (expectedStringified != actualStringified)
|
||||
{
|
||||
return std::make_pair(false, key.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
iter.MoveNext();
|
||||
}
|
||||
|
||||
return std::make_pair(true, L"");
|
||||
}
|
||||
|
||||
static std::pair<bool, std::wstring> CompareJsonArrays(const json::JsonArray& expected, const json::JsonArray& actual)
|
||||
{
|
||||
if (expected.Size() != actual.Size())
|
||||
{
|
||||
return std::make_pair(false, L"Array sizes don't match");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < expected.Size(); i++)
|
||||
{
|
||||
auto res = CustomAssert::CompareJsonObjects(expected.GetObjectAt(i), actual.GetObjectAt(i));
|
||||
if (!res.first)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(true, L"");
|
||||
}
|
||||
}
|
||||
|
||||
namespace Mocks
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace FancyZonesEditor
|
||||
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
|
||||
}
|
||||
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
App.FancyZonesEditorIO.SerializeLayoutTemplates();
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
@@ -281,6 +281,8 @@ namespace FancyZonesEditor
|
||||
CancelLayoutChanges();
|
||||
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
App.FancyZonesEditorIO.SerializeLayoutHotkeys();
|
||||
App.FancyZonesEditorIO.SerializeLayoutTemplates();
|
||||
App.Overlay.CloseLayoutWindow();
|
||||
App.Current.Shutdown();
|
||||
}
|
||||
@@ -424,6 +426,8 @@ namespace FancyZonesEditor
|
||||
}
|
||||
|
||||
App.FancyZonesEditorIO.SerializeZoneSettings();
|
||||
App.FancyZonesEditorIO.SerializeLayoutTemplates();
|
||||
App.FancyZonesEditorIO.SerializeLayoutHotkeys();
|
||||
|
||||
// reset selected model
|
||||
Select(_settings.AppliedModel);
|
||||
|
||||
@@ -393,6 +393,15 @@ namespace FancyZonesEditor.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to An error occurred while parsing template layouts..
|
||||
/// </summary>
|
||||
public static string Error_Parsing_Layout_Templates_Message {
|
||||
get {
|
||||
return ResourceManager.GetString("Error_Parsing_Layout_Templates_Message", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to A layout that contained invalid data has been removed..
|
||||
/// </summary>
|
||||
|
||||
@@ -386,4 +386,7 @@
|
||||
<data name="Error_Parsing_Layout_Hotkeys_Message" xml:space="preserve">
|
||||
<value>An error occurred while parsing layout hotkeys.</value>
|
||||
</data>
|
||||
<data name="Error_Parsing_Layout_Templates_Message" xml:space="preserve">
|
||||
<value>An error occurred while parsing template layouts.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -30,6 +30,7 @@ namespace FancyZonesEditor.Utils
|
||||
// Non-localizable strings: Files
|
||||
private const string ZonesSettingsFile = "\\Microsoft\\PowerToys\\FancyZones\\zones-settings.json";
|
||||
private const string LayoutHotkeysFile = "\\Microsoft\\PowerToys\\FancyZones\\layout-hotkeys.json";
|
||||
private const string LayoutTemplatesFile = "\\Microsoft\\PowerToys\\FancyZones\\layout-templates.json";
|
||||
private const string ParamsFile = "\\Microsoft\\PowerToys\\FancyZones\\editor-parameters.json";
|
||||
|
||||
// Non-localizable string: Multi-monitor id
|
||||
@@ -52,6 +53,8 @@ namespace FancyZonesEditor.Utils
|
||||
|
||||
public string FancyZonesLayoutHotkeysFile { get; private set; }
|
||||
|
||||
public string FancyZonesLayoutTemplatesFile { get; private set; }
|
||||
|
||||
public string FancyZonesEditorParamsFile { get; private set; }
|
||||
|
||||
private enum CmdArgs
|
||||
@@ -182,7 +185,7 @@ namespace FancyZonesEditor.Utils
|
||||
public JsonElement Info { get; set; } // CanvasInfoWrapper or GridInfoWrapper
|
||||
}
|
||||
|
||||
// zones-settings: templates
|
||||
// layout-templates: templates
|
||||
private struct TemplateLayoutWrapper
|
||||
{
|
||||
public string Type { get; set; }
|
||||
@@ -196,6 +199,12 @@ namespace FancyZonesEditor.Utils
|
||||
public int SensitivityRadius { get; set; }
|
||||
}
|
||||
|
||||
// layout-templates: layout-templates-wrapper
|
||||
private struct TemplateLayoutsListWrapper
|
||||
{
|
||||
public List<TemplateLayoutWrapper> TemplatesList { get; set; }
|
||||
}
|
||||
|
||||
// layout-hotkeys: layout-hotkeys-wrapper
|
||||
private struct LayoutHotkeyWrapper
|
||||
{
|
||||
@@ -216,8 +225,6 @@ namespace FancyZonesEditor.Utils
|
||||
public List<DeviceWrapper> Devices { get; set; }
|
||||
|
||||
public List<CustomLayoutWrapper> CustomZoneSets { get; set; }
|
||||
|
||||
public List<TemplateLayoutWrapper> Templates { get; set; }
|
||||
}
|
||||
|
||||
private struct EditorParams
|
||||
@@ -250,6 +257,7 @@ namespace FancyZonesEditor.Utils
|
||||
var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
FancyZonesSettingsFile = localAppDataDir + ZonesSettingsFile;
|
||||
FancyZonesLayoutHotkeysFile = localAppDataDir + LayoutHotkeysFile;
|
||||
FancyZonesLayoutTemplatesFile = localAppDataDir + LayoutTemplatesFile;
|
||||
FancyZonesEditorParamsFile = localAppDataDir + ParamsFile;
|
||||
}
|
||||
|
||||
@@ -523,8 +531,6 @@ namespace FancyZonesEditor.Utils
|
||||
{
|
||||
bool devicesParsingResult = SetDevices(zoneSettings.Devices);
|
||||
bool customZonesParsingResult = SetCustomLayouts(zoneSettings.CustomZoneSets);
|
||||
bool templatesParsingResult = SetTemplateLayouts(zoneSettings.Templates);
|
||||
|
||||
if (!devicesParsingResult || !customZonesParsingResult)
|
||||
{
|
||||
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Message, settingsString);
|
||||
@@ -543,6 +549,12 @@ namespace FancyZonesEditor.Utils
|
||||
return parsingHotkeysResult;
|
||||
}
|
||||
|
||||
var parsingTemplatesResult = ParseLayoutTemplates();
|
||||
if (!parsingTemplatesResult.Result)
|
||||
{
|
||||
return parsingTemplatesResult;
|
||||
}
|
||||
|
||||
return new ParsingResult(true);
|
||||
}
|
||||
|
||||
@@ -585,6 +597,46 @@ namespace FancyZonesEditor.Utils
|
||||
return new ParsingResult(true);
|
||||
}
|
||||
|
||||
public ParsingResult ParseLayoutTemplates()
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
if (_fileSystem.File.Exists(FancyZonesLayoutTemplatesFile))
|
||||
{
|
||||
TemplateLayoutsListWrapper templates;
|
||||
string dataString = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
dataString = ReadFile(FancyZonesLayoutTemplatesFile);
|
||||
templates = JsonSerializer.Deserialize<TemplateLayoutsListWrapper>(dataString, _options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Layout templates parsing error", ex);
|
||||
return new ParsingResult(false, ex.Message, dataString);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool parsingResult = SetTemplateLayouts(templates.TemplatesList);
|
||||
if (parsingResult)
|
||||
{
|
||||
return new ParsingResult(true);
|
||||
}
|
||||
|
||||
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Layout_Templates_Message, dataString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Layout templates parsing error", ex);
|
||||
return new ParsingResult(false, ex.Message, dataString);
|
||||
}
|
||||
}
|
||||
|
||||
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Layout_Templates_Message);
|
||||
}
|
||||
|
||||
public void SerializeZoneSettings()
|
||||
{
|
||||
Logger.LogTrace();
|
||||
@@ -592,7 +644,6 @@ namespace FancyZonesEditor.Utils
|
||||
ZoneSettingsWrapper zoneSettings = new ZoneSettingsWrapper { };
|
||||
zoneSettings.Devices = new List<DeviceWrapper>();
|
||||
zoneSettings.CustomZoneSets = new List<CustomLayoutWrapper>();
|
||||
zoneSettings.Templates = new List<TemplateLayoutWrapper>();
|
||||
|
||||
// Serialize used devices
|
||||
foreach (var monitor in App.Overlay.Monitors)
|
||||
@@ -710,25 +761,6 @@ namespace FancyZonesEditor.Utils
|
||||
zoneSettings.CustomZoneSets.Add(customLayout);
|
||||
}
|
||||
|
||||
// Serialize template layouts
|
||||
foreach (LayoutModel layout in MainWindowSettingsModel.DefaultModels)
|
||||
{
|
||||
TemplateLayoutWrapper wrapper = new TemplateLayoutWrapper
|
||||
{
|
||||
Type = LayoutTypeToJsonTag(layout.Type),
|
||||
SensitivityRadius = layout.SensitivityRadius,
|
||||
ZoneCount = layout.TemplateZoneCount,
|
||||
};
|
||||
|
||||
if (layout is GridLayoutModel grid)
|
||||
{
|
||||
wrapper.ShowSpacing = grid.ShowSpacing;
|
||||
wrapper.Spacing = grid.Spacing;
|
||||
}
|
||||
|
||||
zoneSettings.Templates.Add(wrapper);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string jsonString = JsonSerializer.Serialize(zoneSettings, _options);
|
||||
@@ -739,11 +771,9 @@ namespace FancyZonesEditor.Utils
|
||||
Logger.LogError("Serialize zone settings error", ex);
|
||||
App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex);
|
||||
}
|
||||
|
||||
SerializeLayoutHotkeys();
|
||||
}
|
||||
|
||||
private void SerializeLayoutHotkeys()
|
||||
public void SerializeLayoutHotkeys()
|
||||
{
|
||||
LayoutHotkeysWrapper hotkeys = new LayoutHotkeysWrapper { };
|
||||
hotkeys.LayoutHotkeys = new List<LayoutHotkeyWrapper>();
|
||||
@@ -781,6 +811,41 @@ namespace FancyZonesEditor.Utils
|
||||
}
|
||||
}
|
||||
|
||||
public void SerializeLayoutTemplates()
|
||||
{
|
||||
TemplateLayoutsListWrapper templates = new TemplateLayoutsListWrapper { };
|
||||
templates.TemplatesList = new List<TemplateLayoutWrapper>();
|
||||
|
||||
foreach (LayoutModel layout in MainWindowSettingsModel.DefaultModels)
|
||||
{
|
||||
TemplateLayoutWrapper wrapper = new TemplateLayoutWrapper
|
||||
{
|
||||
Type = LayoutTypeToJsonTag(layout.Type),
|
||||
SensitivityRadius = layout.SensitivityRadius,
|
||||
ZoneCount = layout.TemplateZoneCount,
|
||||
};
|
||||
|
||||
if (layout is GridLayoutModel grid)
|
||||
{
|
||||
wrapper.ShowSpacing = grid.ShowSpacing;
|
||||
wrapper.Spacing = grid.Spacing;
|
||||
}
|
||||
|
||||
templates.TemplatesList.Add(wrapper);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string jsonString = JsonSerializer.Serialize(templates, _options);
|
||||
_fileSystem.File.WriteAllText(FancyZonesLayoutTemplatesFile, jsonString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Serialize layout templates error", ex);
|
||||
App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string ReadFile(string fileName)
|
||||
{
|
||||
Logger.LogTrace();
|
||||
|
||||
Reference in New Issue
Block a user