mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
[FancyZones] Split zones-settings: devices -> applied layouts (#15779)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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>
|
||||
|
||||
15
src/modules/fancyzones/FancyZonesLib/FancyZonesData/Layout.h
Normal file
15
src/modules/fancyzones/FancyZonesLib/FancyZonesData/Layout.h
Normal 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;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
namespace DefaultValues
|
||||
{
|
||||
const int ZoneCount = 3;
|
||||
const bool ShowSpacing = true;
|
||||
const int Spacing = 16;
|
||||
const int SensitivityRadius = 20;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
Reference in New Issue
Block a user