mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 19:57:07 +02:00
[FancyZones] Split zones-settings: custom layouts (#15642)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user