[FancyZones] Split zones-settings: layout templates (#15588)

This commit is contained in:
Seraphima Zykova
2022-01-18 16:13:32 +03:00
committed by GitHub
parent 758a21a22f
commit f6576e01f3
17 changed files with 380 additions and 141 deletions

View File

@@ -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)
{

View File

@@ -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)
{

View File

@@ -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";
}
};

View File

@@ -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" />

View File

@@ -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">

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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());
}
};
}

View File

@@ -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());
}
};
}

View File

@@ -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>

View File

@@ -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">

View File

@@ -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

View File

@@ -31,7 +31,7 @@ namespace FancyZonesEditor
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
}
App.FancyZonesEditorIO.SerializeZoneSettings();
App.FancyZonesEditorIO.SerializeLayoutTemplates();
Close();
}

View File

@@ -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);

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();