[FancyZones] Split zones-settings: devices -> applied layouts (#15779)

This commit is contained in:
Seraphima Zykova
2022-01-27 15:21:15 +03:00
committed by GitHub
parent 792a04cf48
commit 03c36b4f65
41 changed files with 1461 additions and 1362 deletions

View File

@@ -5,8 +5,10 @@
#include <common/logger/logger.h>
#include <common/utils/process_path.h>
#include <FancyZonesLib/GuidUtils.h>
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>
AppZoneHistory::AppZoneHistory()
{
@@ -104,7 +106,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Dev
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
if (data.deviceId == deviceId)
{
// application already has history on this work area, update it with new window position
data.processIdToHandleMap[processId] = window;
@@ -151,7 +153,7 @@ bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::D
auto& perDesktopData = history->second;
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
{
if (data->deviceId.isEqualWithNullVirtualDesktopId(deviceId) && data->zoneSetUuid == zoneSetId)
if (data->deviceId == deviceId && data->zoneSetUuid == zoneSetId)
{
if (!IsAnotherWindowOfApplicationInstanceZoned(window, deviceId))
{
@@ -209,7 +211,7 @@ std::optional<FancyZonesDataTypes::AppZoneHistoryData> AppZoneHistory::GetZoneHi
auto historyVector = iter->second;
for (const auto& history : historyVector)
{
if (history.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
if (history.deviceId == deviceId)
{
return history;
}
@@ -230,7 +232,7 @@ bool AppZoneHistory::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
if (data.deviceId == deviceId)
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
@@ -264,7 +266,7 @@ void AppZoneHistory::UpdateProcessIdToHandleMap(HWND window, const FancyZonesDat
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
if (data.deviceId == deviceId)
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
@@ -287,7 +289,7 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
const auto& perDesktopData = history->second;
for (const auto& data : perDesktopData)
{
if (data.zoneSetUuid == zoneSetId && data.deviceId.isEqualWithNullVirtualDesktopId(deviceId))
if (data.zoneSetUuid == zoneSetId && data.deviceId == deviceId)
{
return data.zoneIndexSet;
}
@@ -298,16 +300,68 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
return {};
}
void AppZoneHistory::RemoveDesktopAppZoneHistory(GUID desktopId)
void AppZoneHistory::SyncVirtualDesktops(GUID currentVirtualDesktopId)
{
_TRACER_;
// Explorer persists current virtual desktop identifier to registry on a per session basis,
// but only after first virtual desktop switch happens. If the user hasn't switched virtual
// desktops in this session value in registry will be empty and we will use default GUID in
// that case (00000000-0000-0000-0000-000000000000).
bool dirtyFlag = false;
for (auto& [path, perDesktopData] : m_history)
{
for (auto& data : perDesktopData)
{
if (data.deviceId.virtualDesktopId == GUID_NULL)
{
data.deviceId.virtualDesktopId = currentVirtualDesktopId;
dirtyFlag = true;
}
else
{
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(data.deviceId.virtualDesktopId))
{
data.deviceId.virtualDesktopId = GUID_NULL;
dirtyFlag = true;
}
}
}
}
if (dirtyFlag)
{
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (SUCCEEDED(StringFromCLSID(currentVirtualDesktopId, &virtualDesktopIdStr)))
{
Logger::info(L"Update Virtual Desktop id to {}", virtualDesktopIdStr.get());
}
SaveData();
}
}
void AppZoneHistory::RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops)
{
std::unordered_set<GUID> active(std::begin(activeDesktops), std::end(activeDesktops));
bool dirtyFlag = false;
for (auto it = std::begin(m_history); it != std::end(m_history);)
{
auto& perDesktopData = it->second;
for (auto desktopIt = std::begin(perDesktopData); desktopIt != std::end(perDesktopData);)
{
if (desktopIt->deviceId.virtualDesktopId == desktopId)
if (desktopIt->deviceId.virtualDesktopId != GUID_NULL && !active.contains(desktopIt->deviceId.virtualDesktopId))
{
auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(desktopIt->deviceId.virtualDesktopId);
if (virtualDesktopIdStr)
{
Logger::info(L"Remove Virtual Desktop id {} from app-zone-history", virtualDesktopIdStr.value());
}
desktopIt = perDesktopData.erase(desktopIt);
dirtyFlag = true;
}
else
{
@@ -324,4 +378,9 @@ void AppZoneHistory::RemoveDesktopAppZoneHistory(GUID desktopId)
++it;
}
}
if (dirtyFlag)
{
SaveData();
}
}

View File

@@ -21,8 +21,6 @@ public:
return saveFolderPath + L"\\app-zone-history.json";
}
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
void LoadData();
void SaveData();
@@ -38,7 +36,9 @@ public:
void UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId);
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::DeviceIdData& deviceId, const std::wstring_view& zoneSetId) const;
void RemoveDesktopAppZoneHistory(GUID desktopId);
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
void SyncVirtualDesktops(GUID currentVirtualDesktopId);
void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops);
private:
AppZoneHistory();

View File

@@ -0,0 +1,420 @@
#include "../pch.h"
#include "AppliedLayouts.h"
#include <common/logger/call_tracer.h>
#include <common/logger/logger.h>
#include <FancyZonesLib/GuidUtils.h>
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesData/LayoutTemplates.h>
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>
namespace JsonUtils
{
struct LayoutJSON
{
static std::optional<Layout> FromJson(const json::JsonObject& json)
{
try
{
Layout data;
auto idStr = json.GetNamedString(NonLocalizable::AppliedLayoutsIds::UuidID);
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::AppliedLayoutsIds::TypeID) });
data.showSpacing = json.GetNamedBoolean(NonLocalizable::AppliedLayoutsIds::ShowSpacingID);
data.spacing = static_cast<int>(json.GetNamedNumber(NonLocalizable::AppliedLayoutsIds::SpacingID));
data.zoneCount = static_cast<int>(json.GetNamedNumber(NonLocalizable::AppliedLayoutsIds::ZoneCountID));
data.sensitivityRadius = static_cast<int>(json.GetNamedNumber(NonLocalizable::AppliedLayoutsIds::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::AppliedLayoutsIds::UuidID, json::value(FancyZonesUtils::GuidToString(data.uuid).value()));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(data.type)));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(data.showSpacing));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(data.spacing));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(data.zoneCount));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(data.sensitivityRadius));
return result;
}
};
struct AppliedLayoutsJSON
{
FancyZonesDataTypes::DeviceIdData deviceId;
Layout data;
static std::optional<AppliedLayoutsJSON> FromJson(const json::JsonObject& json)
{
try
{
AppliedLayoutsJSON result;
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::AppliedLayoutsIds::DeviceIdID).c_str();
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!deviceId.has_value())
{
return std::nullopt;
}
auto layout = JsonUtils::LayoutJSON::FromJson(json.GetNamedObject(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID));
if (!layout.has_value())
{
return std::nullopt;
}
result.deviceId = std::move(deviceId.value());
result.data = std::move(layout.value());
return result;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
static json::JsonObject ToJson(const AppliedLayoutsJSON& value)
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(value.deviceId.toString()));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, JsonUtils::LayoutJSON::ToJson(value.data));
return result;
}
};
AppliedLayouts::TAppliedLayoutsMap ParseJson(const json::JsonObject& json)
{
AppliedLayouts::TAppliedLayoutsMap map{};
auto layouts = json.GetNamedArray(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID);
for (uint32_t i = 0; i < layouts.Size(); ++i)
{
if (auto obj = AppliedLayoutsJSON::FromJson(layouts.GetObjectAt(i)); obj.has_value())
{
map[obj->deviceId] = std::move(obj->data);
}
}
return map;
}
json::JsonObject SerializeJson(const AppliedLayouts::TAppliedLayoutsMap& map)
{
json::JsonObject json{};
json::JsonArray layoutArray{};
for (const auto& [id, data] : map)
{
AppliedLayoutsJSON obj{};
obj.deviceId = id;
obj.data = data;
layoutArray.Append(AppliedLayoutsJSON::ToJson(obj));
}
json.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutArray);
return json;
}
}
AppliedLayouts::AppliedLayouts()
{
const std::wstring& fileName = AppliedLayoutsFileName();
m_fileWatcher = std::make_unique<FileWatcher>(fileName, [&]() {
PostMessageW(HWND_BROADCAST, WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE, NULL, NULL);
});
}
AppliedLayouts& AppliedLayouts::instance()
{
static AppliedLayouts self;
return self;
}
void AppliedLayouts::LoadData()
{
auto data = json::from_file(AppliedLayoutsFileName());
try
{
if (data)
{
m_layouts = JsonUtils::ParseJson(data.value());
}
else
{
m_layouts.clear();
Logger::info(L"applied-layouts.json file is missing or malformed");
}
}
catch (const winrt::hresult_error& e)
{
Logger::error(L"Parsing applied-layouts error: {}", e.message());
}
}
void AppliedLayouts::SaveData()
{
_TRACER_;
bool dirtyFlag = false;
TAppliedLayoutsMap updatedMap;
if (m_virtualDesktopCheckCallback)
{
for (const auto& [id, data] : m_layouts)
{
auto updatedId = id;
if (!m_virtualDesktopCheckCallback(id.virtualDesktopId))
{
updatedId.virtualDesktopId = GUID_NULL;
dirtyFlag = true;
}
updatedMap.insert({ updatedId, data });
}
}
if (dirtyFlag)
{
json::to_file(AppliedLayoutsFileName(), JsonUtils::SerializeJson(updatedMap));
}
else
{
json::to_file(AppliedLayoutsFileName(), JsonUtils::SerializeJson(m_layouts));
}
}
void AppliedLayouts::SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback)
{
m_virtualDesktopCheckCallback = callback;
}
void AppliedLayouts::SyncVirtualDesktops(GUID currentVirtualDesktopId)
{
_TRACER_;
// Explorer persists current virtual desktop identifier to registry on a per session basis,
// but only after first virtual desktop switch happens. If the user hasn't switched virtual
// desktops in this session value in registry will be empty and we will use default GUID in
// that case (00000000-0000-0000-0000-000000000000).
bool dirtyFlag = false;
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithCurrentId{};
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithNullId{};
for (const auto& [id, data] : m_layouts)
{
if (id.virtualDesktopId == GUID_NULL)
{
replaceWithCurrentId.push_back(id);
dirtyFlag = true;
}
else
{
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(id.virtualDesktopId))
{
replaceWithNullId.push_back(id);
dirtyFlag = true;
}
}
}
for (const auto& id : replaceWithCurrentId)
{
auto mapEntry = m_layouts.extract(id);
mapEntry.key().virtualDesktopId = currentVirtualDesktopId;
m_layouts.insert(std::move(mapEntry));
}
for (const auto& id : replaceWithNullId)
{
auto mapEntry = m_layouts.extract(id);
mapEntry.key().virtualDesktopId = GUID_NULL;
m_layouts.insert(std::move(mapEntry));
}
if (dirtyFlag)
{
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (SUCCEEDED(StringFromCLSID(currentVirtualDesktopId, &virtualDesktopIdStr)))
{
Logger::info(L"Update Virtual Desktop id to {}", virtualDesktopIdStr.get());
}
SaveData();
}
}
void AppliedLayouts::RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops)
{
std::unordered_set<GUID> active(std::begin(activeDesktops), std::end(activeDesktops));
bool dirtyFlag = false;
for (auto it = std::begin(m_layouts); it != std::end(m_layouts);)
{
GUID desktopId = it->first.virtualDesktopId;
if (desktopId != GUID_NULL)
{
auto foundId = active.find(desktopId);
if (foundId == std::end(active))
{
wil::unique_cotaskmem_string virtualDesktopIdStr;
if (SUCCEEDED(StringFromCLSID(desktopId, &virtualDesktopIdStr)))
{
Logger::info(L"Remove Virtual Desktop id {}", virtualDesktopIdStr.get());
}
it = m_layouts.erase(it);
dirtyFlag = true;
continue;
}
}
++it;
}
if (dirtyFlag)
{
SaveData();
}
}
std::optional<Layout> AppliedLayouts::GetDeviceLayout(const FancyZonesDataTypes::DeviceIdData& id) const noexcept
{
auto iter = m_layouts.find(id);
if (iter != m_layouts.end())
{
return iter->second;
}
return std::nullopt;
}
const AppliedLayouts::TAppliedLayoutsMap& AppliedLayouts::GetAppliedLayoutMap() const noexcept
{
return m_layouts;
}
bool AppliedLayouts::IsLayoutApplied(const FancyZonesDataTypes::DeviceIdData& id) const noexcept
{
auto iter = m_layouts.find(id);
return iter != m_layouts.end();
}
bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& layout)
{
auto uuid = FancyZonesUtils::GuidFromString(layout.uuid);
if (!uuid)
{
return false;
}
Layout layoutToApply {
.uuid = uuid.value(),
.type = layout.type,
.showSpacing = DefaultValues::ShowSpacing,
.spacing = DefaultValues::Spacing,
.zoneCount = DefaultValues::ZoneCount,
.sensitivityRadius = DefaultValues::SensitivityRadius,
};
// copy layouts properties to the applied-layout
auto customLayout = CustomLayouts::instance().GetLayout(layoutToApply.uuid);
if (customLayout)
{
if (customLayout.value().type == FancyZonesDataTypes::CustomLayoutType::Grid)
{
auto layoutInfo = std::get<FancyZonesDataTypes::GridLayoutInfo>(customLayout.value().info);
layoutToApply.sensitivityRadius = layoutInfo.sensitivityRadius();
layoutToApply.showSpacing = layoutInfo.showSpacing();
layoutToApply.spacing = layoutInfo.spacing();
layoutToApply.zoneCount = layoutInfo.zoneCount();
}
else if (customLayout.value().type == FancyZonesDataTypes::CustomLayoutType::Canvas)
{
auto layoutInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(customLayout.value().info);
layoutToApply.sensitivityRadius = layoutInfo.sensitivityRadius;
layoutToApply.zoneCount = (int)layoutInfo.zones.size();
}
}
else
{
// check templates only if it wasn't a custom layout, since templates don't have ids yet
auto templateLayout = LayoutTemplates::instance().GetLayout(layout.type);
if (templateLayout)
{
auto layoutInfo = templateLayout.value();
layoutToApply.sensitivityRadius = layoutInfo.sensitivityRadius;
layoutToApply.showSpacing = layoutInfo.showSpacing;
layoutToApply.spacing = layoutInfo.spacing;
layoutToApply.zoneCount = layoutInfo.zoneCount;
}
}
m_layouts[deviceId] = std::move(layoutToApply);
return true;
}
bool AppliedLayouts::ApplyDefaultLayout(const FancyZonesDataTypes::DeviceIdData& deviceId)
{
Logger::info(L"Set default layout on {}", deviceId.toString());
GUID guid;
auto result{ CoCreateGuid(&guid) };
if (!SUCCEEDED(result))
{
Logger::error("Failed to create an ID for the new layout");
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);
SaveData();
return true;
}
bool AppliedLayouts::CloneLayout(const FancyZonesDataTypes::DeviceIdData& srcId, const FancyZonesDataTypes::DeviceIdData& dstId)
{
if (srcId == dstId || m_layouts.find(srcId) == m_layouts.end())
{
return false;
}
Logger::info(L"Clone layout from {} to {}", dstId.toString(), srcId.toString());
m_layouts[dstId] = m_layouts[srcId];
SaveData();
return true;
}

View File

@@ -0,0 +1,68 @@
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <FancyZonesLib/FancyZonesData/Layout.h>
#include <FancyZonesLib/ModuleConstants.h>
#include <common/SettingsAPI/FileWatcher.h>
#include <common/SettingsAPI/settings_helpers.h>
namespace NonLocalizable
{
namespace AppliedLayoutsIds
{
const static wchar_t* AppliedLayoutsArrayID = L"applied-layouts";
const static wchar_t* DeviceIdID = L"device-id";
const static wchar_t* AppliedLayoutID = L"applied-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";
}
}
class AppliedLayouts
{
public:
using TAppliedLayoutsMap = std::unordered_map<FancyZonesDataTypes::DeviceIdData, Layout>;
static AppliedLayouts& instance();
inline static std::wstring AppliedLayoutsFileName()
{
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
#if defined(UNIT_TESTS)
return saveFolderPath + L"\\test-applied-layouts.json";
#endif
return saveFolderPath + L"\\applied-layouts.json";
}
void LoadData();
void SaveData();
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
void SyncVirtualDesktops(GUID currentVirtualDesktopId);
void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops);
std::optional<Layout> GetDeviceLayout(const FancyZonesDataTypes::DeviceIdData& id) const noexcept;
const TAppliedLayoutsMap& GetAppliedLayoutMap() const noexcept;
bool IsLayoutApplied(const FancyZonesDataTypes::DeviceIdData& id) const noexcept;
bool ApplyLayout(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& layout);
bool ApplyDefaultLayout(const FancyZonesDataTypes::DeviceIdData& deviceId);
bool CloneLayout(const FancyZonesDataTypes::DeviceIdData& srcId, const FancyZonesDataTypes::DeviceIdData& dstId);
private:
AppliedLayouts();
~AppliedLayouts() = default;
std::unique_ptr<FileWatcher> m_fileWatcher;
TAppliedLayoutsMap m_layouts;
std::function<bool(GUID)> m_virtualDesktopCheckCallback;
};

View File

@@ -3,7 +3,7 @@
#include <common/logger/logger.h>
#include <FancyZonesLib/FancyZonesData.h> // layout defaults
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>

View File

@@ -0,0 +1,15 @@
#pragma once
#include <guiddef.h>
#include <FancyZonesLib/FancyZonesDataTypes.h>
struct Layout
{
GUID uuid;
FancyZonesDataTypes::ZoneSetLayoutType type;
bool showSpacing;
int spacing;
int zoneCount;
int sensitivityRadius;
};

View File

@@ -0,0 +1,9 @@
#pragma once
namespace DefaultValues
{
const int ZoneCount = 3;
const bool ShowSpacing = true;
const int Spacing = 16;
const int SensitivityRadius = 20;
}

View File

@@ -0,0 +1,100 @@
#include "../pch.h"
#include "LayoutTemplates.h"
#include <common/logger/logger.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
namespace JsonUtils
{
struct TemplateLayoutJSON
{
static std::optional<Layout> FromJson(const json::JsonObject& json)
{
try
{
Layout data;
data.uuid = GUID_NULL;
data.type = FancyZonesDataTypes::TypeFromString(std::wstring{ json.GetNamedString(NonLocalizable::LayoutTemplatesIds::TypeID) });
data.showSpacing = json.GetNamedBoolean(NonLocalizable::LayoutTemplatesIds::ShowSpacingID, DefaultValues::ShowSpacing);
data.spacing = static_cast<int>(json.GetNamedNumber(NonLocalizable::LayoutTemplatesIds::SpacingID, DefaultValues::Spacing));
data.zoneCount = static_cast<int>(json.GetNamedNumber(NonLocalizable::LayoutTemplatesIds::ZoneCountID, DefaultValues::ZoneCount));
data.sensitivityRadius = static_cast<int>(json.GetNamedNumber(NonLocalizable::LayoutTemplatesIds::SensitivityRadiusID, DefaultValues::SensitivityRadius));
return data;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
};
std::vector<Layout> ParseJson(const json::JsonObject& json)
{
std::vector<Layout> vec{};
auto layouts = json.GetNamedArray(NonLocalizable::LayoutTemplatesIds::LayoutTemplatesArrayID);
for (uint32_t i = 0; i < layouts.Size(); ++i)
{
if (auto obj = TemplateLayoutJSON::FromJson(layouts.GetObjectAt(i)); obj.has_value())
{
vec.emplace_back(std::move(obj.value()));
}
}
return vec;
}
}
LayoutTemplates::LayoutTemplates()
{
const std::wstring& fileName = LayoutTemplatesFileName();
m_fileWatcher = std::make_unique<FileWatcher>(fileName, [&]() {
PostMessageW(HWND_BROADCAST, WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE, NULL, NULL);
});
}
LayoutTemplates& LayoutTemplates::instance()
{
static LayoutTemplates self;
return self;
}
void LayoutTemplates::LoadData()
{
auto data = json::from_file(LayoutTemplatesFileName());
try
{
if (data)
{
m_layouts = JsonUtils::ParseJson(data.value());
}
else
{
m_layouts.clear();
Logger::info(L"layout-templates.json file is missing or malformed");
}
}
catch (const winrt::hresult_error& e)
{
Logger::error(L"Parsing layout-templates error: {}", e.message());
}
}
std::optional<Layout> LayoutTemplates::GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept
{
for (const auto& layout : m_layouts)
{
if (layout.type == type)
{
return layout;
}
}
return std::nullopt;
}

View File

@@ -1,7 +1,9 @@
#pragma once
#include <FancyZonesLib/FancyZonesData/Layout.h>
#include <FancyZonesLib/ModuleConstants.h>
#include <common/SettingsAPI/FileWatcher.h>
#include <common/SettingsAPI/settings_helpers.h>
namespace NonLocalizable
@@ -9,12 +11,19 @@ namespace NonLocalizable
namespace LayoutTemplatesIds
{
const static wchar_t* LayoutTemplatesArrayID = L"layout-templates";
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";
}
}
class LayoutTemplates
{
public:
static LayoutTemplates& instance();
inline static std::wstring LayoutTemplatesFileName()
{
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
@@ -23,4 +32,15 @@ public:
#endif
return saveFolderPath + L"\\layout-templates.json";
}
void LoadData();
std::optional<Layout> GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept;
private:
LayoutTemplates();
~LayoutTemplates() = default;
std::unique_ptr<FileWatcher> m_fileWatcher;
std::vector<Layout> m_layouts;
};