[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

@@ -2012,6 +2012,7 @@ tadele
talynone
TApp
TApplication
TApplied
targ
TARGETAPPHEADER
TARGETDIR
@@ -2297,7 +2298,7 @@ WINL
winmd
winmm
WINMSAPP
winnt
WINNT
winres
winrt
winsdk

View File

@@ -18,8 +18,7 @@ FancyZonesApp::FancyZonesApp(const std::wstring& appName, const std::wstring& ap
DPIAware::EnableDPIAwarenessForThisProcess();
m_settings = MakeFancyZonesSettings(reinterpret_cast<HINSTANCE>(&__ImageBase), appName.c_str(), appKey.c_str());
FancyZonesDataInstance().LoadFancyZonesData();
InitializeWinhookEventIds();
m_app = MakeFancyZones(reinterpret_cast<HINSTANCE>(&__ImageBase), m_settings, std::bind(&FancyZonesApp::DisableModule, this));

View File

@@ -12,9 +12,11 @@
#include <common/SettingsAPI/FileWatcher.h>
#include <FancyZonesLib/FancyZonesData.h>
#include <FancyZonesLib/FancyZonesData/AppliedLayouts.h>
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutHotkeys.h>
#include <FancyZonesLib/FancyZonesData/LayoutTemplates.h>
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/FancyZonesWinHookEventIDs.h>
#include <FancyZonesLib/MonitorUtils.h>
@@ -57,9 +59,6 @@ public:
m_windowMoveHandler(settings, [this]() {
PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL);
}),
m_zonesSettingsFileWatcher(FancyZonesDataInstance().GetZonesSettingsFileName(), [this]() {
PostMessageW(m_window, WM_PRIV_FILE_UPDATE, NULL, NULL);
}),
m_settingsFileWatcher(FancyZonesDataInstance().GetSettingsFileName(), [this]() {
PostMessageW(m_window, WM_PRIV_SETTINGS_CHANGED, NULL, NULL);
}),
@@ -73,8 +72,10 @@ public:
this->disableModuleCallback = std::move(disableModuleCallback);
FancyZonesDataInstance().ReplaceZoneSettingsFileFromOlderVersions();
LayoutHotkeys::instance().LoadData();
LayoutTemplates::instance().LoadData();
CustomLayouts::instance().LoadData();
LayoutHotkeys::instance().LoadData();
AppliedLayouts::instance().LoadData();
AppZoneHistory::instance().LoadData();
}
@@ -199,7 +200,6 @@ private:
MonitorWorkAreaHandler m_workAreaHandler;
VirtualDesktop m_virtualDesktop;
FileWatcher m_zonesSettingsFileWatcher;
FileWatcher m_settingsFileWatcher;
winrt::com_ptr<IFancyZonesSettings> m_settings{};
@@ -276,7 +276,7 @@ FancyZones::Run() noexcept
}
});
FancyZonesDataInstance().SetVirtualDesktopCheckCallback(std::bind(&VirtualDesktop::IsVirtualDesktopIdSavedInRegistry, &m_virtualDesktop, std::placeholders::_1));
AppliedLayouts::instance().SetVirtualDesktopCheckCallback(std::bind(&VirtualDesktop::IsVirtualDesktopIdSavedInRegistry, &m_virtualDesktop, std::placeholders::_1));
AppZoneHistory::instance().SetVirtualDesktopCheckCallback(std::bind(&VirtualDesktop::IsVirtualDesktopIdSavedInRegistry, &m_virtualDesktop, std::placeholders::_1));
}
@@ -336,7 +336,7 @@ std::pair<winrt::com_ptr<IWorkArea>, ZoneIndexSet> FancyZones::GetAppZoneHistory
void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr<IWorkArea> workArea, const ZoneIndexSet& zoneIndexSet) noexcept
{
_TRACER_;
auto& fancyZonesData = FancyZonesDataInstance();
if (!AppZoneHistory::instance().IsAnotherWindowOfApplicationInstanceZoned(window, workArea->UniqueId()))
{
if (workArea)
@@ -771,19 +771,23 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
auto hwnd = reinterpret_cast<HWND>(wparam);
WindowCreated(hwnd);
}
else if (message == WM_PRIV_FILE_UPDATE)
{
FancyZonesDataInstance().LoadFancyZonesData();
UpdateZoneSets();
}
else if (message == WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE)
{
LayoutHotkeys::instance().LoadData();
}
else if (message == WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE)
{
LayoutTemplates::instance().LoadData();
}
else if (message == WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE)
{
CustomLayouts::instance().LoadData();
}
else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE)
{
AppliedLayouts::instance().LoadData();
UpdateZoneSets();
}
else if (message == WM_PRIV_QUICK_LAYOUT_KEY)
{
ApplyQuickLayout(static_cast<int>(lparam));
@@ -895,7 +899,7 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const std::wstring& deviceId) noe
if (workArea)
{
m_workAreaHandler.AddWorkArea(m_currentDesktopId, monitor, workArea);
FancyZonesDataInstance().SaveZoneSettings();
AppliedLayouts::instance().SaveData();
}
}
}
@@ -1217,10 +1221,12 @@ void FancyZones::RegisterVirtualDesktopUpdates() noexcept
if (guids.has_value())
{
m_workAreaHandler.RegisterUpdates(*guids);
FancyZonesDataInstance().RemoveDeletedDesktops(*guids);
AppZoneHistory::instance().RemoveDeletedVirtualDesktops(*guids);
AppliedLayouts::instance().RemoveDeletedVirtualDesktops(*guids);
}
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
AppZoneHistory::instance().SyncVirtualDesktops(m_currentDesktopId);
AppliedLayouts::instance().SyncVirtualDesktops(m_currentDesktopId);
}
void FancyZones::UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept
@@ -1269,8 +1275,8 @@ void FancyZones::OnSettingsChanged() noexcept
void FancyZones::OnEditorExitEvent() noexcept
{
// Collect information about changes in zone layout after editor exited.
FancyZonesDataInstance().LoadFancyZonesData();
FancyZonesDataInstance().SyncVirtualDesktops(m_currentDesktopId);
AppZoneHistory::instance().SyncVirtualDesktops(m_currentDesktopId);
AppliedLayouts::instance().SyncVirtualDesktops(m_currentDesktopId);
UpdateZoneSets();
}
@@ -1332,8 +1338,8 @@ void FancyZones::ApplyQuickLayout(int key) noexcept
FancyZonesDataTypes::ZoneSetData data{ .uuid = uuidStr.value(), .type = FancyZonesDataTypes::ZoneSetLayoutType::Custom };
auto workArea = m_workAreaHandler.GetWorkAreaFromCursor(m_currentDesktopId);
FancyZonesDataInstance().SetActiveZoneSet(workArea->UniqueId(), data);
FancyZonesDataInstance().SaveZoneSettings();
AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), data);
AppliedLayouts::instance().SaveData();
UpdateZoneSets();
FlashZones();
}

View File

@@ -1,149 +1,23 @@
#include "pch.h"
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include "JsonHelpers.h"
#include "ZoneSet.h"
#include "Settings.h"
#include "GuidUtils.h"
#include <filesystem>
#include <common/Display/dpi_aware.h>
#include <common/logger/call_tracer.h>
#include <common/utils/json.h>
#include <FancyZonesLib/util.h>
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <shlwapi.h>
#include <filesystem>
#include <fstream>
#include <optional>
#include <regex>
#include <sstream>
#include <unordered_set>
#include <common/utils/process_path.h>
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutHotkeys.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/ModuleConstants.h>
#include <FancyZonesLib/util.h>
// Non-localizable strings
namespace NonLocalizable
{
const wchar_t NullStr[] = L"null";
const wchar_t FancyZonesSettingsFile[] = L"settings.json";
const wchar_t FancyZonesDataFile[] = L"zones-settings.json";
const wchar_t FancyZonesAppZoneHistoryFile[] = L"app-zone-history.json";
const wchar_t FancyZonesEditorParametersFile[] = L"editor-parameters.json";
const wchar_t RegistryPath[] = L"Software\\SuperFancyZones";
}
namespace
{
std::wstring ExtractVirtualDesktopId(const std::wstring& deviceId)
{
// Format: <device-id>_<resolution>_<virtual-desktop-id>
return deviceId.substr(deviceId.rfind('_') + 1);
}
const std::wstring& GetTempDirPath()
{
static std::wstring tmpDirPath;
static std::once_flag flag;
std::call_once(flag, []() {
wchar_t buffer[MAX_PATH];
auto charsWritten = GetTempPath(MAX_PATH, buffer);
if (charsWritten > MAX_PATH || (charsWritten == 0))
{
abort();
}
tmpDirPath = std::wstring{ buffer };
});
return tmpDirPath;
}
bool DeleteRegistryKey(HKEY hKeyRoot, LPTSTR lpSubKey)
{
// First, see if we can delete the key without having to recurse.
if (ERROR_SUCCESS == RegDeleteKey(hKeyRoot, lpSubKey))
{
return true;
}
HKEY hKey;
if (ERROR_SUCCESS != RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey))
{
return false;
}
// Check for an ending slash and add one if it is missing.
LPTSTR lpEnd = lpSubKey + lstrlen(lpSubKey);
if (*(lpEnd - 1) != TEXT('\\'))
{
*lpEnd = TEXT('\\');
lpEnd++;
*lpEnd = TEXT('\0');
}
// Enumerate the keys
DWORD dwSize = MAX_PATH;
TCHAR szName[MAX_PATH];
FILETIME ftWrite;
auto result = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
if (result == ERROR_SUCCESS)
{
do
{
*lpEnd = TEXT('\0');
StringCchCat(lpSubKey, MAX_PATH * 2, szName);
if (!DeleteRegistryKey(hKeyRoot, lpSubKey))
{
break;
}
dwSize = MAX_PATH;
result = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
} while (result == ERROR_SUCCESS);
}
lpEnd--;
*lpEnd = TEXT('\0');
RegCloseKey(hKey);
// Try again to delete the root key.
if (ERROR_SUCCESS == RegDeleteKey(hKeyRoot, lpSubKey))
{
return true;
}
return false;
}
bool DeleteFancyZonesRegistryData()
{
wchar_t key[256];
StringCchPrintf(key, ARRAYSIZE(key), L"%s", NonLocalizable::RegistryPath);
HKEY hKey;
if (ERROR_FILE_NOT_FOUND == RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_READ, &hKey))
{
return true;
}
else
{
return DeleteRegistryKey(HKEY_CURRENT_USER, key);
}
}
}
FancyZonesData& FancyZonesDataInstance()
@@ -157,8 +31,8 @@ FancyZonesData::FancyZonesData()
std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
settingsFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesSettingsFile);
zonesSettingsFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesDataFile);
appZoneHistoryFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesAppZoneHistoryFile);
zonesSettingsFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesDataFile);
editorParametersFileName = saveFolderPath + L"\\" + std::wstring(NonLocalizable::FancyZonesEditorParametersFile);
}
@@ -166,9 +40,15 @@ void FancyZonesData::ReplaceZoneSettingsFileFromOlderVersions()
{
if (std::filesystem::exists(zonesSettingsFileName))
{
json::JsonObject fancyZonesDataJSON = GetPersistFancyZonesJSON();
Logger::info("Replace zones-settings file");
//deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
json::JsonObject fancyZonesDataJSON = JSONHelpers::GetPersistFancyZonesJSON(zonesSettingsFileName, appZoneHistoryFileName);
auto deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
if (deviceInfoMap)
{
JSONHelpers::SaveAppliedLayouts(deviceInfoMap.value());
}
auto customLayouts = JSONHelpers::ParseCustomZoneSets(fancyZonesDataJSON);
if (customLayouts)
@@ -187,295 +67,8 @@ void FancyZonesData::ReplaceZoneSettingsFileFromOlderVersions()
{
JSONHelpers::SaveLayoutHotkeys(quickKeysMap.value());
}
}
//TODO: remove zone-settings.json after getting all info from it
}
void FancyZonesData::SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback)
{
m_virtualDesktopCheckCallback = callback;
}
const JSONHelpers::TDeviceInfoMap& FancyZonesData::GetDeviceInfoMap() const
{
std::scoped_lock lock{ dataLock };
return deviceInfoMap;
}
std::optional<FancyZonesDataTypes::DeviceInfoData> FancyZonesData::FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& id) const
{
std::scoped_lock lock{ dataLock };
for (const auto& [deviceId, deviceInfo] : deviceInfoMap)
{
if (id.isEqualWithNullVirtualDesktopId(deviceId))
{
return deviceInfo;
}
}
return std::nullopt;
}
bool FancyZonesData::AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId)
{
_TRACER_;
using namespace FancyZonesDataTypes;
auto deviceInfo = FindDeviceInfo(deviceId);
std::scoped_lock lock{ dataLock };
if (!deviceInfo.has_value())
{
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(deviceId.virtualDesktopId, &virtualDesktopId)))
{
Logger::info(L"Create new device on virtual desktop {}", virtualDesktopId.get());
}
// Creates default entry in map when WorkArea is created
GUID guid;
auto result{ CoCreateGuid(&guid) };
wil::unique_cotaskmem_string guidString;
if (result == S_OK && SUCCEEDED(StringFromCLSID(guid, &guidString)))
{
const ZoneSetData zoneSetData{ guidString.get(), ZoneSetLayoutType::PriorityGrid };
DeviceInfoData defaultDeviceInfoData{ zoneSetData, DefaultValues::ShowSpacing, DefaultValues::Spacing, DefaultValues::ZoneCount, DefaultValues::SensitivityRadius };
deviceInfoMap[deviceId] = std::move(defaultDeviceInfoData);
return true;
}
else
{
Logger::error("Failed to create an ID for the new layout");
}
}
return false;
}
void FancyZonesData::CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& source, const FancyZonesDataTypes::DeviceIdData& destination)
{
if (source == destination)
{
return;
}
std::scoped_lock lock{ dataLock };
// The source virtual desktop is deleted, simply ignore it.
if (!FindDeviceInfo(source).has_value())
{
return;
}
deviceInfoMap[destination] = deviceInfoMap[source];
}
void FancyZonesData::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).
// This method will go through all our persisted data with default GUID and update it with
// valid one.
std::scoped_lock lock{ dataLock };
bool dirtyFlag = false;
for (auto& [path, perDesktopData] : appZoneHistoryMap)
{
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;
}
}
}
}
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithCurrentId{};
std::vector<FancyZonesDataTypes::DeviceIdData> replaceWithNullId{};
for (const auto& [desktopId, data] : deviceInfoMap)
{
if (desktopId.virtualDesktopId == GUID_NULL)
{
replaceWithCurrentId.push_back(desktopId);
dirtyFlag = true;
}
else
{
if (m_virtualDesktopCheckCallback && !m_virtualDesktopCheckCallback(desktopId.virtualDesktopId))
{
replaceWithNullId.push_back(desktopId);
dirtyFlag = true;
}
}
}
for (const auto& id : replaceWithCurrentId)
{
auto mapEntry = deviceInfoMap.extract(id);
mapEntry.key().virtualDesktopId = currentVirtualDesktopId;
deviceInfoMap.insert(std::move(mapEntry));
}
for (const auto& id : replaceWithNullId)
{
auto mapEntry = deviceInfoMap.extract(id);
mapEntry.key().virtualDesktopId = GUID_NULL;
deviceInfoMap.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());
}
SaveZoneSettings();
AppZoneHistory::instance().SaveData();
}
}
void FancyZonesData::RemoveDeletedDesktops(const std::vector<GUID>& activeDesktops)
{
std::unordered_set<GUID> active(std::begin(activeDesktops), std::end(activeDesktops));
std::scoped_lock lock{ dataLock };
bool dirtyFlag = false;
for (auto it = std::begin(deviceInfoMap); it != std::end(deviceInfoMap);)
{
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());
}
AppZoneHistory::instance().RemoveDesktopAppZoneHistory(desktopId);
it = deviceInfoMap.erase(it);
dirtyFlag = true;
continue;
}
}
++it;
}
if (dirtyFlag)
{
SaveZoneSettings();
AppZoneHistory::instance().SaveData();
}
}
void FancyZonesData::SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& data)
{
std::scoped_lock lock{ dataLock };
for (auto& [deviceIdData, deviceInfo] : deviceInfoMap)
{
if (deviceId.isEqualWithNullVirtualDesktopId(deviceIdData))
{
deviceInfo.activeZoneSet = data;
// If the zone set is custom, we need to copy its properties to the device
auto id = FancyZonesUtils::GuidFromString(data.uuid);
if (id.has_value())
{
auto layout = CustomLayouts::instance().GetLayout(id.value());
if (layout)
{
if (layout.value().type == FancyZonesDataTypes::CustomLayoutType::Grid)
{
auto layoutInfo = std::get<FancyZonesDataTypes::GridLayoutInfo>(layout.value().info);
deviceInfo.sensitivityRadius = layoutInfo.sensitivityRadius();
deviceInfo.showSpacing = layoutInfo.showSpacing();
deviceInfo.spacing = layoutInfo.spacing();
deviceInfo.zoneCount = layoutInfo.zoneCount();
}
else if (layout.value().type == FancyZonesDataTypes::CustomLayoutType::Canvas)
{
auto layoutInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(layout.value().info);
deviceInfo.sensitivityRadius = layoutInfo.sensitivityRadius;
deviceInfo.zoneCount = (int)layoutInfo.zones.size();
}
}
}
break;
}
}
}
json::JsonObject FancyZonesData::GetPersistFancyZonesJSON()
{
return JSONHelpers::GetPersistFancyZonesJSON(zonesSettingsFileName, appZoneHistoryFileName);
}
void FancyZonesData::LoadFancyZonesData()
{
if (!std::filesystem::exists(zonesSettingsFileName))
{
SaveZoneSettings();
AppZoneHistory::instance().SaveData();
}
else
{
json::JsonObject fancyZonesDataJSON = GetPersistFancyZonesJSON();
deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
}
}
void FancyZonesData::SaveZoneSettings() const
{
_TRACER_;
std::scoped_lock lock{ dataLock };
bool dirtyFlag = false;
JSONHelpers::TDeviceInfoMap updatedDeviceInfoMap;
if (m_virtualDesktopCheckCallback)
{
for (const auto& [id, data] : deviceInfoMap)
{
auto updatedId = id;
if (!m_virtualDesktopCheckCallback(id.virtualDesktopId))
{
updatedId.virtualDesktopId = GUID_NULL;
dirtyFlag = true;
}
updatedDeviceInfoMap.insert({ updatedId, data });
}
}
if (dirtyFlag)
{
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, updatedDeviceInfoMap);
}
else
{
JSONHelpers::SaveZoneSettings(zonesSettingsFileName, deviceInfoMap);
std::filesystem::remove(zonesSettingsFileName);
}
}

View File

@@ -1,38 +1,16 @@
#pragma once
#include "JsonHelpers.h"
#if defined(UNIT_TESTS)
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/json.h>
#include <mutex>
#include <string>
#include <unordered_map>
#include <optional>
#include <vector>
#include <winnt.h>
#include <FancyZonesLib/JsonHelpers.h>
namespace FancyZonesDataTypes
{
struct ZoneSetData;
struct DeviceIdData;
struct DeviceInfoData;
struct CustomLayoutData;
struct AppZoneHistoryData;
}
#endif
#if defined(UNIT_TESTS)
namespace FancyZonesUnitTests
{
class FancyZonesDataUnitTests;
class FancyZonesIFancyZonesCallbackUnitTests;
class ZoneSetCalculateZonesUnitTests;
class WorkAreaUnitTests;
class WorkAreaCreationUnitTests;
class LayoutHotkeysUnitTests;
class LayoutTemplatesUnitTests;
class CustomLayoutsUnitTests;
class AppliedLayoutsUnitTests;
}
#endif
@@ -43,63 +21,19 @@ public:
void ReplaceZoneSettingsFileFromOlderVersions();
void SetVirtualDesktopCheckCallback(std::function<bool(GUID)> callback);
std::optional<FancyZonesDataTypes::DeviceInfoData> FindDeviceInfo(const FancyZonesDataTypes::DeviceIdData& id) const;
const JSONHelpers::TDeviceInfoMap& GetDeviceInfoMap() const;
inline const std::wstring& GetZonesSettingsFileName() const
{
return zonesSettingsFileName;
}
inline const std::wstring& GetSettingsFileName() const
{
return settingsFileName;
}
bool AddDevice(const FancyZonesDataTypes::DeviceIdData& deviceId);
void CloneDeviceInfo(const FancyZonesDataTypes::DeviceIdData& source, const FancyZonesDataTypes::DeviceIdData& destination);
void SyncVirtualDesktops(GUID desktopId);
void RemoveDeletedDesktops(const std::vector<GUID>& activeDesktops);
void SetActiveZoneSet(const FancyZonesDataTypes::DeviceIdData& deviceId, const FancyZonesDataTypes::ZoneSetData& zoneSet);
json::JsonObject GetPersistFancyZonesJSON();
void LoadFancyZonesData();
void SaveZoneSettings() const;
void SaveFancyZonesEditorParameters(bool spanZonesAcrossMonitors, const std::wstring& virtualDesktopId, const HMONITOR& targetMonitor, const std::vector<std::pair<HMONITOR, MONITORINFOEX>>& allMonitors) const;
private:
#if defined(UNIT_TESTS)
friend class FancyZonesUnitTests::FancyZonesDataUnitTests;
friend class FancyZonesUnitTests::FancyZonesIFancyZonesCallbackUnitTests;
friend class FancyZonesUnitTests::WorkAreaUnitTests;
friend class FancyZonesUnitTests::WorkAreaCreationUnitTests;
friend class FancyZonesUnitTests::ZoneSetCalculateZonesUnitTests;
friend class FancyZonesUnitTests::LayoutHotkeysUnitTests;
friend class FancyZonesUnitTests::LayoutTemplatesUnitTests;
friend class FancyZonesUnitTests::CustomLayoutsUnitTests;
inline void SetDeviceInfo(const FancyZonesDataTypes::DeviceIdData& deviceId, FancyZonesDataTypes::DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
inline bool ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
{
deviceInfoMap = JSONHelpers::ParseDeviceInfos(fancyZonesDataJSON);
return !deviceInfoMap.empty();
}
inline void clear_data()
{
appZoneHistoryMap.clear();
deviceInfoMap.clear();
}
friend class FancyZonesUnitTests::AppliedLayoutsUnitTests;
inline void SetSettingsModulePath(std::wstring_view moduleName)
{
@@ -114,27 +48,10 @@ private:
return result + L"\\" + std::wstring(L"zones-settings.json");
}
#endif
// Maps app path to app's zone history data
std::unordered_map<std::wstring, std::vector<FancyZonesDataTypes::AppZoneHistoryData>> appZoneHistoryMap{};
// Maps device unique ID to device data
JSONHelpers::TDeviceInfoMap deviceInfoMap{};
std::wstring settingsFileName;
std::wstring zonesSettingsFileName;
std::wstring appZoneHistoryFileName;
std::wstring editorParametersFileName;
std::function<bool(GUID)> m_virtualDesktopCheckCallback;
mutable std::recursive_mutex dataLock;
};
FancyZonesData& FancyZonesDataInstance();
namespace DefaultValues
{
const int ZoneCount = 3;
const bool ShowSpacing = true;
const int Spacing = 16;
const int SensitivityRadius = 20;
}
FancyZonesData& FancyZonesDataInstance();

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;
};

View File

@@ -305,9 +305,4 @@ namespace FancyZonesDataTypes
return result;
}
bool DeviceIdData::isEqualWithNullVirtualDesktopId(const DeviceIdData& other) const
{
return deviceName.compare(other.deviceName) == 0 && width == other.width && height == other.height && (virtualDesktopId == other.virtualDesktopId || virtualDesktopId == GUID_NULL || other.virtualDesktopId == GUID_NULL) && monitorId.compare(other.monitorId) == 0;
}
}

View File

@@ -125,7 +125,6 @@ namespace FancyZonesDataTypes
static bool IsValidDeviceId(const std::wstring& str);
std::wstring toString() const;
bool isEqualWithNullVirtualDesktopId(const DeviceIdData& other) const;
};
struct AppZoneHistoryData
@@ -153,7 +152,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.monitorId.compare(rhs.monitorId) == 0;
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 DeviceIdData& lhs, const DeviceIdData& rhs)

View File

@@ -38,9 +38,12 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="FancyZonesData\CustomLayouts.h" />
<ClInclude Include="FancyZonesData\AppliedLayouts.h" />
<ClInclude Include="FancyZonesData\AppZoneHistory.h" />
<ClInclude Include="FancyZones.h" />
<ClInclude Include="FancyZonesDataTypes.h" />
<ClInclude Include="FancyZonesData\Layout.h" />
<ClInclude Include="FancyZonesData\LayoutDefaults.h" />
<ClInclude Include="FancyZonesData\LayoutTemplates.h" />
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
<ClInclude Include="GenericKeyHook.h" />
@@ -79,6 +82,14 @@
</ClCompile>
<ClCompile Include="FancyZones.cpp" />
<ClCompile Include="FancyZonesDataTypes.cpp" />
<ClCompile Include="FancyZonesData\AppliedLayouts.cpp">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
<ClCompile Include="FancyZonesData.cpp" />
<ClCompile Include="JsonHelpers.cpp" />

View File

@@ -93,6 +93,9 @@
<ClInclude Include="FancyZonesData\AppZoneHistory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\AppliedLayouts.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\CustomLayouts.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -105,6 +108,12 @@
<ClInclude Include="FancyZonesData\LayoutTemplates.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\LayoutDefaults.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FancyZonesData\Layout.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -173,6 +182,12 @@
<ClCompile Include="FancyZonesData\AppZoneHistory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\AppliedLayouts.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FancyZonesData\LayoutTemplates.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -11,9 +11,10 @@ UINT WM_PRIV_VD_INIT;
UINT WM_PRIV_VD_SWITCH;
UINT WM_PRIV_VD_UPDATE;
UINT WM_PRIV_EDITOR;
UINT WM_PRIV_FILE_UPDATE;
UINT WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE;
UINT WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE;
UINT WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE;
UINT WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE;
UINT WM_PRIV_SNAP_HOTKEY;
UINT WM_PRIV_QUICK_LAYOUT_KEY;
UINT WM_PRIV_SETTINGS_CHANGED;
@@ -32,9 +33,10 @@ void InitializeWinhookEventIds()
WM_PRIV_VD_SWITCH = RegisterWindowMessage(L"{128c2cb0-6bdf-493e-abbe-f8705e04aa95}");
WM_PRIV_VD_UPDATE = RegisterWindowMessage(L"{b8b72b46-f42f-4c26-9e20-29336cf2f22e}");
WM_PRIV_EDITOR = RegisterWindowMessage(L"{87543824-7080-4e91-9d9c-0404642fc7b6}");
WM_PRIV_FILE_UPDATE = RegisterWindowMessage(L"{632f17a9-55a7-45f1-a4db-162e39271d92}");
WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE = RegisterWindowMessage(L"{07229b7e-4f22-4357-b136-33c289be2295}");
WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE = RegisterWindowMessage(L"{4686f019-5d3d-4c5c-9051-b7cbbccca77d}");
WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE = RegisterWindowMessage(L"{0972787e-cdab-4e16-b228-91acdc38f40f}");
WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE = RegisterWindowMessage(L"{2ef2c8a7-e0d5-4f31-9ede-52aade2d284d}");
WM_PRIV_SNAP_HOTKEY = RegisterWindowMessage(L"{72f4fd8e-23f1-43ab-bbbc-029363df9a84}");
WM_PRIV_QUICK_LAYOUT_KEY = RegisterWindowMessage(L"{15baab3d-c67b-4a15-aFF0-13610e05e947}");
WM_PRIV_SETTINGS_CHANGED = RegisterWindowMessage(L"{89ca3Daa-bf2d-4e73-9f3f-c60716364e27}");

View File

@@ -9,9 +9,10 @@ extern UINT WM_PRIV_VD_INIT; // Scheduled when FancyZones is initialized
extern UINT WM_PRIV_VD_SWITCH; // Scheduled when virtual desktop switch occurs
extern UINT WM_PRIV_VD_UPDATE; // Scheduled on virtual desktops update (creation/deletion)
extern UINT WM_PRIV_EDITOR; // Scheduled when the editor exits
extern UINT WM_PRIV_FILE_UPDATE; // Scheduled when the watched zone-settings file is updated
extern UINT WM_PRIV_LAYOUT_HOTKEYS_FILE_UPDATE; // Scheduled when the watched layout-hotkeys.json file is updated
extern UINT WM_PRIV_LAYOUT_TEMPLATES_FILE_UPDATE; // Scheduled when the watched layout-templates.json file is updated
extern UINT WM_PRIV_CUSTOM_LAYOUTS_FILE_UPDATE; // Scheduled when the watched custom-layouts.json file is updated
extern UINT WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE; // Scheduled when the watched applied-layouts.json file is updated
extern UINT WM_PRIV_SNAP_HOTKEY; // Scheduled when we receive a snap hotkey key down press
extern UINT WM_PRIV_QUICK_LAYOUT_KEY; // Scheduled when we receive a key down press to quickly apply a layout
extern UINT WM_PRIV_SETTINGS_CHANGED; // Scheduled when the a watched settings file is updated

View File

@@ -1,12 +1,13 @@
#include "pch.h"
#include "JsonHelpers.h"
#include "FancyZonesData.h"
#include "FancyZonesDataTypes.h"
#include "trace.h"
#include "util.h"
#include <FancyZonesLib/FancyZonesData/AppliedLayouts.h>
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesData/LayoutHotkeys.h>
#include <FancyZonesLib/FancyZonesData/LayoutTemplates.h>
@@ -580,21 +581,6 @@ namespace JSONHelpers
}
}
void SaveZoneSettings(const std::wstring& zonesSettingsFileName, const TDeviceInfoMap& deviceInfoMap)
{
auto before = json::from_file(zonesSettingsFileName);
json::JsonObject root{};
root.SetNamedValue(NonLocalizable::DevicesStr, JSONHelpers::SerializeDeviceInfos(deviceInfoMap));
if (!before.has_value() || before.value().Stringify() != root.Stringify())
{
Trace::FancyZones::DataChanged();
json::to_file(zonesSettingsFileName, root);
}
}
void SaveAppZoneHistory(const std::wstring& appZoneHistoryFileName, const TAppZoneHistoryMap& appZoneHistoryMap)
{
json::JsonObject root{};
@@ -644,7 +630,7 @@ namespace JSONHelpers
return appHistoryArray;
}
TDeviceInfoMap ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
std::optional<TDeviceInfoMap> ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON)
{
try
{
@@ -661,22 +647,37 @@ namespace JSONHelpers
return std::move(deviceInfoMap);
}
catch (const winrt::hresult_error&)
catch (const winrt::hresult_error& e)
{
return {};
Logger::error(L"Parsing device info error: {}", e.message());
return std::nullopt;
}
}
json::JsonArray SerializeDeviceInfos(const TDeviceInfoMap& deviceInfoMap)
void SaveAppliedLayouts(const TDeviceInfoMap& deviceInfoMap)
{
json::JsonArray DeviceInfosJSON{};
json::JsonObject root{};
json::JsonArray layoutsArray{};
for (const auto& [deviceID, deviceData] : deviceInfoMap)
for (const auto& [deviceID, data] : deviceInfoMap)
{
DeviceInfosJSON.Append(DeviceInfoJSON::DeviceInfoJSON::ToJson(DeviceInfoJSON{ deviceID, deviceData }));
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(data.activeZoneSet.uuid));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(data.activeZoneSet.type)));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(data.showSpacing));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(data.spacing));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(data.zoneCount));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(data.sensitivityRadius));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(deviceID.toString()));
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
layoutsArray.Append(obj);
}
return DeviceInfosJSON;
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
}
json::JsonArray SerializeCustomZoneSets(const TCustomZoneSetsMap& customZoneSetsMap)

View File

@@ -93,14 +93,13 @@ namespace JSONHelpers
json::JsonObject GetPersistFancyZonesJSON(const std::wstring& zonesSettingsFileName, const std::wstring& appZoneHistoryFileName);
void SaveZoneSettings(const std::wstring& zonesSettingsFileName, const TDeviceInfoMap& deviceInfoMap);
void SaveAppZoneHistory(const std::wstring& appZoneHistoryFileName, const TAppZoneHistoryMap& appZoneHistoryMap);
TAppZoneHistoryMap ParseAppZoneHistory(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeAppZoneHistory(const TAppZoneHistoryMap& appZoneHistoryMap);
void SaveAppZoneHistory(const std::wstring& appZoneHistoryFileName, const TAppZoneHistoryMap& appZoneHistoryMap);
TDeviceInfoMap ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
json::JsonArray SerializeDeviceInfos(const TDeviceInfoMap& deviceInfoMap);
// replace zones-settings: applied layouts
std::optional<TDeviceInfoMap> ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON);
void SaveAppliedLayouts(const TDeviceInfoMap& deviceInfoMap);
// replace zones-settings: layout hotkeys
std::optional<TLayoutQuickKeysMap> ParseQuickKeys(const json::JsonObject& fancyZonesDataJSON);

View File

@@ -4,7 +4,7 @@
#include <common/logger/call_tracer.h>
#include <common/logger/logger.h>
#include "FancyZonesData.h"
#include "FancyZonesData/AppliedLayouts.h"
#include "FancyZonesData/AppZoneHistory.h"
#include "FancyZonesDataTypes.h"
#include "ZonesOverlay.h"
@@ -486,73 +486,65 @@ void WorkArea::InitializeZoneSets(const FancyZonesDataTypes::DeviceIdData& paren
wil::unique_cotaskmem_string virtualDesktopId;
if (SUCCEEDED(StringFromCLSID(m_uniqueId.virtualDesktopId, &virtualDesktopId)))
{
Logger::debug(L"Initialize zone sets on the virtual desktop {}", virtualDesktopId.get());
Logger::debug(L"Initialize layout on the virtual desktop {}", virtualDesktopId.get());
}
bool deviceAdded = FancyZonesDataInstance().AddDevice(m_uniqueId);
// If the device has been added, check if it should inherit the parent's layout
if (deviceAdded && parentUniqueId.virtualDesktopId != GUID_NULL)
bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId);
if (!isLayoutAlreadyApplied)
{
FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
if (parentUniqueId.virtualDesktopId != GUID_NULL)
{
AppliedLayouts::instance().CloneLayout(parentUniqueId, m_uniqueId);
}
else
{
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
}
}
CalculateZoneSet(m_overlappingAlgorithm);
}
void WorkArea::CalculateZoneSet(OverlappingZonesAlgorithm overlappingAlgorithm) noexcept
{
const auto& fancyZonesData = FancyZonesDataInstance();
const auto deviceInfoData = fancyZonesData.FindDeviceInfo(m_uniqueId);
if (!deviceInfoData.has_value())
const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId);
if (!appliedLayout.has_value())
{
return;
}
const auto& activeZoneSet = deviceInfoData->activeZoneSet;
auto zoneSet = MakeZoneSet(ZoneSetConfig(
appliedLayout->uuid,
appliedLayout->type,
m_monitor,
appliedLayout->sensitivityRadius,
overlappingAlgorithm));
if (activeZoneSet.uuid.empty())
RECT workArea;
if (m_monitor)
{
return;
}
GUID zoneSetId;
if (SUCCEEDED_LOG(CLSIDFromString(activeZoneSet.uuid.c_str(), &zoneSetId)))
{
int sensitivityRadius = deviceInfoData->sensitivityRadius;
auto zoneSet = MakeZoneSet(ZoneSetConfig(
zoneSetId,
activeZoneSet.type,
m_monitor,
sensitivityRadius,
overlappingAlgorithm));
RECT workArea;
if (m_monitor)
MONITORINFO monitorInfo{};
monitorInfo.cbSize = sizeof(monitorInfo);
if (GetMonitorInfoW(m_monitor, &monitorInfo))
{
MONITORINFO monitorInfo{};
monitorInfo.cbSize = sizeof(monitorInfo);
if (GetMonitorInfoW(m_monitor, &monitorInfo))
{
workArea = monitorInfo.rcWork;
}
else
{
return;
}
workArea = monitorInfo.rcWork;
}
else
{
workArea = GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>();
return;
}
bool showSpacing = deviceInfoData->showSpacing;
int spacing = showSpacing ? deviceInfoData->spacing : 0;
int zoneCount = deviceInfoData->zoneCount;
zoneSet->CalculateZones(workArea, zoneCount, spacing);
UpdateActiveZoneSet(zoneSet.get());
}
else
{
workArea = GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>();
}
bool showSpacing = appliedLayout->showSpacing;
int spacing = showSpacing ? appliedLayout->spacing : 0;
int zoneCount = appliedLayout->zoneCount;
zoneSet->CalculateZones(workArea, zoneCount, spacing);
UpdateActiveZoneSet(zoneSet.get());
}
void WorkArea::UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept
@@ -568,7 +560,8 @@ void WorkArea::UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept
.uuid = zoneSetId.get(),
.type = m_zoneSet->LayoutType()
};
FancyZonesDataInstance().SetActiveZoneSet(m_uniqueId, data);
AppliedLayouts::instance().ApplyLayout(m_uniqueId, data);
}
}
}

View File

@@ -2,7 +2,6 @@
#include "ZoneSet.h"
#include "FancyZonesData.h"
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include "FancyZonesDataTypes.h"
#include "FancyZonesWindowProperties.h"

View File

@@ -2,8 +2,8 @@
#include "trace.h"
#include "FancyZonesLib/ZoneSet.h"
#include "FancyZonesLib/Settings.h"
#include "FancyZonesLib/FancyZonesData.h"
#include "FancyZonesData/AppZoneHistory.h"
#include "FancyZonesLib/FancyZonesData/AppliedLayouts.h"
#include "FancyZonesLib/FancyZonesData/CustomLayouts.h"
#include "FancyZonesLib/FancyZonesData/LayoutHotkeys.h"
#include "FancyZonesLib/FancyZonesDataTypes.h"
@@ -145,10 +145,9 @@ void Trace::FancyZones::OnKeyDown(DWORD vkCode, bool win, bool control, bool inM
void Trace::FancyZones::DataChanged() noexcept
{
const FancyZonesData& data = FancyZonesDataInstance();
int appsHistorySize = static_cast<int>(AppZoneHistory::instance().GetFullAppZoneHistory().size());
const auto& customZones = CustomLayouts::instance().GetAllLayouts();
const auto& devices = data.GetDeviceInfoMap();
const auto& layouts = AppliedLayouts::instance().GetAppliedLayoutMap();
auto quickKeysCount = LayoutHotkeys::instance().GetHotkeysCount();
std::unique_ptr<INT32[]> customZonesArray(new (std::nothrow) INT32[customZones.size()]);
@@ -157,7 +156,7 @@ void Trace::FancyZones::DataChanged() noexcept
return;
}
auto getCustomZoneCount = [&data](const std::variant<FancyZonesDataTypes::CanvasLayoutInfo, FancyZonesDataTypes::GridLayoutInfo>& layoutInfo) -> int {
auto getCustomZoneCount = [](const std::variant<FancyZonesDataTypes::CanvasLayoutInfo, FancyZonesDataTypes::GridLayoutInfo>& layoutInfo) -> int {
if (std::holds_alternative<FancyZonesDataTypes::GridLayoutInfo>(layoutInfo))
{
const auto& info = std::get<FancyZonesDataTypes::GridLayoutInfo>(layoutInfo);
@@ -181,9 +180,9 @@ void Trace::FancyZones::DataChanged() noexcept
// ActiveZoneSetsList
std::wstring activeZoneSetInfo;
for (const auto& [id, device] : devices)
for (const auto& [id, layout] : layouts)
{
const FancyZonesDataTypes::ZoneSetLayoutType type = device.activeZoneSet.type;
const FancyZonesDataTypes::ZoneSetLayoutType type = layout.type;
if (!activeZoneSetInfo.empty())
{
activeZoneSetInfo += L"; ";
@@ -193,19 +192,16 @@ void Trace::FancyZones::DataChanged() noexcept
int zoneCount = -1;
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Custom)
{
auto guid = FancyZonesUtils::GuidFromString(device.activeZoneSet.uuid);
if (guid)
auto guid = layout.uuid;
const auto& activeCustomZone = customZones.find(guid);
if (activeCustomZone != customZones.end())
{
const auto& activeCustomZone = customZones.find(guid.value());
if (activeCustomZone != customZones.end())
{
zoneCount = getCustomZoneCount(activeCustomZone->second.info);
}
}
zoneCount = getCustomZoneCount(activeCustomZone->second.info);
}
}
else
{
zoneCount = device.zoneCount;
zoneCount = layout.zoneCount;
}
if (zoneCount != -1)
@@ -226,7 +222,7 @@ void Trace::FancyZones::DataChanged() noexcept
TraceLoggingInt32(appsHistorySize, AppsInHistoryCountKey),
TraceLoggingInt32(static_cast<int>(customZones.size()), CustomZoneSetCountKey),
TraceLoggingInt32Array(customZonesArray.get(), static_cast<int>(customZones.size()), NumberOfZonesForEachCustomZoneSetKey),
TraceLoggingInt32(static_cast<int>(devices.size()), ActiveZoneSetsCountKey),
TraceLoggingInt32(static_cast<int>(layouts.size()), ActiveZoneSetsCountKey),
TraceLoggingWideString(activeZoneSetInfo.c_str(), ActiveZoneSetsListKey),
TraceLoggingInt32(static_cast<int>(quickKeysCount), LayoutUsingQuickKeyCountKey));
}

View File

@@ -0,0 +1,374 @@
#include "pch.h"
#include <filesystem>
#include <FancyZonesLib/FancyZonesData.h>
#include <FancyZonesLib/FancyZonesData/AppliedLayouts.h>
#include <FancyZonesLib/util.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace FancyZonesUnitTests
{
TEST_CLASS (AppliedLayoutsUnitTests)
{
FancyZonesData& m_fzData = FancyZonesDataInstance();
std::wstring m_testFolder = L"FancyZonesUnitTests";
TEST_METHOD_INITIALIZE(Init)
{
m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests");
}
TEST_METHOD_CLEANUP(CleanUp)
{
std::filesystem::remove_all(AppliedLayouts::AppliedLayoutsFileName());
std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_testFolder));
}
TEST_METHOD (AppliedLayoutsParse)
{
// prepare
json::JsonObject root{};
json::JsonArray layoutsArray{};
{
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Rows)));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(true));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(3));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(4));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(22));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(L"DELA026#5&10a58c63&0&UID16777488_2194_1234_{61FA9FC0-26A6-4B37-A834-491C148DFC57}"));
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
layoutsArray.Append(obj);
}
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
// test
AppliedLayouts::instance().LoadData();
Assert::AreEqual((size_t)1, AppliedLayouts::instance().GetAppliedLayoutMap().size());
FancyZonesDataTypes::DeviceIdData id{
.deviceName = L"DELA026#5&10a58c63&0&UID16777488",
.width = 2194,
.height = 1234,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value()
};
Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(id).has_value());
}
TEST_METHOD (AppliedLayoutsParseEmpty)
{
// prepare
json::JsonObject root{};
json::JsonArray layoutsArray{};
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
// test
AppliedLayouts::instance().LoadData();
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
}
TEST_METHOD (AppliedLayoutsNoFile)
{
// test
AppliedLayouts::instance().LoadData();
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
}
TEST_METHOD (MoveAppliedLayoutsFromZonesSettings)
{
// prepare
json::JsonObject root{};
json::JsonArray devicesArray{}, customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{};
{
json::JsonObject activeZoneset{};
activeZoneset.SetNamedValue(L"uuid", json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
activeZoneset.SetNamedValue(L"type", json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Rows)));
json::JsonObject obj{};
obj.SetNamedValue(L"device-id", json::value(L"VSC9636#5&37ac4db&0&UID160005_3840_2160_{00000000-0000-0000-0000-000000000000}"));
obj.SetNamedValue(L"active-zoneset", activeZoneset);;
obj.SetNamedValue(L"editor-show-spacing", json::value(true));
obj.SetNamedValue(L"editor-spacing", json::value(3));
obj.SetNamedValue(L"editor-zone-count", json::value(4));
obj.SetNamedValue(L"editor-sensitivity-radius", json::value(22));
devicesArray.Append(obj);
}
root.SetNamedValue(L"devices", devicesArray);
root.SetNamedValue(L"custom-zone-sets", customLayoutsArray);
root.SetNamedValue(L"templates", templateLayoutsArray);
root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray);
json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root);
// test
m_fzData.ReplaceZoneSettingsFileFromOlderVersions();
AppliedLayouts::instance().LoadData();
Assert::AreEqual((size_t)1, AppliedLayouts::instance().GetAppliedLayoutMap().size());
FancyZonesDataTypes::DeviceIdData id{
.deviceName = L"VSC9636#5&37ac4db&0&UID160005",
.width = 3840,
.height = 2160,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(id).has_value());
}
TEST_METHOD (MoveAppliedLayoutsFromZonesSettingsNoAppliedLayoutsData)
{
// prepare
json::JsonObject root{};
json::JsonArray customLayoutsArray{}, templateLayoutsArray{}, quickLayoutKeysArray{};
root.SetNamedValue(L"custom-zone-sets", customLayoutsArray);
root.SetNamedValue(L"templates", templateLayoutsArray);
root.SetNamedValue(L"quick-layout-keys", quickLayoutKeysArray);
json::to_file(m_fzData.GetZoneSettingsPath(m_testFolder), root);
// test
m_fzData.ReplaceZoneSettingsFileFromOlderVersions();
AppliedLayouts::instance().LoadData();
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
}
TEST_METHOD (MoveAppliedLayoutsFromZonesSettingsNoFile)
{
// test
m_fzData.ReplaceZoneSettingsFileFromOlderVersions();
AppliedLayouts::instance().LoadData();
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
}
TEST_METHOD (CloneDeviceInfo)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc));
Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst));
AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst);
auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = AppliedLayouts::instance().GetDeviceLayout(deviceSrc);
auto actual = AppliedLayouts::instance().GetDeviceLayout(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value().uuid == actual.value().uuid);
}
TEST_METHOD (CloneDeviceInfoIntoUnknownDevice)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc));
AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst);
auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = AppliedLayouts::instance().GetDeviceLayout(deviceSrc);
auto actual = AppliedLayouts::instance().GetDeviceLayout(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value().uuid == actual.value().uuid);
}
TEST_METHOD (CloneDeviceInfoFromUnknownDevice)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
AppliedLayouts::instance().LoadData();
Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst));
Assert::IsFalse(AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst));
Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(deviceSrc).has_value());
Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceDst).has_value());
}
TEST_METHOD (CloneDeviceInfoNullVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc));
Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst));
AppliedLayouts::instance().CloneLayout(deviceSrc, deviceDst);
auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = AppliedLayouts::instance().GetDeviceLayout(deviceSrc);
auto actual = AppliedLayouts::instance().GetDeviceLayout(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value().uuid == actual.value().uuid);
}
TEST_METHOD (ApplyLayout)
{
// prepare
FancyZonesDataTypes::DeviceIdData deviceId {
.deviceName = L"DELA026#5&10a58c63&0&UID16777488",
.width = 2194,
.height = 1234,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value()
};
// test
FancyZonesDataTypes::ZoneSetData expectedZoneSetData {
.uuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}",
.type = FancyZonesDataTypes::ZoneSetLayoutType::Focus
};
AppliedLayouts::instance().ApplyLayout(deviceId, expectedZoneSetData);
Assert::IsFalse(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceId).has_value());
}
TEST_METHOD (ApplyLayoutReplace)
{
// prepare
FancyZonesDataTypes::DeviceIdData deviceId{
.deviceName = L"DELA026#5&10a58c63&0&UID16777488",
.width = 2194,
.height = 1234,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value()
};
json::JsonObject root{};
json::JsonArray layoutsArray{};
{
json::JsonObject layout{};
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::UuidID, json::value(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}"));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::TypeID, json::value(FancyZonesDataTypes::TypeToString(FancyZonesDataTypes::ZoneSetLayoutType::Rows)));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(true));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(3));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(4));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(22));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceIdID, json::value(L"DELA026#5&10a58c63&0&UID16777488_2194_1234_{61FA9FC0-26A6-4B37-A834-491C148DFC57}"));
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
layoutsArray.Append(obj);
}
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
AppliedLayouts::instance().LoadData();
// test
FancyZonesDataTypes::ZoneSetData expectedZoneSetData {
.uuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}",
.type = FancyZonesDataTypes::ZoneSetLayoutType::Focus
};
AppliedLayouts::instance().ApplyLayout(deviceId, expectedZoneSetData);
Assert::AreEqual((size_t)1, AppliedLayouts::instance().GetAppliedLayoutMap().size());
Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceId).has_value());
auto actual = AppliedLayouts::instance().GetAppliedLayoutMap().find(deviceId)->second;
Assert::AreEqual(expectedZoneSetData.uuid.c_str(), FancyZonesUtils::GuidToString(actual.uuid).value().c_str());
Assert::IsTrue(expectedZoneSetData.type == actual.type);
}
TEST_METHOD (ApplyDefaultLayout)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
};
auto result = AppliedLayouts::instance().ApplyDefaultLayout(expected);
Assert::IsTrue(result);
auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (ApplyDefaultLayoutWithNullVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = AppliedLayouts::instance().ApplyDefaultLayout(expected);
Assert::IsTrue(result);
auto actualMap = AppliedLayouts::instance().GetAppliedLayoutMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
};
}

View File

@@ -96,7 +96,6 @@ namespace FancyZonesUnitTests
TEST_METHOD_INITIALIZE(Init)
{
m_fzData.clear_data();
m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests");
}

View File

@@ -3,9 +3,10 @@
#include <filesystem>
#include <FancyZonesLib/FancyZones.h>
#include <FancyZonesLib/FancyZonesData.h>
#include <FancyZonesLib/Settings.h>
#include <common/SettingsAPI/settings_helpers.h>
#include "util.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@@ -63,8 +64,6 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IFancyZonesSettings> m_settings = nullptr;
winrt::com_ptr<IFancyZonesCallback> m_fzCallback = nullptr;
FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
std::wstring serializedPowerToySettings(const Settings& settings)
{
PowerToysSettings::Settings ptSettings(HINSTANCE{}, L"FancyZonesUnitTests");
@@ -111,31 +110,27 @@ namespace FancyZonesUnitTests
}
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_settings = MakeFancyZonesSettings(m_hInst, m_moduleName.c_str(), m_moduleKey.c_str());
Assert::IsTrue(m_settings != nullptr);
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_settings = MakeFancyZonesSettings(m_hInst, m_moduleName.c_str(), m_moduleKey.c_str());
Assert::IsTrue(m_settings != nullptr);
auto fancyZones = MakeFancyZones(m_hInst, m_settings, nullptr);
Assert::IsTrue(fancyZones != nullptr);
auto fancyZones = MakeFancyZones(m_hInst, m_settings, nullptr);
Assert::IsTrue(fancyZones != nullptr);
m_fzCallback = fancyZones.as<IFancyZonesCallback>();
Assert::IsTrue(m_fzCallback != nullptr);
m_fzCallback = fancyZones.as<IFancyZonesCallback>();
Assert::IsTrue(m_fzCallback != nullptr);
}
m_fancyZonesData.clear_data();
}
TEST_METHOD_CLEANUP(Cleanup)
{
sendKeyboardInput(VK_SHIFT, true);
sendKeyboardInput(VK_LWIN, true);
sendKeyboardInput(VK_CONTROL, true);
TEST_METHOD_CLEANUP(Cleanup)
{
sendKeyboardInput(VK_SHIFT, true);
sendKeyboardInput(VK_LWIN, true);
sendKeyboardInput(VK_CONTROL, true);
auto settingsFolder = PTSettingsHelper::get_module_save_folder_location(m_moduleName);
const auto settingsFile = settingsFolder + L"\\settings.json";
std::filesystem::remove(settingsFile);
std::filesystem::remove(settingsFolder);
}
auto settingsFolder = PTSettingsHelper::get_module_save_folder_location(m_moduleName);
std::filesystem::remove_all(settingsFolder);
}
TEST_METHOD (OnKeyDownNothingPressed)
{

View File

@@ -3,8 +3,8 @@
#include <fstream>
#include <utility>
#include <FancyZonesLib/FancyZonesData.h>
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include <FancyZonesLib/FancyZonesDataTypes.h>
#include <FancyZonesLib/JsonHelpers.h>
#include <FancyZonesLib/util.h>
@@ -996,20 +996,20 @@ namespace FancyZonesUnitTests
const std::wstring_view m_moduleName = L"FancyZonesUnitTests";
const std::wstring m_defaultDeviceIdStr = L"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
const std::wstring m_defaultCustomDeviceStr = L"{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"active-zoneset\": {\"type\": \"custom\", \"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\"}, \"editor-show-spacing\": true, \"editor-spacing\": 16, \"editor-zone-count\": 3}";
const std::wstring m_defaultCustomLayoutStr = L"{\"device-id\": \"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\", \"applied-layout\": {\"type\": \"custom\", \"uuid\": \"{33A2B101-06E0-437B-A61E-CDBECF502906}\", \"show-spacing\": true, \"spacing\": 16, \"zone-count\": 3, \"sensitivity-radius\": 30}}";
const json::JsonValue m_defaultCustomDeviceValue = json::JsonValue::Parse(m_defaultCustomDeviceStr);
const json::JsonObject m_defaultCustomDeviceObj = json::JsonObject::Parse(m_defaultCustomDeviceStr);
const json::JsonObject m_defaultCustomLayoutObj = json::JsonObject::Parse(m_defaultCustomLayoutStr);
const FancyZonesDataTypes::DeviceIdData m_defaultDeviceId = FancyZonesDataTypes::DeviceIdData::ParseDeviceId(m_defaultDeviceIdStr).value();
GUID m_defaultVDId;
HINSTANCE m_hInst{};
FancyZonesData& m_fzData = FancyZonesDataInstance();
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_fzData.clear_data();
std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName));
auto guid = Helpers::StringToGuid(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}");
@@ -1020,46 +1020,15 @@ namespace FancyZonesUnitTests
TEST_METHOD_CLEANUP(CleanUp)
{
std::filesystem::remove_all(PTSettingsHelper::get_module_save_folder_location(m_moduleName));
std::filesystem::remove_all(AppZoneHistory::AppZoneHistoryFileName());
}
public:
TEST_METHOD (FancyZonesDataPath)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
Assert::IsFalse(data.zonesSettingsFileName.empty());
}
TEST_METHOD (FancyZonesDataJsonEmpty)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
json::JsonObject expected;
auto actual = data.GetPersistFancyZonesJSON();
Assert::AreEqual(expected.Stringify().c_str(), actual.Stringify().c_str());
}
TEST_METHOD (FancyZonesDataJson)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
json::JsonObject expected = json::JsonObject::Parse(L"{\"fancy-zones\":{\"custom-zonesets \":[{\"uuid\":\"uuid1\",\"name\":\"Custom1\",\"type\":\"custom\" }] }, \"app-zone-history\":[] }");
json::to_file(jsonPath, expected);
auto actual = data.GetPersistFancyZonesJSON();
Assert::AreEqual(expected.Stringify().c_str(), actual.Stringify().c_str());
}
TEST_METHOD (FancyZonesDataDeviceInfoMapParseEmpty)
{
json::JsonObject deviceInfoJson;
const auto& deviceInfoMap = ParseDeviceInfos(deviceInfoJson);
Assert::IsTrue(deviceInfoMap.empty());
Assert::IsFalse(deviceInfoMap.has_value());
}
TEST_METHOD (FancyZonesDataDeviceInfoMapParseValidEmpty)
@@ -1070,7 +1039,8 @@ namespace FancyZonesUnitTests
const auto& deviceInfoMap = ParseDeviceInfos(deviceInfoJson);
Assert::IsTrue(deviceInfoMap.empty());
Assert::IsTrue(deviceInfoMap.has_value());
Assert::IsTrue(deviceInfoMap->empty());
}
TEST_METHOD (FancyZonesDataDeviceInfoMapParseValidAndInvalid)
@@ -1084,7 +1054,7 @@ namespace FancyZonesUnitTests
const auto& deviceInfoMap = ParseDeviceInfos(deviceInfoJson);
Assert::AreEqual((size_t)1, deviceInfoMap.size());
Assert::AreEqual((size_t)1, deviceInfoMap->size());
}
TEST_METHOD (FancyZonesDataDeviceInfoMapParseInvalid)
@@ -1097,7 +1067,7 @@ namespace FancyZonesUnitTests
const auto& deviceInfoMap = ParseDeviceInfos(deviceInfoJson);
Assert::IsTrue(deviceInfoMap.empty());
Assert::IsTrue(deviceInfoMap->empty());
}
TEST_METHOD (FancyZonesDataDeviceInfoMapParseSingle)
@@ -1109,7 +1079,7 @@ namespace FancyZonesUnitTests
const auto& deviceInfoMap = ParseDeviceInfos(deviceInfoJson);
Assert::AreEqual((size_t)1, deviceInfoMap.size());
Assert::AreEqual((size_t)1, deviceInfoMap->size());
}
TEST_METHOD (FancyZonesDataDeviceInfoMapParseMany)
@@ -1129,21 +1099,7 @@ namespace FancyZonesUnitTests
const auto& deviceInfoMap = ParseDeviceInfos(expected);
Assert::AreEqual((size_t)10, deviceInfoMap.size());
}
TEST_METHOD (FancyZonesDataSerialize)
{
json::JsonArray expectedDevices;
expectedDevices.Append(m_defaultCustomDeviceObj);
json::JsonObject expected;
expected.SetNamedValue(L"devices", expectedDevices);
const auto& deviceInfoMap = ParseDeviceInfos(expected);
auto actual = SerializeDeviceInfos(deviceInfoMap);
auto res = CustomAssert::CompareJsonArrays(expectedDevices, actual);
Assert::IsTrue(res.first, res.second.c_str());
Assert::AreEqual((size_t)10, deviceInfoMap->size());
}
TEST_METHOD (AppZoneHistoryParseSingle)
@@ -1372,449 +1328,6 @@ namespace FancyZonesUnitTests
auto res = CustomAssert::CompareJsonArrays(expected, actual);
Assert::IsTrue(res.first, res.second.c_str());
}
TEST_METHOD (SetActiveZoneSet)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto uniqueId = m_defaultDeviceId;
json::JsonArray devices;
devices.Append(m_defaultCustomDeviceValue);
json::JsonObject json;
json.SetNamedValue(L"devices", devices);
data.ParseDeviceInfos(json);
FancyZonesDataTypes::ZoneSetData expectedZoneSetData{
.uuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}",
.type = ZoneSetLayoutType::Focus
};
data.SetActiveZoneSet(uniqueId, expectedZoneSetData);
auto actual = data.GetDeviceInfoMap().find(uniqueId)->second;
Assert::AreEqual(expectedZoneSetData.uuid.c_str(), actual.activeZoneSet.uuid.c_str());
Assert::IsTrue(expectedZoneSetData.type == actual.activeZoneSet.type);
}
TEST_METHOD (SetActiveZoneSetUuidEmpty)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring expected = L"{39B25DD2-130D-4B5D-8851-4791D66B1539}";
const auto uniqueId = m_defaultDeviceId;
json::JsonArray devices;
devices.Append(m_defaultCustomDeviceValue);
json::JsonObject json;
json.SetNamedValue(L"devices", devices);
data.ParseDeviceInfos(json);
FancyZonesDataTypes::ZoneSetData expectedZoneSetData{
.uuid = L"",
.type = ZoneSetLayoutType::Focus
};
data.SetActiveZoneSet(uniqueId, expectedZoneSetData);
auto actual = data.GetDeviceInfoMap().find(uniqueId)->second;
Assert::AreEqual(expectedZoneSetData.uuid.c_str(), actual.activeZoneSet.uuid.c_str());
Assert::IsTrue(expectedZoneSetData.type == actual.activeZoneSet.type);
}
TEST_METHOD (SetActiveZoneSetUniqueIdInvalid)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const std::wstring expected = L"{33A2B101-06E0-437B-A61E-CDBECF502906}";
auto uniqueId = m_defaultDeviceId;
uniqueId.deviceName = L"id-not-contained-by-device-info-map";
json::JsonArray devices;
devices.Append(m_defaultCustomDeviceValue);
json::JsonObject json;
json.SetNamedValue(L"devices", devices);
bool parseRes = data.ParseDeviceInfos(json);
Assert::IsTrue(parseRes);
FancyZonesDataTypes::ZoneSetData zoneSetData{
.uuid = L"new_uuid",
.type = ZoneSetLayoutType::Focus
};
data.SetActiveZoneSet(uniqueId, zoneSetData);
const auto& deviceInfoMap = data.GetDeviceInfoMap();
auto actual = deviceInfoMap.find(m_defaultDeviceId)->second;
Assert::AreEqual(expected.c_str(), actual.activeZoneSet.uuid.c_str());
Assert::IsTrue(deviceInfoMap.end() == deviceInfoMap.find(uniqueId), L"new device info should not be added");
}
TEST_METHOD (LoadFancyZonesDataFromJson)
{
FancyZonesData fancyZonesData;
fancyZonesData.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = fancyZonesData.zonesSettingsFileName;
auto savedJson = json::from_file(jsonPath);
if (std::filesystem::exists(jsonPath))
{
std::filesystem::remove(jsonPath);
}
const GridLayoutInfo grid(GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 3,
.rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }));
CustomZoneSetJSON zoneSets{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", CustomLayoutData{ L"name", CustomLayoutType::Grid, grid } };
AppZoneHistoryData data{
.zoneSetUuid = L"{33A2B101-06E0-437B-A61E-CDBECF502906}", .deviceId = L"device-id", .zoneIndexSet = { 54321 }
};
AppZoneHistoryJSON appZoneHistory{ L"app-path", std::vector<AppZoneHistoryData>{ data } };
DeviceInfoJSON deviceInfo { FancyZonesDataTypes::DeviceIdData{ L"device-id", 0, 0, m_defaultVDId }, DeviceInfoData{ ZoneSetData{ L"{33A2B101-16E1-437B-A61E-CDBECF502906}", ZoneSetLayoutType::Custom }, true, 16, 3 } };
LayoutQuickKeyJSON quickKeys{ L"{33A2B101-06E0-437B-A61E-CDBECF502906}", 1 };
json::JsonArray zoneSetsArray, appZonesArray, deviceInfoArray, quickKeysArray;
zoneSetsArray.Append(CustomZoneSetJSON::ToJson(zoneSets));
appZonesArray.Append(AppZoneHistoryJSON::ToJson(appZoneHistory));
deviceInfoArray.Append(DeviceInfoJSON::ToJson(deviceInfo));
quickKeysArray.Append(LayoutQuickKeyJSON::ToJson(quickKeys));
json::JsonObject fancyZones;
fancyZones.SetNamedValue(L"custom-zone-sets", zoneSetsArray);
fancyZones.SetNamedValue(L"app-zone-history", appZonesArray);
fancyZones.SetNamedValue(L"devices", deviceInfoArray);
fancyZones.SetNamedValue(L"quick-layout-keys", quickKeysArray);
json::to_file(jsonPath, fancyZones);
fancyZonesData.LoadFancyZonesData();
if (savedJson)
{
json::to_file(jsonPath, *savedJson);
}
else
{
std::filesystem::remove(jsonPath);
}
Assert::IsFalse(fancyZonesData.GetDeviceInfoMap().empty());
}
TEST_METHOD (LoadFancyZonesDataFromCroppedJson)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
std::wofstream{ jsonPath.data(), std::ios::binary } << L"{ \"devices\": [{\"device-id\": \"";
data.LoadFancyZonesData();
Assert::IsTrue(data.GetDeviceInfoMap().empty());
}
TEST_METHOD (LoadFancyZonesDataFromJsonWithCyrillicSymbols)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
std::wofstream{ jsonPath.data(), std::ios::binary } << L"{ \"devices\": [{\"device-id\": \"кириллица\"}], \"custom-zone-sets\": []}";
data.LoadFancyZonesData();
Assert::IsTrue(data.GetDeviceInfoMap().empty());
}
TEST_METHOD (LoadFancyZonesDataFromJsonWithInvalidTypes)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
std::wofstream{ jsonPath.data(), std::ios::binary } << L"{ \"devices\": [{\"device-id\":\"AOC2460#4&fe3a015&0&UID65793_1920_1200_{39B25DD2-130D-4B5D-8851-4791D66B1539}\",\"active-zoneset\":{\"uuid\":\"{568EBC3A-C09C-483E-A64D-6F1F2AF4E48D}\",\"type\":\"columns\"},\"editor-show-spacing\":true,\"editor-spacing\":16,\"editor-zone-count\":3}], \"custom-zone-sets\": null}";
data.LoadFancyZonesData();
Assert::IsFalse(data.GetDeviceInfoMap().empty());
}
TEST_METHOD (LoadFancyZonesDataFromRegistry)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
data.LoadFancyZonesData();
bool actual = std::filesystem::exists(jsonPath);
Assert::IsTrue(actual);
}
TEST_METHOD (SaveFancyZonesData)
{
FancyZonesData data;
data.SetSettingsModulePath(m_moduleName);
const auto& jsonPath = data.zonesSettingsFileName;
data.SaveZoneSettings();
bool actual = std::filesystem::exists(jsonPath);
Assert::IsTrue(actual);
}
TEST_METHOD (AddDevice)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceWithNullVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceDuplicate)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto result2 = m_fzData.AddDevice(expected);
Assert::IsFalse(result2);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceWithNullVirtualDesktopIdDuplicated)
{
FancyZonesDataTypes::DeviceIdData expected{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = m_fzData.AddDevice(expected);
Assert::IsTrue(result);
auto result2 = m_fzData.AddDevice(expected);
Assert::IsFalse(result2);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(expected) == actualMap.end());
}
TEST_METHOD (AddDeviceDuplicatedComparedWithNillVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData device1{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData device2{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result = m_fzData.AddDevice(device1);
Assert::IsTrue(result);
auto result2 = m_fzData.AddDevice(device2);
Assert::IsFalse(result2);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(device1) == actualMap.end());
Assert::IsTrue(actualMap.find(device2) == actualMap.end());
}
TEST_METHOD (AddDeviceDuplicatedComparedWithNillVirtualDesktopId2)
{
FancyZonesDataTypes::DeviceIdData device1{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData device2{
.deviceName = L"Device",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
auto result2 = m_fzData.AddDevice(device2);
Assert::IsTrue(result2);
auto result1 = m_fzData.AddDevice(device1);
Assert::IsFalse(result1);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(device2) == actualMap.end());
Assert::IsTrue(actualMap.find(device1) == actualMap.end());
}
TEST_METHOD(CloneDeviceInfo)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceSrc));
Assert::IsTrue(m_fzData.AddDevice(deviceDst));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = m_fzData.FindDeviceInfo(deviceSrc);
auto actual = m_fzData.FindDeviceInfo(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value() == actual.value());
}
TEST_METHOD (CloneDeviceInfoIntoUnknownDevice)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceSrc));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = m_fzData.FindDeviceInfo(deviceSrc);
auto actual = m_fzData.FindDeviceInfo(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value() == actual.value());
}
TEST_METHOD (CloneDeviceInfoFromUnknownDevice)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = m_defaultVDId
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceDst));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsTrue(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
Assert::IsFalse(m_fzData.FindDeviceInfo(deviceSrc).has_value());
Assert::IsTrue(m_fzData.FindDeviceInfo(deviceDst).has_value());
}
TEST_METHOD(CloneDeviceInfoNullVirtualDesktopId)
{
FancyZonesDataTypes::DeviceIdData deviceSrc{
.deviceName = L"Device1",
.width = 200,
.height = 100,
.virtualDesktopId = GUID_NULL
};
FancyZonesDataTypes::DeviceIdData deviceDst{
.deviceName = L"Device2",
.width = 300,
.height = 400,
.virtualDesktopId = m_defaultVDId
};
Assert::IsTrue(m_fzData.AddDevice(deviceSrc));
Assert::IsTrue(m_fzData.AddDevice(deviceDst));
m_fzData.CloneDeviceInfo(deviceSrc, deviceDst);
auto actualMap = m_fzData.GetDeviceInfoMap();
Assert::IsFalse(actualMap.find(deviceSrc) == actualMap.end());
Assert::IsFalse(actualMap.find(deviceDst) == actualMap.end());
auto expected = m_fzData.FindDeviceInfo(deviceSrc);
auto actual = m_fzData.FindDeviceInfo(deviceDst);
Assert::IsTrue(expected.has_value());
Assert::IsTrue(actual.has_value());
Assert::IsTrue(expected.value() == actual.value());
}
};
TEST_CLASS(EditorArgsUnitTests)

View File

@@ -16,8 +16,7 @@ namespace FancyZonesUnitTests
TEST_METHOD_INITIALIZE(Init)
{
m_fzData.clear_data();
m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests");
m_fzData.SetSettingsModulePath(m_testFolder);
}
TEST_METHOD_CLEANUP(CleanUp)

View File

@@ -17,8 +17,7 @@ namespace FancyZonesUnitTests
TEST_METHOD_INITIALIZE(Init)
{
m_fzData.clear_data();
m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests");
m_fzData.SetSettingsModulePath(m_testFolder);
}
TEST_METHOD_CLEANUP(CleanUp)

View File

@@ -41,6 +41,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AppliedLayoutsTests.Spec.cpp" />
<ClCompile Include="AppZoneHistoryTests.Spec.cpp" />
<ClCompile Include="CustomLayoutsTests.Spec.cpp" />
<ClCompile Include="FancyZones.Spec.cpp" />

View File

@@ -54,6 +54,9 @@
<ClCompile Include="AppZoneHistoryTests.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AppliedLayoutsTests.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">

View File

@@ -6,7 +6,7 @@
#include <FancyZonesLib/ZoneSet.h>
#include <FancyZonesLib/WorkArea.h>
#include <FancyZonesLib/FancyZones.h>
#include <FancyZonesLib/FancyZonesData.h>
#include <FancyZonesLib/FancyZonesData/AppliedLayouts.h>
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesDataTypes.h>
#include <FancyZonesLib/JsonHelpers.h>
@@ -35,8 +35,6 @@ namespace FancyZonesUnitTests
OverlappingZonesAlgorithm m_overlappingAlgorithm = OverlappingZonesAlgorithm::Positional;
bool m_showZoneText = true;
FancyZonesData& m_fancyZonesData = FancyZonesDataInstance();
void testWorkArea(winrt::com_ptr<IWorkArea> workArea)
{
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
@@ -46,37 +44,43 @@ namespace FancyZonesUnitTests
}
TEST_METHOD_INITIALIZE(Init)
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
{
m_hInst = (HINSTANCE)GetModuleHandleW(nullptr);
m_monitor = MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_monitor = MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_parentUniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_parentUniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_parentUniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}", &m_parentUniqueId.virtualDesktopId);
m_parentUniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_parentUniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_parentUniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}", &m_parentUniqueId.virtualDesktopId);
m_uniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId);
m_fancyZonesData.SetSettingsModulePath(L"FancyZonesUnitTests");
m_fancyZonesData.clear_data();
m_uniqueId.deviceName = L"DELA026#5&10a58c63&0&UID16777488";
m_uniqueId.width = m_monitorInfo.rcMonitor.right - m_monitorInfo.rcMonitor.left;
m_uniqueId.height = m_monitorInfo.rcMonitor.bottom - m_monitorInfo.rcMonitor.top;
CLSIDFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}", &m_uniqueId.virtualDesktopId);
auto guid = Helpers::StringToGuid(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}");
Assert::IsTrue(guid.has_value());
m_virtualDesktopGuid = *guid;
auto guid = Helpers::StringToGuid(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}");
Assert::IsTrue(guid.has_value());
m_virtualDesktopGuid = *guid;
m_zoneColors = ZoneColors{
.primaryColor = FancyZonesUtils::HexToRGB(L"#4287f5"),
.borderColor = FancyZonesUtils::HexToRGB(L"#FFFFFF"),
.highlightColor = FancyZonesUtils::HexToRGB(L"#42eff5"),
.highlightOpacity = 50,
};
m_zoneColors = ZoneColors{
.primaryColor = FancyZonesUtils::HexToRGB(L"#4287f5"),
.borderColor = FancyZonesUtils::HexToRGB(L"#FFFFFF"),
.highlightColor = FancyZonesUtils::HexToRGB(L"#42eff5"),
.highlightOpacity = 50,
};
}
AppZoneHistory::instance().LoadData();
AppliedLayouts::instance().LoadData();
}
TEST_METHOD_CLEANUP(CleanUp)
{
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
}
TEST_METHOD (CreateWorkArea)
{
@@ -176,29 +180,26 @@ namespace FancyZonesUnitTests
{
using namespace FancyZonesDataTypes;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId, parentDeviceInfo);
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText);
auto parentWorkArea = MakeWorkArea(m_hInst, m_monitor, m_parentUniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText);
// newWorkArea = false - workArea won't be cloned from parent
auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText);
// newWorkArea = false - workArea won't be cloned from parent
auto actualWorkArea = MakeWorkArea(m_hInst, m_monitor, m_uniqueId, {}, m_zoneColors, m_overlappingAlgorithm, m_showZoneText);
Assert::IsNotNull(actualWorkArea->ZoneSet());
Assert::IsNotNull(actualWorkArea->ZoneSet());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId);
// default values
Assert::AreEqual(true, currentDeviceInfo.showSpacing);
Assert::AreEqual(3, currentDeviceInfo.zoneCount);
Assert::AreEqual(16, currentDeviceInfo.spacing);
Assert::AreEqual(static_cast<int>(ZoneSetLayoutType::PriorityGrid), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_uniqueId));
auto currentDeviceInfo = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_uniqueId);
// default values
Assert::AreEqual(true, currentDeviceInfo.showSpacing);
Assert::AreEqual(3, currentDeviceInfo.zoneCount);
Assert::AreEqual(16, currentDeviceInfo.spacing);
Assert::AreEqual(static_cast<int>(ZoneSetLayoutType::PriorityGrid), static_cast<int>(currentDeviceInfo.type));
}
};
TEST_CLASS (WorkAreaUnitTests)
@@ -233,11 +234,13 @@ namespace FancyZonesUnitTests
};
AppZoneHistory::instance().LoadData();
AppliedLayouts::instance().LoadData();
}
TEST_METHOD_CLEANUP(CleanUp)
{
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
}
public:

View File

@@ -1,5 +1,5 @@
#include "pch.h"
#include "FancyZonesLib\FancyZonesData.h"
#include <FancyZonesLib/FancyZonesData/LayoutDefaults.h>
#include "FancyZonesLib\FancyZonesDataTypes.h"
#include "FancyZonesLib\JsonHelpers.h"
#include "FancyZonesLib\VirtualDesktop.h"

View File

@@ -84,37 +84,32 @@ namespace FancyZonesEditor
FancyZonesEditorIO.ParseCommandLineArguments();
}
var parseResult = FancyZonesEditorIO.ParseZoneSettings();
// 10ms retry loop with 1 second timeout
if (!parseResult.Result)
{
CancellationTokenSource ts = new CancellationTokenSource();
Task t = Task.Run(() =>
{
while (!parseResult.Result && !ts.IsCancellationRequested)
{
Task.Delay(10).Wait();
parseResult = FancyZonesEditorIO.ParseZoneSettings();
}
});
try
{
bool result = t.Wait(1000, ts.Token);
ts.Cancel();
}
catch (OperationCanceledException)
{
ts.Dispose();
}
}
// Error message if parsing failed
var parseResult = FancyZonesEditorIO.ParseAppliedLayouts();
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Title, MessageBoxButton.OK);
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Data_Title, MessageBoxButton.OK);
}
parseResult = FancyZonesEditorIO.ParseCustomLayouts();
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Data_Title, MessageBoxButton.OK);
}
parseResult = FancyZonesEditorIO.ParseLayoutHotkeys();
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Data_Title, MessageBoxButton.OK);
}
parseResult = FancyZonesEditorIO.ParseLayoutTemplates();
if (!parseResult.Result)
{
Logger.LogError(ParsingErrorReportTag + ": " + parseResult.Message + "; " + ParsingErrorDataTag + ": " + parseResult.MalformedData);
MessageBox.Show(parseResult.Message, FancyZonesEditor.Properties.Resources.Error_Parsing_Data_Title, MessageBoxButton.OK);
}
MainWindowSettingsModel settings = ((App)Current).MainWindowSettings;

View File

@@ -33,6 +33,7 @@ namespace FancyZonesEditor
App.FancyZonesEditorIO.SerializeLayoutTemplates();
App.FancyZonesEditorIO.SerializeCustomLayouts();
App.FancyZonesEditorIO.SerializeAppliedLayouts();
Close();
}

View File

@@ -250,7 +250,7 @@ namespace FancyZonesEditor
model.Persist();
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
App.FancyZonesEditorIO.SerializeZoneSettings();
App.FancyZonesEditorIO.SerializeAppliedLayouts();
App.FancyZonesEditorIO.SerializeCustomLayouts();
}
@@ -272,7 +272,7 @@ namespace FancyZonesEditor
{
_settings.SetAppliedModel(model);
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
App.FancyZonesEditorIO.SerializeZoneSettings();
App.FancyZonesEditorIO.SerializeAppliedLayouts();
App.FancyZonesEditorIO.SerializeCustomLayouts();
}
}
@@ -282,7 +282,7 @@ namespace FancyZonesEditor
Logger.LogTrace();
CancelLayoutChanges();
App.FancyZonesEditorIO.SerializeZoneSettings();
App.FancyZonesEditorIO.SerializeAppliedLayouts();
App.FancyZonesEditorIO.SerializeCustomLayouts();
App.FancyZonesEditorIO.SerializeLayoutHotkeys();
App.FancyZonesEditorIO.SerializeLayoutTemplates();
@@ -428,7 +428,7 @@ namespace FancyZonesEditor
App.Overlay.SetLayoutSettings(App.Overlay.Monitors[App.Overlay.CurrentDesktop], model);
}
App.FancyZonesEditorIO.SerializeZoneSettings();
App.FancyZonesEditorIO.SerializeAppliedLayouts();
App.FancyZonesEditorIO.SerializeCustomLayouts();
App.FancyZonesEditorIO.SerializeLayoutTemplates();
App.FancyZonesEditorIO.SerializeLayoutHotkeys();
@@ -475,7 +475,7 @@ namespace FancyZonesEditor
}
}
App.FancyZonesEditorIO.SerializeZoneSettings();
App.FancyZonesEditorIO.SerializeAppliedLayouts();
App.FancyZonesEditorIO.SerializeCustomLayouts();
model.Delete();
}

View File

@@ -375,6 +375,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to An error occurred while parsing applied layouts..
/// </summary>
public static string Error_Parsing_Applied_Layouts_Message {
get {
return ResourceManager.GetString("Error_Parsing_Applied_Layouts_Message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An error occurred while parsing custom layouts..
/// </summary>
@@ -384,6 +393,15 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Editor data parsing error..
/// </summary>
public static string Error_Parsing_Data_Title {
get {
return ResourceManager.GetString("Error_Parsing_Data_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error parsing device info data..
/// </summary>
@@ -411,24 +429,6 @@ namespace FancyZonesEditor.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to A layout that contained invalid data has been removed..
/// </summary>
public static string Error_Parsing_Zones_Settings_Message {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_Message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Editor data parsing error..
/// </summary>
public static string Error_Parsing_Zones_Settings_Title {
get {
return ResourceManager.GetString("Error_Parsing_Zones_Settings_Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error persisting custom layout.
/// </summary>

View File

@@ -304,11 +304,11 @@
<value>Delete zone</value>
<comment>A tooltip on a button that allows the user to delete a zone</comment>
</data>
<data name="Error_Parsing_Zones_Settings_Title" xml:space="preserve">
<data name="Error_Parsing_Data_Title" xml:space="preserve">
<value>Editor data parsing error.</value>
</data>
<data name="Error_Parsing_Zones_Settings_Message" xml:space="preserve">
<value>A layout that contained invalid data has been removed.</value>
<data name="Error_Parsing_Applied_Layouts_Message" xml:space="preserve">
<value>An error occurred while parsing applied layouts.</value>
</data>
<data name="Apply_Layout" xml:space="preserve">
<value>Apply layout</value>

View File

@@ -10,6 +10,7 @@ using System.IO;
using System.IO.Abstractions;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using FancyZonesEditor.Logs;
using FancyZonesEditor.Models;
@@ -28,7 +29,7 @@ namespace FancyZonesEditor.Utils
private const string CustomJsonTag = "custom";
// Non-localizable strings: Files
private const string ZonesSettingsFile = "\\Microsoft\\PowerToys\\FancyZones\\zones-settings.json";
private const string AppliedLayoutsFile = "\\Microsoft\\PowerToys\\FancyZones\\applied-layouts.json";
private const string LayoutHotkeysFile = "\\Microsoft\\PowerToys\\FancyZones\\layout-hotkeys.json";
private const string LayoutTemplatesFile = "\\Microsoft\\PowerToys\\FancyZones\\layout-templates.json";
private const string CustomLayoutsFile = "\\Microsoft\\PowerToys\\FancyZones\\custom-layouts.json";
@@ -48,9 +49,9 @@ namespace FancyZonesEditor.Utils
WriteIndented = true,
};
private List<DeviceWrapper> _unusedDevices = new List<DeviceWrapper>();
private List<AppliedLayoutWrapper> _unusedLayouts = new List<AppliedLayoutWrapper>();
public string FancyZonesSettingsFile { get; private set; }
public string FancyZonesAppliedLayoutsFile { get; private set; }
public string FancyZonesLayoutHotkeysFile { get; private set; }
@@ -110,27 +111,33 @@ namespace FancyZonesEditor.Utils
}
}
// zones-settings: devices
private struct DeviceWrapper
// applied-layouts.json
private struct AppliedLayoutWrapper
{
public struct ActiveZoneSetWrapper
public struct LayoutWrapper
{
public string Uuid { get; set; }
public string Type { get; set; }
public bool ShowSpacing { get; set; }
public int Spacing { get; set; }
public int ZoneCount { get; set; }
public int SensitivityRadius { get; set; }
}
public string DeviceId { get; set; }
public ActiveZoneSetWrapper ActiveZoneset { get; set; }
public LayoutWrapper AppliedLayout { get; set; }
}
public bool EditorShowSpacing { get; set; }
public int EditorSpacing { get; set; }
public int EditorZoneCount { get; set; }
public int EditorSensitivityRadius { get; set; }
// applied-layouts.json
private struct AppliedLayoutsListWrapper
{
public List<AppliedLayoutWrapper> AppliedLayouts { get; set; }
}
// custom-layouts.json
@@ -228,12 +235,6 @@ namespace FancyZonesEditor.Utils
public List<LayoutHotkeyWrapper> LayoutHotkeys { get; set; }
}
// zones-settings
private struct ZoneSettingsWrapper
{
public List<DeviceWrapper> Devices { get; set; }
}
private struct EditorParams
{
public int ProcessId { get; set; }
@@ -262,7 +263,7 @@ namespace FancyZonesEditor.Utils
public FancyZonesEditorIO()
{
var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
FancyZonesSettingsFile = localAppDataDir + ZonesSettingsFile;
FancyZonesAppliedLayoutsFile = localAppDataDir + AppliedLayoutsFile;
FancyZonesLayoutHotkeysFile = localAppDataDir + LayoutHotkeysFile;
FancyZonesLayoutTemplatesFile = localAppDataDir + LayoutTemplatesFile;
FancyZonesCustomLayoutsFile = localAppDataDir + CustomLayoutsFile;
@@ -513,61 +514,43 @@ namespace FancyZonesEditor.Utils
}
}
public ParsingResult ParseZoneSettings()
public ParsingResult ParseAppliedLayouts()
{
Logger.LogTrace();
_unusedDevices.Clear();
_unusedLayouts.Clear();
if (_fileSystem.File.Exists(FancyZonesSettingsFile))
if (_fileSystem.File.Exists(FancyZonesAppliedLayoutsFile))
{
ZoneSettingsWrapper zoneSettings;
AppliedLayoutsListWrapper appliedLayouts;
string settingsString = string.Empty;
try
{
settingsString = ReadFile(FancyZonesSettingsFile);
zoneSettings = JsonSerializer.Deserialize<ZoneSettingsWrapper>(settingsString, _options);
settingsString = ReadFile(FancyZonesAppliedLayoutsFile);
appliedLayouts = JsonSerializer.Deserialize<AppliedLayoutsListWrapper>(settingsString, _options);
}
catch (Exception ex)
{
Logger.LogError("Zone settings parsing error", ex);
Logger.LogError("Applied layouts parsing error", ex);
return new ParsingResult(false, ex.Message, settingsString);
}
try
{
bool devicesParsingResult = SetDevices(zoneSettings.Devices);
if (!devicesParsingResult)
bool parsingResult = SetAppliedLayouts(appliedLayouts.AppliedLayouts);
if (!parsingResult)
{
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Zones_Settings_Message, settingsString);
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Applied_Layouts_Message, settingsString);
}
}
catch (Exception ex)
{
Logger.LogError("Zone settings parsing error", ex);
Logger.LogError("Applied layouts parsing error", ex);
return new ParsingResult(false, ex.Message, settingsString);
}
}
var parsingCustomLayoutsResult = ParseCustomLayouts();
if (!parsingCustomLayoutsResult.Result)
{
return parsingCustomLayoutsResult;
}
var parsingHotkeysResult = ParseLayoutHotkeys();
if (!parsingHotkeysResult.Result)
{
return parsingHotkeysResult;
}
var parsingTemplatesResult = ParseLayoutTemplates();
if (!parsingTemplatesResult.Result)
{
return parsingTemplatesResult;
}
return new ParsingResult(true);
}
@@ -690,14 +673,14 @@ namespace FancyZonesEditor.Utils
return new ParsingResult(false, FancyZonesEditor.Properties.Resources.Error_Parsing_Custom_Layouts_Message);
}
public void SerializeZoneSettings()
public void SerializeAppliedLayouts()
{
Logger.LogTrace();
ZoneSettingsWrapper zoneSettings = new ZoneSettingsWrapper { };
zoneSettings.Devices = new List<DeviceWrapper>();
AppliedLayoutsListWrapper layouts = new AppliedLayoutsListWrapper { };
layouts.AppliedLayouts = new List<AppliedLayoutWrapper>();
// Serialize used devices
// Serialize used layouts
foreach (var monitor in App.Overlay.Monitors)
{
LayoutSettings zoneset = monitor.Settings;
@@ -706,35 +689,35 @@ namespace FancyZonesEditor.Utils
continue;
}
zoneSettings.Devices.Add(new DeviceWrapper
layouts.AppliedLayouts.Add(new AppliedLayoutWrapper
{
DeviceId = monitor.Device.Id,
ActiveZoneset = new DeviceWrapper.ActiveZoneSetWrapper
AppliedLayout = new AppliedLayoutWrapper.LayoutWrapper
{
Uuid = zoneset.ZonesetUuid,
Type = LayoutTypeToJsonTag(zoneset.Type),
ShowSpacing = zoneset.ShowSpacing,
Spacing = zoneset.Spacing,
ZoneCount = zoneset.ZoneCount,
SensitivityRadius = zoneset.SensitivityRadius,
},
EditorShowSpacing = zoneset.ShowSpacing,
EditorSpacing = zoneset.Spacing,
EditorZoneCount = zoneset.ZoneCount,
EditorSensitivityRadius = zoneset.SensitivityRadius,
});
}
// Serialize unused devices
foreach (var device in _unusedDevices)
// Serialize unused layouts
foreach (var device in _unusedLayouts)
{
zoneSettings.Devices.Add(device);
layouts.AppliedLayouts.Add(device);
}
try
{
string jsonString = JsonSerializer.Serialize(zoneSettings, _options);
_fileSystem.File.WriteAllText(FancyZonesSettingsFile, jsonString);
string jsonString = JsonSerializer.Serialize(layouts, _options);
_fileSystem.File.WriteAllText(FancyZonesAppliedLayoutsFile, jsonString);
}
catch (Exception ex)
{
Logger.LogError("Serialize zone settings error", ex);
Logger.LogError("Serialize applied layouts error", ex);
App.ShowExceptionMessageBox(Properties.Resources.Error_Applying_Layout, ex);
}
}
@@ -918,29 +901,45 @@ namespace FancyZonesEditor.Utils
{
Logger.LogTrace();
Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open);
using (StreamReader reader = new StreamReader(inputStream))
var attempts = 0;
while (attempts < 10)
{
string data = reader.ReadToEnd();
inputStream.Close();
return data;
try
{
Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open);
using (StreamReader reader = new StreamReader(inputStream))
{
string data = reader.ReadToEnd();
inputStream.Close();
return data;
}
}
catch (Exception)
{
Logger.LogError("File reading error, retry");
Task.Delay(10).Wait();
}
attempts++;
}
return string.Empty;
}
private bool SetDevices(List<DeviceWrapper> devices)
private bool SetAppliedLayouts(List<AppliedLayoutWrapper> layouts)
{
Logger.LogTrace();
if (devices == null)
if (layouts == null)
{
return false;
}
bool result = true;
var monitors = App.Overlay.Monitors;
foreach (var device in devices)
foreach (var layout in layouts)
{
if (device.DeviceId == null || device.DeviceId.Length == 0 || device.ActiveZoneset.Uuid == null || device.ActiveZoneset.Uuid.Length == 0)
if (layout.DeviceId == null || layout.DeviceId.Length == 0 || layout.AppliedLayout.Uuid == null || layout.AppliedLayout.Uuid.Length == 0)
{
result = false;
continue;
@@ -950,21 +949,21 @@ namespace FancyZonesEditor.Utils
foreach (Monitor monitor in monitors)
{
string deviceIdSaved = monitor.Device.Id.Substring(0, monitor.Device.Id.LastIndexOf("_"));
string deviceIdReadFromSettings = device.DeviceId.Substring(0, device.DeviceId.LastIndexOf("_"));
string deviceIdReadFromSettings = layout.DeviceId.Substring(0, layout.DeviceId.LastIndexOf("_"));
string virtualDesktopIdSaved = monitor.Device.Id.Substring(monitor.Device.Id.LastIndexOf("_") + 1);
string virtualDesktopIdReadFromSettings = device.DeviceId.Substring(device.DeviceId.LastIndexOf("_") + 1);
string virtualDesktopIdReadFromSettings = layout.DeviceId.Substring(layout.DeviceId.LastIndexOf("_") + 1);
if (deviceIdSaved == deviceIdReadFromSettings && (virtualDesktopIdSaved == virtualDesktopIdReadFromSettings || virtualDesktopIdReadFromSettings == DefaultVirtualDesktopGuid))
{
var settings = new LayoutSettings
{
ZonesetUuid = device.ActiveZoneset.Uuid,
ShowSpacing = device.EditorShowSpacing,
Spacing = device.EditorSpacing,
Type = JsonTagToLayoutType(device.ActiveZoneset.Type),
ZoneCount = device.EditorZoneCount,
SensitivityRadius = device.EditorSensitivityRadius,
ZonesetUuid = layout.AppliedLayout.Uuid,
ShowSpacing = layout.AppliedLayout.ShowSpacing,
Spacing = layout.AppliedLayout.Spacing,
Type = JsonTagToLayoutType(layout.AppliedLayout.Type),
ZoneCount = layout.AppliedLayout.ZoneCount,
SensitivityRadius = layout.AppliedLayout.SensitivityRadius,
};
monitor.Settings = settings;
@@ -975,7 +974,7 @@ namespace FancyZonesEditor.Utils
if (unused)
{
_unusedDevices.Add(device);
_unusedLayouts.Add(layout);
}
}