[FancyZones] Remove resolution from "device-id" (#17412)

* removed resolution from device id

* update applied layouts device id

* app zone history device id updated

* moved old device id parsing

* updated tests

* remove resolution in the editor

* remove resolution from device id generation

* update editor params
This commit is contained in:
Seraphima Zykova
2022-04-07 11:48:29 +02:00
committed by GitHub
parent 529bccc0bf
commit 98268cc10a
19 changed files with 969 additions and 928 deletions

View File

@@ -862,23 +862,10 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const std::wstring& deviceId) noe
if (monitor)
{
uniqueId.deviceName = FancyZonesUtils::TrimDeviceId(deviceId);
MONITORINFOEXW mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(monitor, &mi))
{
const FancyZonesUtils::Rect monitorRect(mi.rcMonitor);
uniqueId.width = monitorRect.width();
uniqueId.height = monitorRect.height();
}
}
else
{
uniqueId.deviceName = ZonedWindowProperties::MultiMonitorDeviceID;
RECT combinedResolution = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcMonitor>();
uniqueId.width = combinedResolution.right - combinedResolution.left;
uniqueId.height = combinedResolution.bottom - combinedResolution.top;
}
FancyZonesDataTypes::DeviceIdData parentId{};

View File

@@ -9,6 +9,7 @@
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/ModuleConstants.h>
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/util.h>
// Non-localizable strings
@@ -81,10 +82,10 @@ void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors
if (spanZonesAcrossMonitors)
{
auto monitorRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
std::wstring monitorId = FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId);
JSONHelpers::MonitorInfo monitorJson;
monitorJson.id = monitorId;
monitorJson.monitorName = ZonedWindowProperties::MultiMonitorDeviceID;
monitorJson.virtualDesktop = virtualDesktopId;
monitorJson.top = monitorRect.top;
monitorJson.left = monitorRect.left;
monitorJson.width = monitorRect.right - monitorRect.left;
@@ -107,14 +108,14 @@ void FancyZonesData::SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors
JSONHelpers::MonitorInfo monitorJson;
std::wstring deviceId = FancyZonesUtils::GetDisplayDeviceId(monitorInfo.szDevice, displayDeviceIdxMap);
std::wstring monitorId = FancyZonesUtils::GenerateUniqueId(monitor, deviceId, virtualDesktopId);
if (monitor == targetMonitor)
{
monitorJson.isSelected = true; /* Is monitor selected for the main editor window opening */
}
monitorJson.id = monitorId; /* Monitor id */
monitorJson.monitorName = FancyZonesUtils::TrimDeviceId(deviceId); /* Monitor name */
monitorJson.virtualDesktop = virtualDesktopId; /* Virtual desktop id */
UINT dpi = 0;
if (DPIAware::GetScreenDPIForMonitor(monitor, dpi) != S_OK)

View File

@@ -10,6 +10,207 @@
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>
namespace JsonUtils
{
struct AppZoneHistoryJSON
{
private:
static std::optional<FancyZonesDataTypes::DeviceIdData> DeviceIdFromJson(const json::JsonObject& json)
{
try
{
if (json.HasKey(NonLocalizable::AppZoneHistoryIds::DeviceID))
{
json::JsonObject device = json.GetNamedObject(NonLocalizable::AppZoneHistoryIds::DeviceID);
std::wstring monitor = device.GetNamedString(NonLocalizable::AppZoneHistoryIds::MonitorID).c_str();
std::wstring virtualDesktop = device.GetNamedString(NonLocalizable::AppZoneHistoryIds::VirtualDesktopID).c_str();
auto virtualDesktopGuid = FancyZonesUtils::GuidFromString(virtualDesktop);
if (!virtualDesktopGuid)
{
return std::nullopt;
}
return FancyZonesDataTypes::DeviceIdData{
.deviceName = monitor,
.virtualDesktopId = virtualDesktopGuid.value(),
};
}
else
{
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::DeviceIdID).c_str();
auto bcDeviceId = BackwardsCompatibility::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!bcDeviceId)
{
return std::nullopt;
}
return FancyZonesDataTypes::DeviceIdData{
.deviceName = bcDeviceId->deviceName,
.virtualDesktopId = bcDeviceId->virtualDesktopId,
};
}
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
static std::optional<FancyZonesDataTypes::AppZoneHistoryData> ParseSingleAppZoneHistoryItem(const json::JsonObject& json)
{
FancyZonesDataTypes::AppZoneHistoryData data;
if (json.HasKey(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID))
{
data.zoneIndexSet = {};
for (const auto& value : json.GetNamedArray(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID))
{
data.zoneIndexSet.push_back(static_cast<ZoneIndex>(value.GetNumber()));
}
}
else if (json.HasKey(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID))
{
data.zoneIndexSet = { static_cast<ZoneIndex>(json.GetNamedNumber(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID)) };
}
auto deviceIdOpt = DeviceIdFromJson(json);
if (!deviceIdOpt)
{
return std::nullopt;
}
data.deviceId = deviceIdOpt.value();
data.zoneSetUuid = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID);
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
{
return std::nullopt;
}
return data;
}
public:
std::wstring appPath;
std::vector<FancyZonesDataTypes::AppZoneHistoryData> data;
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& json)
{
try
{
AppZoneHistoryJSON result;
result.appPath = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::AppPathID);
if (json.HasKey(NonLocalizable::AppZoneHistoryIds::HistoryID))
{
auto appHistoryArray = json.GetNamedArray(NonLocalizable::AppZoneHistoryIds::HistoryID);
for (uint32_t i = 0; i < appHistoryArray.Size(); ++i)
{
json::JsonObject json = appHistoryArray.GetObjectAt(i);
if (auto data = ParseSingleAppZoneHistoryItem(json); data.has_value())
{
result.data.push_back(std::move(data.value()));
}
}
}
else
{
// handle previous file format, with single desktop layout information per application
if (auto data = ParseSingleAppZoneHistoryItem(json); data.has_value())
{
result.data.push_back(std::move(data.value()));
}
}
if (result.data.empty())
{
return std::nullopt;
}
return result;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
static json::JsonObject ToJson(const AppZoneHistoryJSON& appZoneHistory)
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::AppZoneHistoryIds::AppPathID, json::value(appZoneHistory.appPath));
json::JsonArray appHistoryArray;
for (const auto& data : appZoneHistory.data)
{
json::JsonObject desktopData;
json::JsonArray jsonIndexSet;
for (ZoneIndex index : data.zoneIndexSet)
{
jsonIndexSet.Append(json::value(static_cast<int>(index)));
}
json::JsonObject device{};
device.SetNamedValue(NonLocalizable::AppZoneHistoryIds::MonitorID, json::value(data.deviceId.deviceName));
auto virtualDesktopStr = FancyZonesUtils::GuidToString(data.deviceId.virtualDesktopId);
if (virtualDesktopStr)
{
device.SetNamedValue(NonLocalizable::AppZoneHistoryIds::VirtualDesktopID, json::value(virtualDesktopStr.value()));
}
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID, jsonIndexSet);
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::DeviceID, device);
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIdID, json::value(data.zoneSetUuid));
appHistoryArray.Append(desktopData);
}
result.SetNamedValue(NonLocalizable::AppZoneHistoryIds::HistoryID, appHistoryArray);
return result;
}
};
AppZoneHistory::TAppZoneHistoryMap ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON)
{
try
{
AppZoneHistory::TAppZoneHistoryMap appZoneHistoryMap{};
auto appLastZones = fancyZonesDataJSON.GetNamedArray(NonLocalizable::AppZoneHistoryIds::AppZoneHistoryID);
for (uint32_t i = 0; i < appLastZones.Size(); ++i)
{
json::JsonObject appLastZone = appLastZones.GetObjectAt(i);
if (auto appZoneHistory = AppZoneHistoryJSON::FromJson(appLastZone); appZoneHistory.has_value())
{
appZoneHistoryMap[appZoneHistory->appPath] = std::move(appZoneHistory->data);
}
}
return std::move(appZoneHistoryMap);
}
catch (const winrt::hresult_error&)
{
return {};
}
}
json::JsonObject SerializeJson(const AppZoneHistory::TAppZoneHistoryMap& map)
{
json::JsonObject root{};
json::JsonArray appHistoryArray{};
for (const auto& [appPath, appZoneHistoryData] : map)
{
appHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, appZoneHistoryData }));
}
root.SetNamedValue(NonLocalizable::AppZoneHistoryIds::AppZoneHistoryID, appHistoryArray);
return root;
}
}
AppZoneHistory::AppZoneHistory()
{
}
@@ -34,7 +235,7 @@ void AppZoneHistory::LoadData()
{
if (data)
{
m_history = JSONHelpers::ParseAppZoneHistory(data.value());
m_history = JsonUtils::ParseAppZoneHistory(data.value());
}
else
{
@@ -72,11 +273,11 @@ void AppZoneHistory::SaveData()
if (dirtyFlag)
{
JSONHelpers::SaveAppZoneHistory(AppZoneHistoryFileName(), updatedHistory);
json::to_file(AppZoneHistoryFileName(), JsonUtils::SerializeJson(updatedHistory));
}
else
{
JSONHelpers::SaveAppZoneHistory(AppZoneHistoryFileName(), m_history);
json::to_file(AppZoneHistoryFileName(), JsonUtils::SerializeJson(m_history));
}
}

View File

@@ -5,6 +5,22 @@
#include <common/SettingsAPI/settings_helpers.h>
namespace NonLocalizable
{
namespace AppZoneHistoryIds
{
const static wchar_t* AppZoneHistoryID = L"app-zone-history";
const static wchar_t* AppPathID = L"app-path";
const static wchar_t* HistoryID = L"history";
const static wchar_t* LayoutIndexesID = L"zone-index-set";
const static wchar_t* LayoutIdID = L"zoneset-uuid";
const static wchar_t* DeviceIdID = L"device-id";
const static wchar_t* DeviceID = L"device";
const static wchar_t* MonitorID = L"monitor";
const static wchar_t* VirtualDesktopID = L"virtual-desktop";
}
}
class AppZoneHistory
{
public:

View File

@@ -11,6 +11,19 @@
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>
namespace
{
// didn't use default constants since if they'll be changed later, it'll break this function
bool isLayoutDefault(const Layout& layout)
{
return layout.type == FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid &&
layout.zoneCount == 3 &&
layout.spacing == 16 &&
layout.showSpacing == true &&
layout.sensitivityRadius == 20;
}
}
namespace JsonUtils
{
struct LayoutJSON
@@ -57,6 +70,50 @@ namespace JsonUtils
struct AppliedLayoutsJSON
{
private:
static std::optional<FancyZonesDataTypes::DeviceIdData> DeviceIdFromJson(const json::JsonObject& json)
{
try
{
if (json.HasKey(NonLocalizable::AppliedLayoutsIds::DeviceID))
{
json::JsonObject device = json.GetNamedObject(NonLocalizable::AppliedLayoutsIds::DeviceID);
std::wstring monitor = device.GetNamedString(NonLocalizable::AppliedLayoutsIds::MonitorID).c_str();
std::wstring virtualDesktop = device.GetNamedString(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID).c_str();
auto virtualDesktopGuid = FancyZonesUtils::GuidFromString(virtualDesktop);
if (!virtualDesktopGuid)
{
return std::nullopt;
}
return FancyZonesDataTypes::DeviceIdData{
.deviceName = monitor,
.virtualDesktopId = virtualDesktopGuid.value(),
};
}
else
{
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::AppliedLayoutsIds::DeviceIdID).c_str();
auto bcDeviceId = BackwardsCompatibility::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!bcDeviceId)
{
return std::nullopt;
}
return FancyZonesDataTypes::DeviceIdData{
.deviceName = bcDeviceId->deviceName,
.virtualDesktopId = bcDeviceId->virtualDesktopId,
};
}
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
public:
FancyZonesDataTypes::DeviceIdData deviceId;
Layout data;
@@ -66,9 +123,8 @@ namespace JsonUtils
{
AppliedLayoutsJSON result;
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::AppliedLayoutsIds::DeviceIdID).c_str();
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!deviceId.has_value())
auto deviceIdOpt = DeviceIdFromJson(json);
if (!deviceIdOpt.has_value())
{
return std::nullopt;
}
@@ -79,7 +135,7 @@ namespace JsonUtils
return std::nullopt;
}
result.deviceId = std::move(deviceId.value());
result.deviceId = std::move(deviceIdOpt.value());
result.data = std::move(layout.value());
return result;
}
@@ -91,9 +147,17 @@ namespace JsonUtils
static json::JsonObject ToJson(const AppliedLayoutsJSON& value)
{
json::JsonObject result{};
json::JsonObject device{};
device.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(value.deviceId.deviceName));
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(value.deviceId.toString()));
auto virtualDesktopStr = FancyZonesUtils::GuidToString(value.deviceId.virtualDesktopId);
if (virtualDesktopStr)
{
device.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(virtualDesktopStr.value()));
}
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceID, device);
result.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, JsonUtils::LayoutJSON::ToJson(value.data));
return result;
@@ -109,7 +173,12 @@ namespace JsonUtils
{
if (auto obj = AppliedLayoutsJSON::FromJson(layouts.GetObjectAt(i)); obj.has_value())
{
map[obj->deviceId] = std::move(obj->data);
// 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->deviceId) && !isLayoutDefault(obj->data))
{
map[obj->deviceId] = std::move(obj->data);
}
}
}

View File

@@ -16,6 +16,9 @@ namespace NonLocalizable
{
const static wchar_t* AppliedLayoutsArrayID = L"applied-layouts";
const static wchar_t* DeviceIdID = L"device-id";
const static wchar_t* DeviceID = L"device";
const static wchar_t* MonitorID = L"monitor";
const static wchar_t* VirtualDesktopID = L"virtual-desktop";
const static wchar_t* AppliedLayoutID = L"applied-layout";
const static wchar_t* UuidID = L"uuid";
const static wchar_t* TypeID = L"type";

View File

@@ -129,166 +129,6 @@ namespace FancyZonesDataTypes
return high + 1;
}
std::optional<DeviceIdData> DeviceIdData::ParseDeviceId(const std::wstring& str)
{
FancyZonesDataTypes::DeviceIdData data;
std::wstring temp;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
data.deviceName = temp;
if (!std::getline(wss, temp, L'_'))
{
return std::nullopt;
}
data.deviceName += L"#" + temp;
}
else if (std::getline(wss, temp, L'_') && !temp.empty())
{
data.deviceName = temp;
}
else
{
return std::nullopt;
}
// Step 2: parse the rest of the id
std::vector<std::wstring> parts;
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 3 && parts.size() != 4)
{
return std::nullopt;
}
/*
Refer to FancyZonesUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
for (const auto& c : parts[0])
{
std::ignore = std::stoi(std::wstring(&c));
}
for (const auto& c : parts[1])
{
std::ignore = std::stoi(std::wstring(&c));
}
data.width = std::stoi(parts[0]);
data.height = std::stoi(parts[1]);
}
catch (const std::exception&)
{
return std::nullopt;
}
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
{
return std::nullopt;
}
if (parts.size() == 4)
{
data.monitorId = parts[3]; //could be empty
}
return data;
}
bool DeviceIdData::IsValidDeviceId(const std::wstring& str)
{
std::wstring monitorName;
std::wstring temp;
std::vector<std::wstring> parts;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
monitorName = temp;
if (!std::getline(wss, temp, L'_'))
{
return false;
}
monitorName += L"#" + temp;
parts.push_back(monitorName);
}
// Step 2: parse the rest of the id
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 4)
{
return false;
}
/*
Refer to FancyZonesUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
//check if resolution contain only digits
for (const auto& c : parts[1])
{
std::ignore = std::stoi(std::wstring(&c));
}
for (const auto& c : parts[2])
{
std::ignore = std::stoi(std::wstring(&c));
}
}
catch (const std::exception&)
{
return false;
}
if (!FancyZonesUtils::IsValidGuid(parts[3]) || parts[0].empty())
{
return false;
}
return true;
}
std::wstring DeviceIdData::toString() const
{
wil::unique_cotaskmem_string virtualDesktopIdStr;
@@ -297,11 +137,7 @@ namespace FancyZonesDataTypes
return std::wstring();
}
std::wstring result = deviceName + L"_" + std::to_wstring(width) + L"_" + std::to_wstring(height) + L"_" + virtualDesktopIdStr.get();
if (!monitorId.empty())
{
result += L"_" + monitorId;
}
std::wstring result = deviceName + L"_" + virtualDesktopIdStr.get();
return result;
}

View File

@@ -116,14 +116,8 @@ namespace FancyZonesDataTypes
struct DeviceIdData
{
std::wstring deviceName = L"FallbackDevice";
int width;
int height;
GUID virtualDesktopId;
std::wstring monitorId;
static std::optional<DeviceIdData> ParseDeviceId(const std::wstring& str);
static bool IsValidDeviceId(const std::wstring& str);
std::wstring toString() const;
};
@@ -152,7 +146,7 @@ namespace FancyZonesDataTypes
inline bool operator==(const DeviceIdData& lhs, const DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) == 0 && lhs.width == rhs.width && lhs.height == rhs.height && (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL) && lhs.monitorId.compare(rhs.monitorId) == 0;
return lhs.deviceName.compare(rhs.deviceName) == 0 && (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL);
}
inline bool operator!=(const DeviceIdData& lhs, const DeviceIdData& rhs)
@@ -162,7 +156,7 @@ namespace FancyZonesDataTypes
inline bool operator<(const DeviceIdData& lhs, const DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) < 0 || lhs.width < rhs.width || lhs.height < rhs.height || lhs.monitorId.compare(rhs.monitorId) < 0;
return lhs.deviceName.compare(rhs.deviceName) < 0;
}
inline bool operator==(const DeviceInfoData& lhs, const DeviceInfoData& rhs)

View File

@@ -63,7 +63,8 @@ namespace NonLocalizable
// Editor arguments
const wchar_t Dpi[] = L"dpi";
const wchar_t MonitorId[] = L"monitor-id";
const wchar_t MonitorNameId[] = L"monitor";
const wchar_t VirtualDesktopId[] = L"virtual-desktop";
const wchar_t TopCoordinate[] = L"top-coordinate";
const wchar_t LeftCoordinate[] = L"left-coordinate";
const wchar_t Width[] = L"width";
@@ -74,6 +75,169 @@ namespace NonLocalizable
const wchar_t Monitors[] = L"monitors";
}
namespace BackwardsCompatibility
{
std::optional<DeviceIdData> DeviceIdData::ParseDeviceId(const std::wstring& str)
{
DeviceIdData data;
std::wstring temp;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
data.deviceName = temp;
if (!std::getline(wss, temp, L'_'))
{
return std::nullopt;
}
data.deviceName += L"#" + temp;
}
else if (std::getline(wss, temp, L'_') && !temp.empty())
{
data.deviceName = temp;
}
else
{
return std::nullopt;
}
// Step 2: parse the rest of the id
std::vector<std::wstring> parts;
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 3 && parts.size() != 4)
{
return std::nullopt;
}
/*
Refer to FancyZonesUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
for (const auto& c : parts[0])
{
std::ignore = std::stoi(std::wstring(&c));
}
for (const auto& c : parts[1])
{
std::ignore = std::stoi(std::wstring(&c));
}
data.width = std::stoi(parts[0]);
data.height = std::stoi(parts[1]);
}
catch (const std::exception&)
{
return std::nullopt;
}
if (!SUCCEEDED(CLSIDFromString(parts[2].c_str(), &data.virtualDesktopId)))
{
return std::nullopt;
}
if (parts.size() == 4)
{
data.monitorId = parts[3]; //could be empty
}
return data;
}
bool DeviceIdData::IsValidDeviceId(const std::wstring& str)
{
std::wstring monitorName;
std::wstring temp;
std::vector<std::wstring> parts;
std::wstringstream wss(str);
/*
Important fix for device info that contains a '_' in the name:
1. first search for '#'
2. Then split the remaining string by '_'
*/
// Step 1: parse the name until the #, then to the '_'
if (str.find(L'#') != std::string::npos)
{
std::getline(wss, temp, L'#');
monitorName = temp;
if (!std::getline(wss, temp, L'_'))
{
return false;
}
monitorName += L"#" + temp;
parts.push_back(monitorName);
}
// Step 2: parse the rest of the id
while (std::getline(wss, temp, L'_'))
{
parts.push_back(temp);
}
if (parts.size() != 4)
{
return false;
}
/*
Refer to FancyZonesUtils::GenerateUniqueId parts contain:
1. monitor id [string]
2. width of device [int]
3. height of device [int]
4. virtual desktop id (GUID) [string]
*/
try
{
//check if resolution contain only digits
for (const auto& c : parts[1])
{
std::ignore = std::stoi(std::wstring(&c));
}
for (const auto& c : parts[2])
{
std::ignore = std::stoi(std::wstring(&c));
}
}
catch (const std::exception&)
{
return false;
}
if (!FancyZonesUtils::IsValidGuid(parts[3]) || parts[0].empty())
{
return false;
}
return true;
}
}
namespace
{
json::JsonArray NumVecToJsonArray(const std::vector<int>& vec)
@@ -115,13 +279,16 @@ namespace
}
std::wstring deviceIdStr = json.GetNamedString(NonLocalizable::DeviceIdStr).c_str();
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
auto deviceId = BackwardsCompatibility::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!deviceId.has_value())
{
return std::nullopt;
}
data.deviceId = *deviceId;
data.deviceId = FancyZonesDataTypes::DeviceIdData{
.deviceName = deviceId->deviceName,
.virtualDesktopId = deviceId->virtualDesktopId
};
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
@@ -372,88 +539,6 @@ namespace JSONHelpers
}
}
json::JsonObject AppZoneHistoryJSON::ToJson(const AppZoneHistoryJSON& appZoneHistory)
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::AppPathStr, json::value(appZoneHistory.appPath));
json::JsonArray appHistoryArray;
for (const auto& data : appZoneHistory.data)
{
json::JsonObject desktopData;
json::JsonArray jsonIndexSet;
for (ZoneIndex index : data.zoneIndexSet)
{
jsonIndexSet.Append(json::value(static_cast<int>(index)));
}
desktopData.SetNamedValue(NonLocalizable::ZoneIndexSetStr, jsonIndexSet);
desktopData.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(data.deviceId.toString()));
desktopData.SetNamedValue(NonLocalizable::ZoneSetUuidStr, json::value(data.zoneSetUuid));
appHistoryArray.Append(desktopData);
}
result.SetNamedValue(NonLocalizable::HistoryStr, appHistoryArray);
return result;
}
std::optional<AppZoneHistoryJSON> AppZoneHistoryJSON::FromJson(const json::JsonObject& zoneSet)
{
try
{
AppZoneHistoryJSON result;
result.appPath = zoneSet.GetNamedString(NonLocalizable::AppPathStr);
if (zoneSet.HasKey(NonLocalizable::HistoryStr))
{
auto appHistoryArray = zoneSet.GetNamedArray(NonLocalizable::HistoryStr);
for (uint32_t i = 0; i < appHistoryArray.Size(); ++i)
{
json::JsonObject json = appHistoryArray.GetObjectAt(i);
if (auto data = ParseSingleAppZoneHistoryItem(json); data.has_value())
{
result.data.push_back(std::move(data.value()));
}
}
}
else
{
// handle previous file format, with single desktop layout information per application
if (auto data = ParseSingleAppZoneHistoryItem(zoneSet); data.has_value())
{
result.data.push_back(std::move(data.value()));
}
}
if (result.data.empty())
{
return std::nullopt;
}
return result;
}
catch (const winrt::hresult_error&)
{
return std::nullopt;
}
}
json::JsonObject DeviceInfoJSON::ToJson(const DeviceInfoJSON& device)
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::DeviceIdStr, json::value(device.deviceId.toString()));
result.SetNamedValue(NonLocalizable::ActiveZoneSetStr, JSONHelpers::ZoneSetDataJSON::ToJson(device.data.activeZoneSet));
result.SetNamedValue(NonLocalizable::EditorShowSpacingStr, json::value(device.data.showSpacing));
result.SetNamedValue(NonLocalizable::EditorSpacingStr, json::value(device.data.spacing));
result.SetNamedValue(NonLocalizable::EditorZoneCountStr, json::value(device.data.zoneCount));
result.SetNamedValue(NonLocalizable::EditorSensitivityRadiusStr, json::value(device.data.sensitivityRadius));
return result;
}
std::optional<DeviceInfoJSON> DeviceInfoJSON::FromJson(const json::JsonObject& device)
{
try
@@ -461,7 +546,7 @@ namespace JSONHelpers
DeviceInfoJSON result;
std::wstring deviceIdStr = device.GetNamedString(NonLocalizable::DeviceIdStr).c_str();
auto deviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(deviceIdStr);
auto deviceId = BackwardsCompatibility::DeviceIdData::ParseDeviceId(deviceIdStr);
if (!deviceId.has_value())
{
return std::nullopt;
@@ -527,8 +612,9 @@ namespace JSONHelpers
{
json::JsonObject result{};
result.SetNamedValue(NonLocalizable::MonitorNameId, json::value(monitor.monitorName));
result.SetNamedValue(NonLocalizable::VirtualDesktopId, json::value(monitor.virtualDesktop));
result.SetNamedValue(NonLocalizable::Dpi, json::value(monitor.dpi));
result.SetNamedValue(NonLocalizable::MonitorId, json::value(monitor.id));
result.SetNamedValue(NonLocalizable::TopCoordinate, json::value(monitor.top));
result.SetNamedValue(NonLocalizable::LeftCoordinate, json::value(monitor.left));
result.SetNamedValue(NonLocalizable::Width, json::value(monitor.width));
@@ -581,55 +667,6 @@ namespace JSONHelpers
}
}
void SaveAppZoneHistory(const std::wstring& appZoneHistoryFileName, const TAppZoneHistoryMap& appZoneHistoryMap)
{
json::JsonObject root{};
root.SetNamedValue(NonLocalizable::AppZoneHistoryStr, JSONHelpers::SerializeAppZoneHistory(appZoneHistoryMap));
auto before = json::from_file(appZoneHistoryFileName);
if (!before.has_value() || before.value().Stringify() != root.Stringify())
{
json::to_file(appZoneHistoryFileName, root);
}
}
TAppZoneHistoryMap ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON)
{
try
{
TAppZoneHistoryMap appZoneHistoryMap{};
auto appLastZones = fancyZonesDataJSON.GetNamedArray(NonLocalizable::AppZoneHistoryStr);
for (uint32_t i = 0; i < appLastZones.Size(); ++i)
{
json::JsonObject appLastZone = appLastZones.GetObjectAt(i);
if (auto appZoneHistory = AppZoneHistoryJSON::FromJson(appLastZone); appZoneHistory.has_value())
{
appZoneHistoryMap[appZoneHistory->appPath] = std::move(appZoneHistory->data);
}
}
return std::move(appZoneHistoryMap);
}
catch (const winrt::hresult_error&)
{
return {};
}
}
json::JsonArray SerializeAppZoneHistory(const TAppZoneHistoryMap& appZoneHistoryMap)
{
json::JsonArray appHistoryArray;
for (const auto& [appPath, appZoneHistoryData] : appZoneHistoryMap)
{
appHistoryArray.Append(AppZoneHistoryJSON::ToJson(AppZoneHistoryJSON{ appPath, appZoneHistoryData }));
}
return appHistoryArray;
}
std::optional<TDeviceInfoMap> ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
{
try
@@ -670,7 +707,16 @@ namespace JSONHelpers
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(data.sensitivityRadius));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(deviceID.toString()));
json::JsonObject device{};
device.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(deviceID.deviceName));
auto virtualDesktopStr = FancyZonesUtils::GuidToString(deviceID.virtualDesktopId);
if (virtualDesktopStr)
{
device.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(virtualDesktopStr.value()));
}
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceID, device);
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
layoutsArray.Append(obj);

View File

@@ -8,6 +8,36 @@
#include <vector>
#include <unordered_map>
namespace BackwardsCompatibility
{
struct DeviceIdData
{
std::wstring deviceName = L"FallbackDevice";
int width;
int height;
GUID virtualDesktopId;
std::wstring monitorId;
static std::optional<DeviceIdData> ParseDeviceId(const std::wstring& str);
static bool IsValidDeviceId(const std::wstring& str);
};
inline bool operator==(const BackwardsCompatibility::DeviceIdData& lhs, const BackwardsCompatibility::DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) == 0 && lhs.width == rhs.width && lhs.height == rhs.height && (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL) && lhs.monitorId.compare(rhs.monitorId) == 0;
}
inline bool operator!=(const BackwardsCompatibility::DeviceIdData& lhs, const BackwardsCompatibility::DeviceIdData& rhs)
{
return !(lhs == rhs);
}
inline bool operator<(const BackwardsCompatibility::DeviceIdData& lhs, const BackwardsCompatibility::DeviceIdData& rhs)
{
return lhs.deviceName.compare(rhs.deviceName) < 0 || lhs.width < rhs.width || lhs.height < rhs.height || lhs.monitorId.compare(rhs.monitorId) < 0;
}
}
namespace JSONHelpers
{
namespace CanvasLayoutInfoJSON
@@ -37,21 +67,11 @@ namespace JSONHelpers
std::optional<FancyZonesDataTypes::ZoneSetData> FromJson(const json::JsonObject& zoneSet);
};
struct AppZoneHistoryJSON
{
std::wstring appPath;
std::vector<FancyZonesDataTypes::AppZoneHistoryData> data;
static json::JsonObject ToJson(const AppZoneHistoryJSON& appZoneHistory);
static std::optional<AppZoneHistoryJSON> FromJson(const json::JsonObject& zoneSet);
};
struct DeviceInfoJSON
{
FancyZonesDataTypes::DeviceIdData deviceId;
BackwardsCompatibility::DeviceIdData deviceId;
FancyZonesDataTypes::DeviceInfoData data;
static json::JsonObject ToJson(const DeviceInfoJSON& device);
static std::optional<DeviceInfoJSON> FromJson(const json::JsonObject& device);
};
@@ -64,15 +84,15 @@ namespace JSONHelpers
static std::optional<LayoutQuickKeyJSON> FromJson(const json::JsonObject& device);
};
using TAppZoneHistoryMap = std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>>;
using TDeviceInfoMap = std::unordered_map<FancyZonesDataTypes::DeviceIdData, FancyZonesDataTypes::DeviceInfoData>;
using TDeviceInfoMap = std::unordered_map<BackwardsCompatibility::DeviceIdData, FancyZonesDataTypes::DeviceInfoData>;
using TCustomZoneSetsMap = std::unordered_map<std::wstring, FancyZonesDataTypes::CustomLayoutData>;
using TLayoutQuickKeysMap = std::unordered_map<std::wstring, int>;
struct MonitorInfo
{
std::wstring monitorName;
std::wstring virtualDesktop;
int dpi;
std::wstring id;
int top;
int left;
int width;
@@ -93,10 +113,6 @@ namespace JSONHelpers
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
TAppZoneHistoryMap ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeAppZoneHistory(const TAppZoneHistoryMap& appZoneHistoryMap);
void SaveAppZoneHistory(const std::wstring& appZoneHistoryFileName, const TAppZoneHistoryMap& appZoneHistoryMap);
// replace zones-settings: applied layouts
std::optional<TDeviceInfoMap> ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
void SaveAppliedLayouts(const TDeviceInfoMap& deviceInfoMap);
@@ -113,3 +129,15 @@ namespace JSONHelpers
std::optional<TCustomZoneSetsMap> ParseCustomZoneSets(const json::JsonObject& fancyZonesDataJSON);
void SaveCustomLayouts(const TCustomZoneSetsMap& map);
}
namespace std
{
template<>
struct hash<BackwardsCompatibility::DeviceIdData>
{
size_t operator()(const BackwardsCompatibility::DeviceIdData& Value) const
{
return 0;
}
};
}

View File

@@ -215,10 +215,6 @@ namespace FancyZonesUtils
Rect const monitorRect(mi.rcMonitor);
// Unique identifier format: <parsed-device-id>_<width>_<height>_<virtual-desktop-id>
return TrimDeviceId(deviceId) +
L'_' +
std::to_wstring(monitorRect.width()) +
L'_' +
std::to_wstring(monitorRect.height()) +
L'_' +
virtualDesktopId;
}