[FancyZones] Split zones-settings: custom layouts (#15642)

This commit is contained in:
Seraphima Zykova
2022-01-20 14:02:38 +03:00
committed by GitHub
parent dec1aca97f
commit 714ca349ff
24 changed files with 853 additions and 448 deletions

View File

@@ -0,0 +1,233 @@
#include "../pch.h"
#include "CustomLayouts.h"
#include <common/logger/logger.h>
#include <FancyZonesLib/FancyZonesData.h> // layout defaults
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>
namespace JsonUtils
{
std::vector<int> JsonArrayToNumVec(const json::JsonArray& arr)
{
std::vector<int> vec;
for (const auto& val : arr)
{
vec.emplace_back(static_cast<int>(val.GetNumber()));
}
return vec;
}
namespace CanvasLayoutInfoJSON
{
std::optional<FancyZonesDataTypes::CanvasLayoutInfo> FromJson(const json::JsonObject& infoJson)
{
try
{
FancyZonesDataTypes::CanvasLayoutInfo info;
info.lastWorkAreaWidth = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::RefWidthID));
info.lastWorkAreaHeight = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::RefHeightID));
json::JsonArray zonesJson = infoJson.GetNamedArray(NonLocalizable::CustomLayoutsIds::ZonesID);
uint32_t size = zonesJson.Size();
info.zones.reserve(size);
for (uint32_t i = 0; i < size; ++i)
{
json::JsonObject zoneJson = zonesJson.GetObjectAt(i);
const int x = static_cast<int>(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::XID));
const int y = static_cast<int>(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::YID));
const int width = static_cast<int>(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::WidthID));
const int height = static_cast<int>(zoneJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::HeightID));
FancyZonesDataTypes::CanvasLayoutInfo::Rect zone{ x, y, width, height };
info.zones.push_back(zone);
}
info.sensitivityRadius = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::SensitivityRadiusID, DefaultValues::SensitivityRadius));
return info;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
}
namespace GridLayoutInfoJSON
{
std::optional<FancyZonesDataTypes::GridLayoutInfo> FromJson(const json::JsonObject& infoJson)
{
try
{
FancyZonesDataTypes::GridLayoutInfo info(FancyZonesDataTypes::GridLayoutInfo::Minimal{});
info.m_rows = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::RowsID));
info.m_columns = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::ColumnsID));
json::JsonArray rowsPercentage = infoJson.GetNamedArray(NonLocalizable::CustomLayoutsIds::RowsPercentageID);
json::JsonArray columnsPercentage = infoJson.GetNamedArray(NonLocalizable::CustomLayoutsIds::ColumnsPercentageID);
json::JsonArray cellChildMap = infoJson.GetNamedArray(NonLocalizable::CustomLayoutsIds::CellChildMapID);
if (rowsPercentage.Size() != info.m_rows || columnsPercentage.Size() != info.m_columns || cellChildMap.Size() != info.m_rows)
{
return std::nullopt;
}
info.m_rowsPercents = JsonArrayToNumVec(rowsPercentage);
info.m_columnsPercents = JsonArrayToNumVec(columnsPercentage);
for (const auto& cellsRow : cellChildMap)
{
const auto cellsArray = cellsRow.GetArray();
if (cellsArray.Size() != info.m_columns)
{
return std::nullopt;
}
info.cellChildMap().push_back(JsonArrayToNumVec(cellsArray));
}
info.m_showSpacing = infoJson.GetNamedBoolean(NonLocalizable::CustomLayoutsIds::ShowSpacingID, DefaultValues::ShowSpacing);
info.m_spacing = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::SpacingID, DefaultValues::Spacing));
info.m_sensitivityRadius = static_cast<int>(infoJson.GetNamedNumber(NonLocalizable::CustomLayoutsIds::SensitivityRadiusID, DefaultValues::SensitivityRadius));
return info;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
}
struct CustomLayoutJSON
{
GUID layoutId;
FancyZonesDataTypes::CustomLayoutData data;
static std::optional<CustomLayoutJSON> FromJson(const json::JsonObject& json)
{
try
{
CustomLayoutJSON result;
auto idStr = json.GetNamedString(NonLocalizable::CustomLayoutsIds::UuidID);
auto id = FancyZonesUtils::GuidFromString(idStr.c_str());
if (!id)
{
return std::nullopt;
}
result.layoutId = id.value();
result.data.name = json.GetNamedString(NonLocalizable::CustomLayoutsIds::NameID);
json::JsonObject infoJson = json.GetNamedObject(NonLocalizable::CustomLayoutsIds::InfoID);
std::wstring zoneSetType = std::wstring{ json.GetNamedString(NonLocalizable::CustomLayoutsIds::TypeID) };
if (zoneSetType.compare(NonLocalizable::CustomLayoutsIds::CanvasID) == 0)
{
if (auto info = CanvasLayoutInfoJSON::FromJson(infoJson); info.has_value())
{
result.data.type = FancyZonesDataTypes::CustomLayoutType::Canvas;
result.data.info = std::move(info.value());
}
else
{
return std::nullopt;
}
}
else if (zoneSetType.compare(NonLocalizable::CustomLayoutsIds::GridID) == 0)
{
if (auto info = GridLayoutInfoJSON::FromJson(infoJson); info.has_value())
{
result.data.type = FancyZonesDataTypes::CustomLayoutType::Grid;
result.data.info = std::move(info.value());
}
else
{
return std::nullopt;
}
}
else
{
return std::nullopt;
}
return result;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
};
CustomLayouts::TCustomLayoutMap ParseJson(const json::JsonObject& json)
{
CustomLayouts::TCustomLayoutMap map{};
auto layouts = json.GetNamedArray(NonLocalizable::CustomLayoutsIds::CustomLayoutsArrayID);
for (uint32_t i = 0; i < layouts.Size(); ++i)
{
if (auto obj = CustomLayoutJSON::FromJson(layouts.GetObjectAt(i)); obj.has_value())
{
map[obj->layoutId] = std::move(obj->data);
}
}
return std::move(map);
}
}
CustomLayouts::CustomLayouts()
{
const std::wstring& dataFileName = CustomLayoutsFileName();
m_fileWatcher = std::make_unique<FileWatcher>(dataFileName, [&]() {
PostMessageW(HWND_BROADCAST, WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE, NULL, NULL);
});
}
CustomLayouts& CustomLayouts::instance()
{
static CustomLayouts self;
return self;
}
void CustomLayouts::LoadData()
{
auto data = json::from_file(CustomLayoutsFileName());
try
{
if (data)
{
m_layouts = JsonUtils::ParseJson(data.value());
}
else
{
m_layouts.clear();
Logger::info(L"custom-layouts.json file is missing or malformed");
}
}
catch (const winrt::hresult_error& e)
{
Logger::error(L"Parsing custom-layouts error: {}", e.message());
}
}
std::optional<FancyZonesDataTypes::CustomLayoutData> CustomLayouts::GetLayout(const GUID& id) const noexcept
{
auto iter = m_layouts.find(id);
if (iter != m_layouts.end())
{
return iter->second;
}
return std::nullopt;
}
const CustomLayouts::TCustomLayoutMap& CustomLayouts::GetAllLayouts() const noexcept
{
return m_layouts;
}

View File

@@ -0,0 +1,75 @@
#pragma once
#include <guiddef.h>
#include <map>
#include <memory>
#include <optional>
#include <FancyZonesLib/FancyZonesDataTypes.h>
#include <FancyZonesLib/GuidUtils.h>
#include <FancyZonesLib/ModuleConstants.h>
#include <common/SettingsAPI/FileWatcher.h>
#include <common/SettingsAPI/settings_helpers.h>
namespace NonLocalizable
{
namespace CustomLayoutsIds
{
const static wchar_t* CustomLayoutsArrayID = L"custom-layouts";
const static wchar_t* UuidID = L"uuid";
const static wchar_t* NameID = L"name";
const static wchar_t* InfoID = L"info";
const static wchar_t* TypeID = L"type";
const static wchar_t* CanvasID = L"canvas";
const static wchar_t* GridID = L"grid";
const static wchar_t* SensitivityRadiusID = L"sensitivity-radius";
// canvas
const static wchar_t* RefHeightID = L"ref-height";
const static wchar_t* RefWidthID = L"ref-width";
const static wchar_t* ZonesID = L"zones";
const static wchar_t* XID = L"X";
const static wchar_t* YID = L"Y";
const static wchar_t* WidthID = L"width";
const static wchar_t* HeightID = L"height";
// grid
const static wchar_t* RowsID = L"rows";
const static wchar_t* ColumnsID = L"columns";
const static wchar_t* RowsPercentageID = L"rows-percentage";
const static wchar_t* ColumnsPercentageID = L"columns-percentage";
const static wchar_t* CellChildMapID = L"cell-child-map";
const static wchar_t* ShowSpacingID = L"show-spacing";
const static wchar_t* SpacingID = L"spacing";
}
}
class CustomLayouts
{
public:
using TCustomLayoutMap = std::unordered_map<GUID, FancyZonesDataTypes::CustomLayoutData>;
static CustomLayouts& instance();
inline static std::wstring CustomLayoutsFileName()
{
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
#if defined(UNIT_TESTS)
return saveFolderPath + L"\\test-custom-layouts.json";
#endif
return saveFolderPath + L"\\custom-layouts.json";
}
void LoadData();
std::optional<FancyZonesDataTypes::CustomLayoutData> GetLayout(const GUID& id) const noexcept;
const TCustomLayoutMap& GetAllLayouts() const noexcept;
private:
CustomLayouts();
~CustomLayouts() = default;
TCustomLayoutMap m_layouts;
std::unique_ptr<FileWatcher> m_fileWatcher;
};