[FancyZones] Customize default layouts (#21156)

This commit is contained in:
Seraphima Zykova
2022-10-25 17:55:36 +02:00
committed by GitHub
parent 2add9db780
commit 10bfb014eb
25 changed files with 1133 additions and 110 deletions

View File

@@ -16,6 +16,7 @@
#include <FancyZonesLib/FancyZonesData/AppliedLayouts.h>
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include <FancyZonesLib/FancyZonesData/DefaultLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutHotkeys.h>
#include <FancyZonesLib/FancyZonesData/LayoutTemplates.h>
#include <FancyZonesLib/FancyZonesWindowProcessing.h>
@@ -73,6 +74,7 @@ public:
LayoutHotkeys::instance().LoadData();
AppliedLayouts::instance().LoadData();
AppZoneHistory::instance().LoadData();
DefaultLayouts::instance().LoadData();
}
// IFancyZones
@@ -662,6 +664,10 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
AppliedLayouts::instance().LoadData();
UpdateZoneSets();
}
else if (message == WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE)
{
DefaultLayouts::instance().LoadData();
}
else if (message == WM_PRIV_QUICK_LAYOUT_KEY)
{
ApplyQuickLayout(static_cast<int>(lparam));

View File

@@ -6,6 +6,7 @@
#include <FancyZonesLib/GuidUtils.h>
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include <FancyZonesLib/FancyZonesData/DefaultLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
#include <FancyZonesLib/JsonHelpers.h>
@@ -73,7 +74,7 @@ namespace JsonUtils
struct AppliedLayoutsJSON
{
private:
static std::optional<FancyZonesDataTypes::WorkAreaId> WorkAreaIdFromJson(const json::JsonObject& json)
static std::pair<std::optional<FancyZonesDataTypes::WorkAreaId>, bool> WorkAreaIdFromJson(const json::JsonObject& json)
{
try
{
@@ -89,7 +90,7 @@ namespace JsonUtils
auto virtualDesktopGuid = FancyZonesUtils::GuidFromString(virtualDesktop);
if (!virtualDesktopGuid)
{
return std::nullopt;
return { std::nullopt, false };
}
FancyZonesDataTypes::DeviceId deviceId{};
@@ -104,16 +105,17 @@ namespace JsonUtils
deviceId.instanceId = monitorInstance;
deviceId.number = monitorNumber;
}
FancyZonesDataTypes::MonitorId monitorId{
.deviceId = deviceId,
.serialNumber = monitorSerialNumber
};
return FancyZonesDataTypes::WorkAreaId{
.monitorId = monitorId,
.virtualDesktopId = virtualDesktopGuid.value(),
};
return { FancyZonesDataTypes::WorkAreaId{
.monitorId = monitorId,
.virtualDesktopId = virtualDesktopGuid.value(),
},
false };
}
else
{
@@ -121,24 +123,26 @@ namespace JsonUtils
auto bcDeviceId = BackwardsCompatibility::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!bcDeviceId)
{
return std::nullopt;
return { std::nullopt, false };
}
return FancyZonesDataTypes::WorkAreaId{
.monitorId = { .deviceId = MonitorUtils::Display::ConvertObsoleteDeviceId(bcDeviceId->deviceName) },
.virtualDesktopId = bcDeviceId->virtualDesktopId,
};
return { FancyZonesDataTypes::WorkAreaId{
.monitorId = { .deviceId = MonitorUtils::Display::ConvertObsoleteDeviceId(bcDeviceId->deviceName) },
.virtualDesktopId = bcDeviceId->virtualDesktopId,
},
true };
}
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
return { std::nullopt, false };
}
}
public:
FancyZonesDataTypes::WorkAreaId workAreaId;
Layout data{};
bool hasResolutionInId = false;
static std::optional<AppliedLayoutsJSON> FromJson(const json::JsonObject& json)
{
@@ -147,7 +151,7 @@ namespace JsonUtils
AppliedLayoutsJSON result;
auto deviceIdOpt = WorkAreaIdFromJson(json);
if (!deviceIdOpt.has_value())
if (!deviceIdOpt.first.has_value())
{
return std::nullopt;
}
@@ -158,8 +162,10 @@ namespace JsonUtils
return std::nullopt;
}
result.workAreaId = std::move(deviceIdOpt.value());
result.workAreaId = std::move(deviceIdOpt.first.value());
result.data = std::move(layout.value());
result.hasResolutionInId = deviceIdOpt.second;
return result;
}
catch (const winrt::hresult_error&)
@@ -200,8 +206,13 @@ namespace JsonUtils
if (auto obj = AppliedLayoutsJSON::FromJson(layouts.GetObjectAt(i)); obj.has_value())
{
// skip default layouts in case if they were applied to different resolutions on the same monitor.
// NOTE: keep the default layout check for users who update PT version from the v0.57
if (!map.contains(obj->workAreaId) && !isLayoutDefault(obj->data))
// NOTE: keep the default layout check for users who update PT version from the v0.57
if (obj->hasResolutionInId && isLayoutDefault(obj->data))
{
continue;
}
if (!map.contains(obj->workAreaId))
{
map[obj->workAreaId] = std::move(obj->data);
}
@@ -456,16 +467,8 @@ bool AppliedLayouts::ApplyDefaultLayout(const FancyZonesDataTypes::WorkAreaId& d
return false;
}
Layout layout{
.uuid = guid,
.type = FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid,
.showSpacing = DefaultValues::ShowSpacing,
.spacing = DefaultValues::Spacing,
.zoneCount = DefaultValues::ZoneCount,
.sensitivityRadius = DefaultValues::SensitivityRadius
};
m_layouts[deviceId] = std::move(layout);
// TODO: vertical or horizontal
m_layouts[deviceId] = DefaultLayouts::instance().GetDefaultLayout();
// Saving default layout data doesn't make sense, since it's ignored on parsing.
// Given that default layouts are ignored when parsing,

View File

@@ -227,11 +227,7 @@ std::optional<Layout> CustomLayouts::GetLayout(const GUID& id) const noexcept
FancyZonesDataTypes::CustomLayoutData customLayout = iter->second;
Layout layout{
.uuid = id,
.type = FancyZonesDataTypes::ZoneSetLayoutType::Custom,
.showSpacing = DefaultValues::ShowSpacing,
.spacing = DefaultValues::Spacing,
.zoneCount = DefaultValues::ZoneCount,
.sensitivityRadius = DefaultValues::SensitivityRadius
.type = FancyZonesDataTypes::ZoneSetLayoutType::Custom
};
if (customLayout.type == FancyZonesDataTypes::CustomLayoutType::Grid)

View File

@@ -0,0 +1,175 @@
#include "../pch.h"
#include "DefaultLayouts.h"
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
#include <FancyZonesLib/util.h>
#include <common/logger/logger.h>
namespace DefaultLayoutsJsonUtils
{
MonitorConfiguraionType TypeFromString(const std::wstring& data)
{
if (data == L"vertical")
{
return MonitorConfiguraionType::Vertical;
}
return MonitorConfiguraionType::Horizontal;
}
std::wstring TypeToString(MonitorConfiguraionType type)
{
switch (type)
{
case MonitorConfiguraionType::Horizontal:
return L"horizontal";
case MonitorConfiguraionType::Vertical:
return L"vertical";
default:
return L"horizontal";
}
}
struct LayoutJSON
{
static std::optional<Layout> FromJson(const json::JsonObject& json)
{
try
{
Layout data{};
auto idStr = json.GetNamedString(NonLocalizable::DefaultLayoutsIds::UuidID, L"");
if (!idStr.empty())
{
auto id = FancyZonesUtils::GuidFromString(idStr.c_str());
if (!id.has_value())
{
return std::nullopt;
}
data.uuid = id.value();
}
data.type = FancyZonesDataTypes::TypeFromString(std::wstring{ json.GetNamedString(NonLocalizable::DefaultLayoutsIds::TypeID) });
data.showSpacing = json.GetNamedBoolean(NonLocalizable::DefaultLayoutsIds::ShowSpacingID, DefaultValues::ShowSpacing);
data.spacing = static_cast<int>(json.GetNamedNumber(NonLocalizable::DefaultLayoutsIds::SpacingID, DefaultValues::Spacing));
data.zoneCount = static_cast<int>(json.GetNamedNumber(NonLocalizable::DefaultLayoutsIds::ZoneCountID, DefaultValues::ZoneCount));
data.sensitivityRadius = static_cast<int>(json.GetNamedNumber(NonLocalizable::DefaultLayoutsIds::SensitivityRadiusID, DefaultValues::SensitivityRadius));
return data;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
static json::JsonObject ToJson(const Layout& data)
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::UuidID, json::value(FancyZonesUtils::GuidToString(data.uuid).value()));
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(data.type)));
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::ShowSpacingID, json::value(data.showSpacing));
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::SpacingID, json::value(data.spacing));
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::ZoneCountID, json::value(data.zoneCount));
result.SetNamedValue(NonLocalizable::DefaultLayoutsIds::SensitivityRadiusID, json::value(data.sensitivityRadius));
return result;
}
};
struct DefaultLayoutJSON
{
MonitorConfiguraionType monitorConfigurationType{ MonitorConfiguraionType::Horizontal };
Layout layout{};
static std::optional<DefaultLayoutJSON> FromJson(const json::JsonObject& json)
{
try
{
DefaultLayoutJSON result;
auto type = TypeFromString(std::wstring{ json.GetNamedString(NonLocalizable::DefaultLayoutsIds::MonitorConfigurationTypeID) });
auto layout = DefaultLayoutsJsonUtils::LayoutJSON::FromJson(json.GetNamedObject(NonLocalizable::DefaultLayoutsIds::LayoutID));
if (!layout.has_value())
{
return std::nullopt;
}
result.monitorConfigurationType = std::move(type);
result.layout = std::move(layout.value());
return result;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
};
DefaultLayouts::TDefaultLayoutsContainer ParseJson(const json::JsonObject& json)
{
DefaultLayouts::TDefaultLayoutsContainer map{};
auto layouts = json.GetNamedArray(NonLocalizable::DefaultLayoutsIds::DefaultLayoutsArrayID);
for (uint32_t i = 0; i < layouts.Size(); ++i)
{
if (auto obj = DefaultLayoutJSON::FromJson(layouts.GetObjectAt(i)); obj.has_value())
{
map[static_cast<MonitorConfiguraionType>(obj->monitorConfigurationType)] = std::move(obj->layout);
}
}
return std::move(map);
}
}
DefaultLayouts::DefaultLayouts()
{
const std::wstring& dataFileName = DefaultLayoutsFileName();
m_fileWatcher = std::make_unique<FileWatcher>(dataFileName, [&]() {
PostMessageW(HWND_BROADCAST, WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE, NULL, NULL);
});
}
DefaultLayouts& DefaultLayouts::instance()
{
static DefaultLayouts self;
return self;
}
void DefaultLayouts::LoadData()
{
auto data = json::from_file(DefaultLayoutsFileName());
try
{
if (data)
{
m_layouts = DefaultLayoutsJsonUtils::ParseJson(data.value());
}
else
{
m_layouts.clear();
Logger::info(L"default-layouts.json file is missing or malformed");
}
}
catch (const winrt::hresult_error& e)
{
Logger::error(L"Parsing default-layouts error: {}", e.message());
}
}
Layout DefaultLayouts::GetDefaultLayout(MonitorConfiguraionType type) const noexcept
{
auto iter = m_layouts.find(type);
if (iter != m_layouts.end())
{
return iter->second;
}
return Layout{};
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include <FancyZonesLib/FancyZonesData/Layout.h>
#include <FancyZonesLib/ModuleConstants.h>
#include <common/SettingsAPI/FileWatcher.h>
#include <common/SettingsAPI/settings_helpers.h>
namespace NonLocalizable
{
namespace DefaultLayoutsIds
{
const static wchar_t* DefaultLayoutsArrayID = L"default-layouts";
const static wchar_t* MonitorConfigurationTypeID = L"monitor-configuration";
const static wchar_t* LayoutID = L"layout";
const static wchar_t* UuidID = L"uuid";
const static wchar_t* TypeID = L"type";
const static wchar_t* ShowSpacingID = L"show-spacing";
const static wchar_t* SpacingID = L"spacing";
const static wchar_t* ZoneCountID = L"zone-count";
const static wchar_t* SensitivityRadiusID = L"sensitivity-radius";
}
}
enum class MonitorConfiguraionType
{
Horizontal = 0,
Vertical
};
class DefaultLayouts
{
public:
using TDefaultLayoutsContainer = std::map<MonitorConfiguraionType, Layout>;
static DefaultLayouts& instance();
inline static std::wstring DefaultLayoutsFileName()
{
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
#if defined(UNIT_TESTS)
return saveFolderPath + L"\\test-default-layouts.json";
#endif
return saveFolderPath + L"\\default-layouts.json";
}
void LoadData();
Layout GetDefaultLayout(MonitorConfiguraionType type = MonitorConfiguraionType::Horizontal) const noexcept;
private:
DefaultLayouts();
~DefaultLayouts() = default;
TDefaultLayoutsContainer m_layouts;
std::unique_ptr<FileWatcher> m_fileWatcher;
};

View File

@@ -2,14 +2,25 @@
#include <guiddef.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesDataTypes.h>
struct Layout
{
GUID uuid;
FancyZonesDataTypes::ZoneSetLayoutType type;
bool showSpacing;
int spacing;
int zoneCount;
int sensitivityRadius;
};
GUID uuid = GUID_NULL;
FancyZonesDataTypes::ZoneSetLayoutType type = FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid;
bool showSpacing = DefaultValues::ShowSpacing;
int spacing = DefaultValues::Spacing;
int zoneCount = DefaultValues::ZoneCount;
int sensitivityRadius = DefaultValues::SensitivityRadius;
};
inline bool operator==(const Layout& lhs, const Layout& rhs)
{
return lhs.uuid == rhs.uuid &&
lhs.type == rhs.type &&
lhs.showSpacing == rhs.showSpacing &&
lhs.spacing == rhs.spacing &&
lhs.zoneCount == rhs.zoneCount &&
lhs.sensitivityRadius == rhs.sensitivityRadius;
}

View File

@@ -41,6 +41,7 @@
<ClInclude Include="FancyZonesData\AppZoneHistory.h" />
<ClInclude Include="FancyZones.h" />
<ClInclude Include="FancyZonesDataTypes.h" />
<ClInclude Include="FancyZonesData\DefaultLayouts.h" />
<ClInclude Include="FancyZonesData\Layout.h" />
<ClInclude Include="FancyZonesData\LayoutDefaults.h" />
<ClInclude Include="FancyZonesData\LayoutTemplates.h" />
@@ -90,6 +91,9 @@
<ClCompile Include="FancyZonesData\AppliedLayouts.cpp">
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="FancyZonesData\DefaultLayouts.cpp">
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
<PrecompiledHeaderFile>../pch.h</PrecompiledHeaderFile>
</ClCompile>

View File

@@ -16,6 +16,12 @@
<Filter Include="Generated Files">
<UniqueIdentifier>{093625ff-2415-4c2c-842c-0ee7fcb1d203}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\FancyZonesData">
<UniqueIdentifier>{dcbd2932-a3b2-400a-ad0d-6315f9795b13}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\FancyZonesData">
<UniqueIdentifier>{5b35a312-b878-49e9-92c0-e098c2b97deb}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
@@ -90,30 +96,9 @@
<ClInclude Include="GuidUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\AppZoneHistory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\AppliedLayouts.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\CustomLayouts.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutHotkeys.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ModuleConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutTemplates.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutDefaults.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\Layout.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ZoneIndexSetBitmask.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -132,6 +117,30 @@
<ClInclude Include="FancyZonesWindowProcessing.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\AppliedLayouts.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\AppZoneHistory.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\CustomLayouts.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\DefaultLayouts.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\Layout.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutDefaults.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutHotkeys.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutTemplates.h">
<Filter>Header Files\FancyZonesData</Filter>
</ClInclude>
<ClInclude Include="EditorParameters.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -194,21 +203,6 @@
<ClCompile Include="MonitorUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\CustomLayouts.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutHotkeys.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\AppZoneHistory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\AppliedLayouts.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesWindowProperties.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -224,6 +218,24 @@
<ClCompile Include="EditorParameters.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\AppliedLayouts.cpp">
<Filter>Source Files\FancyZonesData</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\AppZoneHistory.cpp">
<Filter>Source Files\FancyZonesData</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\CustomLayouts.cpp">
<Filter>Source Files\FancyZonesData</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\DefaultLayouts.cpp">
<Filter>Source Files\FancyZonesData</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutHotkeys.cpp">
<Filter>Source Files\FancyZonesData</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
<Filter>Source Files\FancyZonesData</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -15,6 +15,7 @@ UINT WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE;
UINT WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE;
UINT WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE;
UINT WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE;
UINT WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE;
UINT WM_PRIV_SNAP_HOTKEY;
UINT WM_PRIV_QUICK_LAYOUT_KEY;
UINT WM_PRIV_SETTINGS_CHANGED;
@@ -37,6 +38,7 @@ void InitializeWinhookEventIds()
WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE = RegisterWindowMessage(L"{4686f019-5d3d-4c5c-9051-b7cbbccca77d}");
WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE = RegisterWindowMessage(L"{0972787e-cdab-4e16-b228-91acdc38f40f}");
WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE = RegisterWindowMessage(L"{2ef2c8a7-e0d5-4f31-9ede-52aade2d284d}");
WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE = RegisterWindowMessage(L"{61fd2afb-e909-41b2-b6f3-b9f546f2ae3f}");
WM_PRIV_SNAP_HOTKEY = RegisterWindowMessage(L"{72f4fd8e-23f1-43ab-bbbc-029363df9a84}");
WM_PRIV_QUICK_LAYOUT_KEY = RegisterWindowMessage(L"{15baab3d-c67b-4a15-aFF0-13610e05e947}");
WM_PRIV_SETTINGS_CHANGED = RegisterWindowMessage(L"{89ca3Daa-bf2d-4e73-9f3f-c60716364e27}");

View File

@@ -13,6 +13,7 @@ extern UINT WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE; // Scheduled when the watched la
extern UINT WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE; // Scheduled when the watched layout-templates.json file is updated
extern UINT WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE; // Scheduled when the watched custom-layouts.json file is updated
extern UINT WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE; // Scheduled when the watched applied-layouts.json file is updated
extern UINT WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE; // Scheduled when the watched default-layouts.json file is updated
extern UINT WM_PRIV_SNAP_HOTKEY; // Scheduled when we receive a snap hotkey key down press
extern UINT WM_PRIV_QUICK_LAYOUT_KEY; // Scheduled when we receive a key down press to quickly apply a layout
extern UINT WM_PRIV_SETTINGS_CHANGED; // Scheduled when the a watched settings file is updated

View File

@@ -0,0 +1,121 @@
#include "pch.h"
#include <filesystem>
#include <FancyZonesLib/FancyZonesData/DefaultLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/util.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
{
TEST_CLASS (DefaultLayoutsUnitTests)
{
std::wstring m_testFolder = L"FancyZonesUnitTests";
std::wstring m_testFolderPath = PTSettingsHelper::get_module_save_folder_location(m_testFolder);
TEST_METHOD_CLEANUP(CleanUp)
{
std::filesystem::remove(DefaultLayouts::DefaultLayoutsFileName());
std::filesystem::remove_all(m_testFolderPath);
}
TEST_METHOD (DefaultLayoutsParse)
{
// prepare
json::JsonObject root{};
json::JsonArray layoutsArray{};
// custom, horizontal
{
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::TypeID, json::value(L"custom"));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::DefaultLayoutsIds::MonitorConfigurationTypeID, json::value(L"horizontal"));
obj.SetNamedValue(NonLocalizable::DefaultLayoutsIds::LayoutID, layout);
layoutsArray.Append(obj);
}
// template, vertical
{
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::TypeID, json::value(L"grid"));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::ShowSpacingID, json::value(true));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::SpacingID, json::value(1));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::ZoneCountID, json::value(4));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::SensitivityRadiusID, json::value(30));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::DefaultLayoutsIds::MonitorConfigurationTypeID, json::value(L"vertical"));
obj.SetNamedValue(NonLocalizable::DefaultLayoutsIds::LayoutID, layout);
layoutsArray.Append(obj);
}
root.SetNamedValue(NonLocalizable::DefaultLayoutsIds::DefaultLayoutsArrayID, layoutsArray);
json::to_file(DefaultLayouts::DefaultLayoutsFileName(), root);
// test
DefaultLayouts::instance().LoadData();
Layout horizontal{
.uuid = FancyZonesUtils::GuidFromString(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}").value(),
.type = FancyZonesDataTypes::ZoneSetLayoutType::Custom
};
Assert::IsTrue(horizontal == DefaultLayouts::instance().GetDefaultLayout(MonitorConfiguraionType::Horizontal));
Layout vertical{
.uuid = GUID_NULL,
.type = FancyZonesDataTypes::ZoneSetLayoutType::Grid,
.showSpacing = true,
.spacing = 1,
.zoneCount = 4,
.sensitivityRadius = 30
};
Assert::IsTrue(vertical == DefaultLayouts::instance().GetDefaultLayout(MonitorConfiguraionType::Vertical));
}
TEST_METHOD (DefaultLayoutsParseEmpty)
{
// prepare
json::JsonObject root{};
json::JsonArray layoutsArray{};
root.SetNamedValue(NonLocalizable::DefaultLayoutsIds::DefaultLayoutsArrayID, layoutsArray);
json::to_file(DefaultLayouts::DefaultLayoutsFileName(), root);
// test
DefaultLayouts::instance().LoadData();
Layout priorityGrid{
.uuid = GUID_NULL,
.type = FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid,
.showSpacing = DefaultValues::ShowSpacing,
.spacing = DefaultValues::Spacing,
.zoneCount = DefaultValues::ZoneCount,
.sensitivityRadius = DefaultValues::SensitivityRadius
};
Assert::IsTrue(priorityGrid == DefaultLayouts::instance().GetDefaultLayout(MonitorConfiguraionType::Horizontal));
Assert::IsTrue(priorityGrid == DefaultLayouts::instance().GetDefaultLayout(MonitorConfiguraionType::Vertical));
}
TEST_METHOD (DefaultLayoutsNoFile)
{
// test
DefaultLayouts::instance().LoadData();
Layout priorityGrid{
.uuid = GUID_NULL,
.type = FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid,
.showSpacing = DefaultValues::ShowSpacing,
.spacing = DefaultValues::Spacing,
.zoneCount = DefaultValues::ZoneCount,
.sensitivityRadius = DefaultValues::SensitivityRadius
};
Assert::IsTrue(priorityGrid == DefaultLayouts::instance().GetDefaultLayout(MonitorConfiguraionType::Horizontal));
Assert::IsTrue(priorityGrid == DefaultLayouts::instance().GetDefaultLayout(MonitorConfiguraionType::Vertical));
}
};
}

View File

@@ -44,6 +44,7 @@
<ClCompile Include="AppliedLayoutsTests.Spec.cpp" />
<ClCompile Include="AppZoneHistoryTests.Spec.cpp" />
<ClCompile Include="CustomLayoutsTests.Spec.cpp" />
<ClCompile Include="DefaultLayoutsTests.Spec.cpp" />
<ClCompile Include="FancyZonesSettings.Spec.cpp" />
<ClCompile Include="JsonHelpers.Tests.cpp" />
<ClCompile Include="LayoutHotkeysTests.Spec.cpp" />

View File

@@ -57,6 +57,9 @@
<ClCompile Include="WorkAreaIdTests.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DefaultLayoutsTests.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">

View File

@@ -2,20 +2,14 @@
#include <filesystem>
#include <FancyZonesLib/util.h>
#include <FancyZonesLib/ZoneSet.h>
#include <FancyZonesLib/WorkArea.h>
#include <FancyZonesLib/FancyZones.h>
#include <FancyZonesLib/FancyZonesData/AppliedLayouts.h>
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesDataTypes.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/FancyZonesData/DefaultLayouts.h>
#include "Util.h"
#include <common/utils/process_path.h>
#include <CppUnitTestLogger.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
@@ -40,36 +34,43 @@ namespace FancyZonesUnitTests
AppZoneHistory::instance().LoadData();
AppliedLayouts::instance().LoadData();
DefaultLayouts::instance().LoadData();
}
TEST_METHOD_CLEANUP(CleanUp)
{
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
std::filesystem::remove(DefaultLayouts::DefaultLayoutsFileName());
}
TEST_METHOD (CreateWorkArea)
{
const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout();
auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId);
Assert::IsFalse(workArea == nullptr);
Assert::IsTrue(m_uniqueId == workArea->UniqueId());
auto* zoneSet{ workArea->ZoneSet() };
Assert::IsNotNull(zoneSet);
Assert::AreEqual(static_cast<int>(zoneSet->LayoutType()), static_cast<int>(FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid));
Assert::AreEqual(zoneSet->GetZones().size(), static_cast<size_t>(3));
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(zoneSet->LayoutType()));
Assert::AreEqual(static_cast<size_t>(defaultLayout.zoneCount), zoneSet->GetZones().size());
}
TEST_METHOD (CreateCombinedWorkArea)
{
const auto defaultLayout = DefaultLayouts::instance().GetDefaultLayout();
auto workArea = MakeWorkArea({}, {}, m_uniqueId, m_emptyUniqueId);
Assert::IsFalse(workArea == nullptr);
Assert::IsTrue(m_uniqueId == workArea->UniqueId());
auto* zoneSet{ workArea->ZoneSet() };
Assert::IsNotNull(zoneSet);
Assert::AreEqual(static_cast<int>(zoneSet->LayoutType()), static_cast<int>(FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid));
Assert::AreEqual(zoneSet->GetZones().size(), static_cast<size_t>(3));
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(zoneSet->LayoutType()));
Assert::AreEqual(static_cast<size_t>(defaultLayout.zoneCount), zoneSet->GetZones().size());
}
TEST_METHOD (CreateWorkAreaClonedFromParent)
@@ -107,6 +108,67 @@ namespace FancyZonesUnitTests
Assert::AreEqual(layout.spacing, actualLayout.spacing);
Assert::AreEqual(layout.zoneCount, actualLayout.zoneCount);
}
TEST_METHOD (CreateWorkAreaWithCustomDefault)
{
// prepare
json::JsonObject root{};
json::JsonArray layoutsArray{};
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::TypeID, json::value(L"custom"));
json::JsonObject item{};
item.SetNamedValue(NonLocalizable::DefaultLayoutsIds::MonitorConfigurationTypeID, json::value(L"horizontal"));
item.SetNamedValue(NonLocalizable::DefaultLayoutsIds::LayoutID, layout);
layoutsArray.Append(item);
root.SetNamedValue(NonLocalizable::DefaultLayoutsIds::DefaultLayoutsArrayID, layoutsArray);
json::to_file(DefaultLayouts::DefaultLayoutsFileName(), root);
DefaultLayouts::instance().LoadData();
// test
auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId);
Assert::IsFalse(workArea == nullptr);
Assert::IsTrue(m_uniqueId == workArea->UniqueId());
auto* zoneSet{ workArea->ZoneSet() };
Assert::IsNotNull(zoneSet);
Assert::AreEqual(static_cast<int>(FancyZonesDataTypes::ZoneSetLayoutType::Custom), static_cast<int>(zoneSet->LayoutType()));
Assert::IsTrue(FancyZonesUtils::GuidFromString(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}").value() == zoneSet->Id());
}
TEST_METHOD (CreateWorkAreaWithTemplateDefault)
{
// prepare
json::JsonObject root{};
json::JsonArray layoutsArray{};
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::TypeID, json::value(L"grid"));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::ShowSpacingID, json::value(true));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::SpacingID, json::value(1));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::ZoneCountID, json::value(4));
layout.SetNamedValue(NonLocalizable::DefaultLayoutsIds::SensitivityRadiusID, json::value(30));
json::JsonObject item{};
item.SetNamedValue(NonLocalizable::DefaultLayoutsIds::MonitorConfigurationTypeID, json::value(L"horizontal"));
item.SetNamedValue(NonLocalizable::DefaultLayoutsIds::LayoutID, layout);
layoutsArray.Append(item);
root.SetNamedValue(NonLocalizable::DefaultLayoutsIds::DefaultLayoutsArrayID, layoutsArray);
json::to_file(DefaultLayouts::DefaultLayoutsFileName(), root);
DefaultLayouts::instance().LoadData();
// test
auto workArea = MakeWorkArea({}, Mocks::Monitor(), m_uniqueId, m_emptyUniqueId);
Assert::IsFalse(workArea == nullptr);
Assert::IsTrue(m_uniqueId == workArea->UniqueId());
auto* zoneSet{ workArea->ZoneSet() };
Assert::IsNotNull(zoneSet);
Assert::AreEqual(static_cast<int>(FancyZonesDataTypes::ZoneSetLayoutType::Grid), static_cast<int>(zoneSet->LayoutType()));
Assert::AreEqual(static_cast<size_t>(4), zoneSet->GetZones().size());
Assert::IsTrue(GUID_NULL == zoneSet->Id());
}
};
TEST_CLASS (WorkAreaUnitTests)

View File

@@ -113,6 +113,13 @@ namespace FancyZonesEditor
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Data_Title, MessageBoxButton.OK);
}
parseResult = FancyZonesEditorIO.ParseDefaultLayouts();
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Data_Title, MessageBoxButton.OK);
}
MainWindowSettingsModel settings = ((App)Current).MainWindowSettings;
settings.UpdateSelectedLayoutModel();

View File

@@ -202,7 +202,7 @@
FontWeight="SemiBold"
FontSize="24" />
<ui:GridView ItemsSource="{Binding DefaultModels}"
<ui:GridView ItemsSource="{Binding TemplateModels}"
Grid.Row="1"
ItemTemplate="{StaticResource LayoutItemTemplate}"
AutomationProperties.LabeledBy="{Binding ElementName=TemplatesHeaderBlock}"
@@ -589,9 +589,6 @@
</StackPanel>
</StackPanel>
<StackPanel Margin="0, 12, 0, 0">
<TextBlock Text="{x:Static props:Resources.Distance_adjacent_zones}"
x:Name="distanceTitle"
@@ -635,6 +632,97 @@
VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left"
Margin="0,16,0,0"
Visibility="{Binding Path=Type, Converter={StaticResource LayoutTypeTemplateToVisibilityConverter}}">
<TextBlock FontFamily="Segoe MDL2 Assets"
VerticalAlignment="Center"
FontSize="16"
Margin="0,16,0,0"
AutomationProperties.Name="{x:Static props:Resources.Number_of_zones}"
ToolTip="{x:Static props:Resources.Number_of_zones}"
Text="&#xECA5;" />
<ui:NumberBox Minimum="1"
Maximum="128"
Width="216"
KeyDown="EditDialogNumberBox_KeyDown"
Margin="12,0,0,0"
Header="{x:Static props:Resources.Number_of_zones}"
Loaded="NumberBox_Loaded"
AutomationProperties.Name="{x:Static props:Resources.Number_of_zones}"
SpinButtonPlacementMode="Compact"
Text="{Binding TemplateZoneCount}" />
</StackPanel>
<StackPanel Orientation="Vertical"
HorizontalAlignment="Left"
Margin="0,16,0,0">
<Button x:Name="SetLayoutAsHorizontalDefaultButton"
Click="SetLayoutAsHorizontalDefaultButton_Click"
AutomationProperties.Name="{x:Static props:Resources.Set_Layout_As_Horizontal_Default}"
ToolTip="{x:Static props:Resources.Set_Layout_As_Horizontal_Default}"
Style="{StaticResource IconOnlyButtonStyle}"
Visibility="{Binding CanBeSetAsHorizontalDefault, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Content>
<TextBlock AutomationProperties.Name="{x:Static props:Resources.Set_Layout_As_Horizontal_Default}"
FontSize="14">
<Run Text="&#xE734;" FontFamily="Segoe MDL2 Assets"/>
<Run Text="{x:Static props:Resources.Default_Layout_Horizontal}"/>
</TextBlock>
</Button.Content>
</Button>
<Button x:Name="HorizontalDefaultLayoutButton"
Click="HorizontalDefaultLayoutButton_Click"
ToolTip="{x:Static props:Resources.Default_Layout_Horizontal}"
AutomationProperties.Name="{x:Static props:Resources.Default_Layout_Horizontal}"
Style="{StaticResource IconOnlyButtonStyle}"
Visibility="{Binding IsHorizontalDefault, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Content>
<TextBlock AutomationProperties.Name="{x:Static props:Resources.Default_Layout_Horizontal}"
FontSize="14">
<Run Text="&#xE735;" FontFamily="Segoe MDL2 Assets"/>
<Run Text="{x:Static props:Resources.Default_Layout_Horizontal}"/>
</TextBlock>
</Button.Content>
</Button>
<Button x:Name="SetLayoutAsVerticalDefaultButton"
Click="SetLayoutAsVerticalDefaultButton_Click"
AutomationProperties.Name="{x:Static props:Resources.Set_Layout_As_Vertical_Default}"
ToolTip="{x:Static props:Resources.Set_Layout_As_Vertical_Default}"
Style="{StaticResource IconOnlyButtonStyle}"
Visibility="{Binding CanBeSetAsVerticalDefault, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Content>
<TextBlock AutomationProperties.Name="{x:Static props:Resources.Set_Layout_As_Vertical_Default}"
FontSize="14">
<Run Text="&#xE734;" FontFamily="Segoe MDL2 Assets"/>
<Run Text="{x:Static props:Resources.Default_Layout_Vertical}"/>
</TextBlock>
</Button.Content>
</Button>
<Button x:Name="VerticalDefaultLayoutButton"
Click="VerticalDefaultLayoutButton_Click"
ToolTip="{x:Static props:Resources.Default_Layout_Vertical}"
AutomationProperties.Name="{x:Static props:Resources.Default_Layout_Vertical}"
Style="{StaticResource IconOnlyButtonStyle}"
Visibility="{Binding IsVerticalDefault, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Content>
<TextBlock AutomationProperties.Name="{x:Static props:Resources.Default_Layout_Vertical}"
FontSize="14">
<Run Text="&#xE735;" FontFamily="Segoe MDL2 Assets"/>
<Run Text="{x:Static props:Resources.Default_Layout_Vertical}"/>
</TextBlock>
</Button.Content>
</Button>
</StackPanel>
</StackPanel>
</Grid>
</ui:ContentDialog>

View File

@@ -29,6 +29,7 @@ namespace FancyZonesEditor
private readonly MainWindowSettingsModel _settings = ((App)Application.Current).MainWindowSettings;
private LayoutModel _backup;
private List<LayoutModel> _defaultLayoutsBackup;
private ContentDialog _openedDialog;
private TextBlock _createLayoutAnnounce;
@@ -322,6 +323,8 @@ namespace FancyZonesEditor
_backup = new CanvasLayoutModel(canvas);
}
_defaultLayoutsBackup = new List<LayoutModel>(MainWindowSettingsModel.DefaultLayouts.DefaultLayouts);
Keyboard.ClearFocus();
EditLayoutDialogTitle.Text = string.Format(CultureInfo.CurrentCulture, Properties.Resources.Edit_Template, ((LayoutModel)dataContext).Name);
await EditLayoutDialog.ShowAsync();
@@ -424,6 +427,7 @@ namespace FancyZonesEditor
}
_backup = null;
_defaultLayoutsBackup = null;
// update current settings
if (model == _settings.AppliedModel)
@@ -464,6 +468,8 @@ namespace FancyZonesEditor
_backup = null;
}
MainWindowSettingsModel.DefaultLayouts.Reset(model.Uuid);
if (model == _settings.AppliedModel)
{
_settings.SetAppliedModel(_settings.BlankModel);
@@ -480,6 +486,7 @@ namespace FancyZonesEditor
App.FancyZonesEditorIO.SerializeAppliedLayouts();
App.FancyZonesEditorIO.SerializeCustomLayouts();
App.FancyZonesEditorIO.SerializeDefaultLayouts();
model.Delete();
}
}
@@ -543,6 +550,12 @@ namespace FancyZonesEditor
_settings.RestoreSelectedModel(_backup);
_backup = null;
}
if (_defaultLayoutsBackup != null)
{
MainWindowSettingsModel.DefaultLayouts.Restore(_defaultLayoutsBackup);
_defaultLayoutsBackup = null;
}
}
private void NumberBox_Loaded(object sender, RoutedEventArgs e)
@@ -586,5 +599,45 @@ namespace FancyZonesEditor
}
}
}
private void SetLayoutAsVerticalDefaultButton_Click(object sender, RoutedEventArgs e)
{
var dataContext = ((FrameworkElement)sender).DataContext;
if (dataContext is LayoutModel model)
{
MainWindowSettingsModel.DefaultLayouts.Set(model, MonitorConfigurationType.Vertical);
App.FancyZonesEditorIO.SerializeDefaultLayouts();
}
}
private void SetLayoutAsHorizontalDefaultButton_Click(object sender, RoutedEventArgs e)
{
var dataContext = ((FrameworkElement)sender).DataContext;
if (dataContext is LayoutModel model)
{
MainWindowSettingsModel.DefaultLayouts.Set(model, MonitorConfigurationType.Horizontal);
App.FancyZonesEditorIO.SerializeDefaultLayouts();
}
}
private void HorizontalDefaultLayoutButton_Click(object sender, RoutedEventArgs e)
{
var dataContext = ((FrameworkElement)sender).DataContext;
if (dataContext is LayoutModel model)
{
MainWindowSettingsModel.DefaultLayouts.Reset(MonitorConfigurationType.Horizontal);
App.FancyZonesEditorIO.SerializeDefaultLayouts();
}
}
private void VerticalDefaultLayoutButton_Click(object sender, RoutedEventArgs e)
{
var dataContext = ((FrameworkElement)sender).DataContext;
if (dataContext is LayoutModel model)
{
MainWindowSettingsModel.DefaultLayouts.Reset(MonitorConfigurationType.Vertical);
App.FancyZonesEditorIO.SerializeDefaultLayouts();
}
}
}
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace FancyZonesEditor.Models
{
public class DefaultLayoutsModel : INotifyPropertyChanged
{
private static int Count { get; } = Enum.GetValues(typeof(MonitorConfigurationType)).Length;
public List<LayoutModel> DefaultLayouts { get; } = new List<LayoutModel>(Count);
public DefaultLayoutsModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void Reset(MonitorConfigurationType type)
{
Set(MainWindowSettingsModel.TemplateModels[(int)LayoutType.PriorityGrid], type);
}
public void Reset(string uuid)
{
for (int i = 0; i < Count; i++)
{
if (DefaultLayouts[i].Uuid == uuid)
{
Set(MainWindowSettingsModel.TemplateModels[(int)LayoutType.PriorityGrid], (MonitorConfigurationType)i);
break;
}
}
}
public void Set(LayoutModel layout, MonitorConfigurationType type)
{
if (DefaultLayouts.Count <= (int)type)
{
DefaultLayouts.Insert((int)type, layout);
}
else
{
DefaultLayouts[(int)type] = layout;
}
FirePropertyChanged();
}
public void Restore(List<LayoutModel> layouts)
{
for (int i = 0; i < Count; i++)
{
Set(layouts[i], (MonitorConfigurationType)i);
}
}
protected virtual void FirePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -18,6 +18,8 @@ namespace FancyZonesEditor.Models
{
_guid = Guid.NewGuid();
Type = LayoutType.Custom;
MainWindowSettingsModel.DefaultLayouts.PropertyChanged += DefaultLayouts_PropertyChanged;
}
protected LayoutModel(string name)
@@ -144,6 +146,38 @@ namespace FancyZonesEditor.Models
private bool _isApplied;
public bool IsHorizontalDefault
{
get
{
return MainWindowSettingsModel.DefaultLayouts.DefaultLayouts[(int)MonitorConfigurationType.Horizontal].Uuid == this.Uuid;
}
}
public bool CanBeSetAsHorizontalDefault
{
get
{
return MainWindowSettingsModel.DefaultLayouts.DefaultLayouts[(int)MonitorConfigurationType.Horizontal].Uuid != this.Uuid;
}
}
public bool IsVerticalDefault
{
get
{
return MainWindowSettingsModel.DefaultLayouts.DefaultLayouts[(int)MonitorConfigurationType.Vertical].Uuid == this.Uuid;
}
}
public bool CanBeSetAsVerticalDefault
{
get
{
return MainWindowSettingsModel.DefaultLayouts.DefaultLayouts[(int)MonitorConfigurationType.Vertical].Uuid != this.Uuid;
}
}
public int SensitivityRadius
{
get
@@ -334,5 +368,13 @@ namespace FancyZonesEditor.Models
}
}
}
public void DefaultLayouts_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
FirePropertyChanged(nameof(IsHorizontalDefault));
FirePropertyChanged(nameof(IsVerticalDefault));
FirePropertyChanged(nameof(CanBeSetAsHorizontalDefault));
FirePropertyChanged(nameof(CanBeSetAsVerticalDefault));
}
}
}

View File

@@ -23,10 +23,6 @@ namespace FancyZonesEditor
VirtualDesktopId,
}
// Non-localizable strings
public static readonly string RegistryPath = "SOFTWARE\\SuperFancyZones";
public static readonly string FullRegistryPath = "HKEY_CURRENT_USER\\" + RegistryPath;
public bool IsCustomLayoutActive
{
get
@@ -43,6 +39,8 @@ namespace FancyZonesEditor
}
}
public static DefaultLayoutsModel DefaultLayouts { get; } = new DefaultLayoutsModel();
public MainWindowSettingsModel()
{
// Initialize default layout models: Blank, Focus, Columns, Rows, Grid, and PriorityGrid
@@ -51,11 +49,11 @@ namespace FancyZonesEditor
TemplateZoneCount = 0,
SensitivityRadius = 0,
};
DefaultModels.Insert((int)LayoutType.Blank, blankModel);
TemplateModels.Insert((int)LayoutType.Blank, blankModel);
var focusModel = new CanvasLayoutModel(Properties.Resources.Template_Layout_Focus, LayoutType.Focus);
focusModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Focus, focusModel);
TemplateModels.Insert((int)LayoutType.Focus, focusModel);
var columnsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Columns, LayoutType.Columns)
{
@@ -63,7 +61,7 @@ namespace FancyZonesEditor
RowPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
columnsModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Columns, columnsModel);
TemplateModels.Insert((int)LayoutType.Columns, columnsModel);
var rowsModel = new GridLayoutModel(Properties.Resources.Template_Layout_Rows, LayoutType.Rows)
{
@@ -71,15 +69,19 @@ namespace FancyZonesEditor
ColumnPercents = new List<int>(1) { GridLayoutModel.GridMultiplier },
};
rowsModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Rows, rowsModel);
TemplateModels.Insert((int)LayoutType.Rows, rowsModel);
var gridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Grid, LayoutType.Grid);
gridModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.Grid, gridModel);
TemplateModels.Insert((int)LayoutType.Grid, gridModel);
var priorityGridModel = new GridLayoutModel(Properties.Resources.Template_Layout_Priority_Grid, LayoutType.PriorityGrid);
priorityGridModel.InitTemplateZones();
DefaultModels.Insert((int)LayoutType.PriorityGrid, priorityGridModel);
TemplateModels.Insert((int)LayoutType.PriorityGrid, priorityGridModel);
// set default layouts
DefaultLayouts.Set(priorityGridModel, MonitorConfigurationType.Horizontal);
DefaultLayouts.Set(priorityGridModel, MonitorConfigurationType.Vertical);
}
// IsShiftKeyPressed - is the shift key currently being held down
@@ -126,11 +128,11 @@ namespace FancyZonesEditor
{
get
{
return DefaultModels[(int)LayoutType.Blank];
return TemplateModels[(int)LayoutType.Blank];
}
}
public static IList<LayoutModel> DefaultModels { get; } = new List<LayoutModel>(6);
public static IList<LayoutModel> TemplateModels { get; } = new List<LayoutModel>(6);
public static ObservableCollection<LayoutModel> CustomModels
{
@@ -213,7 +215,7 @@ namespace FancyZonesEditor
public void InitModels()
{
foreach (var model in DefaultModels)
foreach (var model in TemplateModels)
{
model.InitTemplateZones();
}
@@ -239,7 +241,7 @@ namespace FancyZonesEditor
}
else
{
foreach (LayoutModel model in DefaultModels)
foreach (LayoutModel model in TemplateModels)
{
if (model.Type == currentApplied.Type)
{
@@ -261,7 +263,7 @@ namespace FancyZonesEditor
if (foundModel == null)
{
foundModel = DefaultModels[(int)LayoutType.PriorityGrid];
foundModel = TemplateModels[(int)LayoutType.PriorityGrid];
}
SetSelectedModel(foundModel);
@@ -321,9 +323,9 @@ namespace FancyZonesEditor
AppliedModel = model;
}
public void UpdateDefaultModels()
public void UpdateTemplateModels()
{
foreach (LayoutModel model in DefaultModels)
foreach (LayoutModel model in TemplateModels)
{
if (App.Overlay.CurrentLayoutSettings.Type == model.Type && App.Overlay.CurrentLayoutSettings.ZoneCount != model.TemplateZoneCount)
{

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace FancyZonesEditor.Models
{
public enum MonitorConfigurationType
{
Horizontal = 0,
Vertical,
}
}

View File

@@ -107,7 +107,7 @@ namespace FancyZonesEditor
if (settings != null)
{
settings.SetAppliedModel(null);
settings.UpdateDefaultModels();
settings.UpdateTemplateModels();
}
Update();

View File

@@ -222,6 +222,24 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Default layout for horizontal monitor orientation.
/// </summary>
public static string Default_Layout_Horizontal {
get {
return ResourceManager.GetString("Default_Layout_Horizontal", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Default layout for vertical monitor orientation.
/// </summary>
public static string Default_Layout_Vertical {
get {
return ResourceManager.GetString("Default_Layout_Vertical", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
@@ -451,6 +469,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to An error occurred while parsing default layouts..
/// </summary>
public static string Error_Parsing_Default_Layouts_Message {
get {
return ResourceManager.GetString("Error_Parsing_Default_Layouts_Message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error parsing device info data..
/// </summary>
@@ -762,6 +789,24 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Set layout as a default for horizontal monitor orientation.
/// </summary>
public static string Set_Layout_As_Horizontal_Default {
get {
return ResourceManager.GetString("Set_Layout_As_Horizontal_Default", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Set layout as a default for vertical monitor orientation.
/// </summary>
public static string Set_Layout_As_Vertical_Default {
get {
return ResourceManager.GetString("Set_Layout_As_Vertical_Default", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Template settings.
/// </summary>

View File

@@ -414,4 +414,19 @@
<data name="Slider_Value" xml:space="preserve">
<value>{0} pixels</value>
</data>
<data name="Default_Layout_Horizontal" xml:space="preserve">
<value>Default layout for horizontal monitor orientation</value>
</data>
<data name="Set_Layout_As_Horizontal_Default" xml:space="preserve">
<value>Set layout as a default for horizontal monitor orientation</value>
</data>
<data name="Error_Parsing_Default_Layouts_Message" xml:space="preserve">
<value>An error occurred while parsing default layouts.</value>
</data>
<data name="Default_Layout_Vertical" xml:space="preserve">
<value>Default layout for vertical monitor orientation</value>
</data>
<data name="Set_Layout_As_Vertical_Default" xml:space="preserve">
<value>Set layout as a default for vertical monitor orientation</value>
</data>
</root>

View File

@@ -27,12 +27,15 @@ namespace FancyZonesEditor.Utils
private const string GridJsonTag = "grid";
private const string PriorityGridJsonTag = "priority-grid";
private const string CustomJsonTag = "custom";
private const string HorizontalJsonTag = "horizontal";
private const string VerticalJsonTag = "vertical";
// Non-localizable strings: Files
private const string AppliedLayoutsFile = "\\Microsoft\\PowerToys\\FancyZones\\applied-layouts.json";
private const string LayoutHotkeysFile = "\\Microsoft\\PowerToys\\FancyZones\\layout-hotkeys.json";
private const string LayoutTemplatesFile = "\\Microsoft\\PowerToys\\FancyZones\\layout-templates.json";
private const string CustomLayoutsFile = "\\Microsoft\\PowerToys\\FancyZones\\custom-layouts.json";
private const string DefaultLayoutsFile = "\\Microsoft\\PowerToys\\FancyZones\\default-layouts.json";
private const string ParamsFile = "\\Microsoft\\PowerToys\\FancyZones\\editor-parameters.json";
// Non-localizable string: default virtual desktop id
@@ -56,6 +59,8 @@ namespace FancyZonesEditor.Utils
public string FancyZonesCustomLayoutsFile { get; private set; }
public string FancyZonesDefaultLayoutsFile { get; private set; }
public string FancyZonesEditorParamsFile { get; private set; }
private enum CmdArgs
@@ -264,6 +269,35 @@ namespace FancyZonesEditor.Utils
public List<LayoutHotkeyWrapper> LayoutHotkeys { get; set; }
}
// default-layouts.json
private struct DefaultLayoutWrapper
{
public struct LayoutWrapper
{
public string Uuid { get; set; }
public string Type { get; set; }
public bool ShowSpacing { get; set; }
public int Spacing { get; set; }
public int ZoneCount { get; set; }
public int SensitivityRadius { get; set; }
}
public string MonitorConfiguration { get; set; }
public LayoutWrapper Layout { get; set; }
}
// default-layouts.json
private struct DefaultLayoutsListWrapper
{
public List<DefaultLayoutWrapper> DefaultLayouts { get; set; }
}
private struct EditorParams
{
public int ProcessId { get; set; }
@@ -296,6 +330,7 @@ namespace FancyZonesEditor.Utils
FancyZonesLayoutHotkeysFile = localAppDataDir + LayoutHotkeysFile;
FancyZonesLayoutTemplatesFile = localAppDataDir + LayoutTemplatesFile;
FancyZonesCustomLayoutsFile = localAppDataDir + CustomLayoutsFile;
FancyZonesDefaultLayoutsFile = localAppDataDir + DefaultLayoutsFile;
FancyZonesEditorParamsFile = localAppDataDir + ParamsFile;
}
@@ -558,6 +593,46 @@ namespace FancyZonesEditor.Utils
return new ParsingResult(true);
}
public ParsingResult ParseDefaultLayouts()
{
Logger.LogTrace();
if (_fileSystem.File.Exists(FancyZonesDefaultLayoutsFile))
{
DefaultLayoutsListWrapper wrapper;
string dataString = string.Empty;
try
{
dataString = ReadFile(FancyZonesDefaultLayoutsFile);
wrapper = JsonSerializer.Deserialize<DefaultLayoutsListWrapper>(dataString, _options);
}
catch (Exception ex)
{
Logger.LogError("Default layouts parsing error", ex);
return new ParsingResult(false, ex.Message, dataString);
}
try
{
bool parsingResult = SetDefaultLayouts(wrapper.DefaultLayouts);
if (parsingResult)
{
return new ParsingResult(true);
}
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Default_Layouts_Message, dataString);
}
catch (Exception ex)
{
Logger.LogError("Default layouts parsing error", ex);
return new ParsingResult(false, ex.Message, dataString);
}
}
return new ParsingResult(true);
}
public void SerializeAppliedLayouts()
{
Logger.LogTrace();
@@ -658,7 +733,7 @@ namespace FancyZonesEditor.Utils
TemplateLayoutsListWrapper templates = new TemplateLayoutsListWrapper { };
templates.LayoutTemplates = new List<TemplateLayoutWrapper>();
foreach (LayoutModel layout in MainWindowSettingsModel.DefaultModels)
foreach (LayoutModel layout in MainWindowSettingsModel.TemplateModels)
{
TemplateLayoutWrapper wrapper = new TemplateLayoutWrapper
{
@@ -790,6 +865,107 @@ namespace FancyZonesEditor.Utils
}
}
public void SerializeDefaultLayouts()
{
DefaultLayoutsListWrapper layouts = new DefaultLayoutsListWrapper { };
layouts.DefaultLayouts = new List<DefaultLayoutWrapper>();
foreach (LayoutModel layout in MainWindowSettingsModel.TemplateModels)
{
if (layout.IsHorizontalDefault || layout.IsVerticalDefault)
{
DefaultLayoutWrapper.LayoutWrapper layoutWrapper = new DefaultLayoutWrapper.LayoutWrapper
{
Uuid = string.Empty,
Type = LayoutTypeToJsonTag(layout.Type),
SensitivityRadius = layout.SensitivityRadius,
ZoneCount = layout.TemplateZoneCount,
};
if (layout is GridLayoutModel grid)
{
layoutWrapper.ShowSpacing = grid.ShowSpacing;
layoutWrapper.Spacing = grid.Spacing;
}
// can be both horizontal and vertical, so check separately
if (layout.IsHorizontalDefault)
{
DefaultLayoutWrapper wrapper = new DefaultLayoutWrapper
{
MonitorConfiguration = MonitorConfigurationTypeToJsonTag(MonitorConfigurationType.Horizontal),
Layout = layoutWrapper,
};
layouts.DefaultLayouts.Add(wrapper);
}
if (layout.IsVerticalDefault)
{
DefaultLayoutWrapper wrapper = new DefaultLayoutWrapper
{
MonitorConfiguration = MonitorConfigurationTypeToJsonTag(MonitorConfigurationType.Vertical),
Layout = layoutWrapper,
};
layouts.DefaultLayouts.Add(wrapper);
}
}
}
foreach (LayoutModel layout in MainWindowSettingsModel.CustomModels)
{
if (layout.IsHorizontalDefault || layout.IsVerticalDefault)
{
DefaultLayoutWrapper.LayoutWrapper layoutWrapper = new DefaultLayoutWrapper.LayoutWrapper
{
Uuid = layout.Uuid,
Type = LayoutTypeToJsonTag(LayoutType.Custom),
};
if (layout is GridLayoutModel grid)
{
layoutWrapper.ShowSpacing = grid.ShowSpacing;
layoutWrapper.Spacing = grid.Spacing;
}
// can be both horizontal and vertical, so check separately
if (layout.IsHorizontalDefault)
{
DefaultLayoutWrapper wrapper = new DefaultLayoutWrapper
{
MonitorConfiguration = MonitorConfigurationTypeToJsonTag(MonitorConfigurationType.Horizontal),
Layout = layoutWrapper,
};
layouts.DefaultLayouts.Add(wrapper);
}
if (layout.IsVerticalDefault)
{
DefaultLayoutWrapper wrapper = new DefaultLayoutWrapper
{
MonitorConfiguration = MonitorConfigurationTypeToJsonTag(MonitorConfigurationType.Vertical),
Layout = layoutWrapper,
};
layouts.DefaultLayouts.Add(wrapper);
}
}
}
try
{
string jsonString = JsonSerializer.Serialize(layouts, _options);
_fileSystem.File.WriteAllText(FancyZonesDefaultLayoutsFile, jsonString);
}
catch (Exception ex)
{
Logger.LogError("Serialize default layout error", ex);
App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex);
}
}
private string ReadFile(string fileName)
{
Logger.LogTrace();
@@ -942,7 +1118,7 @@ namespace FancyZonesEditor.Utils
foreach (var wrapper in templateLayouts)
{
LayoutType type = JsonTagToLayoutType(wrapper.Type);
LayoutModel layout = MainWindowSettingsModel.DefaultModels[(int)type];
LayoutModel layout = MainWindowSettingsModel.TemplateModels[(int)type];
layout.SensitivityRadius = wrapper.SensitivityRadius;
layout.TemplateZoneCount = wrapper.ZoneCount;
@@ -972,6 +1148,41 @@ namespace FancyZonesEditor.Utils
return true;
}
private bool SetDefaultLayouts(List<DefaultLayoutWrapper> layouts)
{
Logger.LogTrace();
if (layouts == null)
{
return false;
}
foreach (var layout in layouts)
{
if (layout.Layout.Uuid != null && layout.Layout.Uuid != string.Empty)
{
MonitorConfigurationType type = JsonTagToMonitorConfigurationType(layout.MonitorConfiguration);
foreach (var customLayout in MainWindowSettingsModel.CustomModels)
{
if (customLayout.Uuid == layout.Layout.Uuid)
{
MainWindowSettingsModel.DefaultLayouts.Set(customLayout, type);
break;
}
}
}
else
{
LayoutType layoutType = JsonTagToLayoutType(layout.Layout.Type);
MonitorConfigurationType type = JsonTagToMonitorConfigurationType(layout.MonitorConfiguration);
MainWindowSettingsModel.DefaultLayouts.Set(MainWindowSettingsModel.TemplateModels[(int)layoutType], type);
}
}
return true;
}
private CanvasLayoutModel ParseCanvasInfo(CustomLayoutWrapper wrapper)
{
var info = JsonSerializer.Deserialize<CanvasInfoWrapper>(wrapper.Info.GetRawText(), _options);
@@ -1089,5 +1300,31 @@ namespace FancyZonesEditor.Utils
return string.Empty;
}
}
private MonitorConfigurationType JsonTagToMonitorConfigurationType(string tag)
{
switch (tag)
{
case HorizontalJsonTag:
return MonitorConfigurationType.Horizontal;
case VerticalJsonTag:
return MonitorConfigurationType.Vertical;
}
return MonitorConfigurationType.Horizontal;
}
private string MonitorConfigurationTypeToJsonTag(MonitorConfigurationType type)
{
switch (type)
{
case MonitorConfigurationType.Horizontal:
return HorizontalJsonTag;
case MonitorConfigurationType.Vertical:
return VerticalJsonTag;
}
return HorizontalJsonTag;
}
}
}