diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 31b0208e4e..073a291b7f 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -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 diff --git a/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp b/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp index 2db09656d8..d0a91c2c4c 100644 --- a/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp +++ b/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp @@ -18,8 +18,7 @@ FancyZonesApp::FancyZonesApp(const std::wstring& appName, const std::wstring& ap DPIAware::EnableDPIAwarenessForThisProcess(); m_settings = MakeFancyZonesSettings(reinterpret_cast(&__ImageBase), appName.c_str(), appKey.c_str()); - FancyZonesDataInstance().LoadFancyZonesData(); - + InitializeWinhookEventIds(); m_app = MakeFancyZones(reinterpret_cast(&__ImageBase), m_settings, std::bind(&FancyZonesApp::DisableModule, this)); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp index 703fa28004..6130f92526 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp @@ -12,9 +12,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -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 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, ZoneIndexSet> FancyZones::GetAppZoneHistory void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr 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(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(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(); } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.cpp index 25e260d908..1ee3092a62 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.cpp @@ -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 #include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include #include +#include -#include -#include -#include +#include #include +#include // 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: __ - 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 callback) -{ - m_virtualDesktopCheckCallback = callback; -} - -const JSONHelpers::TDeviceInfoMap& FancyZonesData::GetDeviceInfoMap() const -{ - std::scoped_lock lock{ dataLock }; - return deviceInfoMap; -} - -std::optional 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 replaceWithCurrentId{}; - std::vector 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& activeDesktops) -{ - std::unordered_set 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(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(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); } } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h index 738dc7c62e..495de31a0c 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h @@ -1,38 +1,16 @@ #pragma once -#include "JsonHelpers.h" - +#if defined(UNIT_TESTS) #include -#include -#include - -#include -#include -#include -#include -#include -#include - -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 callback); - - std::optional 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& 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>& 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> 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 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(); \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp index 0df5f11ba6..8ed426e880 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp @@ -5,8 +5,10 @@ #include #include +#include #include #include +#include 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 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& activeDesktops) +{ + std::unordered_set 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(); + } } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h index 00d57fd4ee..3eab15fdae 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h @@ -21,8 +21,6 @@ public: return saveFolderPath + L"\\app-zone-history.json"; } - void SetVirtualDesktopCheckCallback(std::function 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 callback); + void SyncVirtualDesktops(GUID currentVirtualDesktopId); + void RemoveDeletedVirtualDesktops(const std::vector& activeDesktops); private: AppZoneHistory(); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp new file mode 100644 index 0000000000..7bd69f0a74 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp @@ -0,0 +1,420 @@ +#include "../pch.h" +#include "AppliedLayouts.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace JsonUtils +{ + struct LayoutJSON + { + static std::optional 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(json.GetNamedNumber(NonLocalizable::AppliedLayoutsIds::SpacingID)); + data.zoneCount = static_cast(json.GetNamedNumber(NonLocalizable::AppliedLayoutsIds::ZoneCountID)); + data.sensitivityRadius = static_cast(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 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(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 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 replaceWithCurrentId{}; + std::vector 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& activeDesktops) +{ + std::unordered_set 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 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(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(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; +} diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h new file mode 100644 index 0000000000..9b202659b2 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include +#include + +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; + + 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 callback); + void SyncVirtualDesktops(GUID currentVirtualDesktopId); + void RemoveDeletedVirtualDesktops(const std::vector& activeDesktops); + + std::optional 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 m_fileWatcher; + TAppliedLayoutsMap m_layouts; + std::function m_virtualDesktopCheckCallback; +}; diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp index bb07b46731..ce32fda8ac 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/CustomLayouts.cpp @@ -3,7 +3,7 @@ #include -#include // layout defaults +#include #include #include #include diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/Layout.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/Layout.h new file mode 100644 index 0000000000..2da62f7ce0 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/Layout.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +struct Layout +{ + GUID uuid; + FancyZonesDataTypes::ZoneSetLayoutType type; + bool showSpacing; + int spacing; + int zoneCount; + int sensitivityRadius; +}; \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutDefaults.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutDefaults.h new file mode 100644 index 0000000000..b41e5fce88 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutDefaults.h @@ -0,0 +1,9 @@ +#pragma once + +namespace DefaultValues +{ + const int ZoneCount = 3; + const bool ShowSpacing = true; + const int Spacing = 16; + const int SensitivityRadius = 20; +} diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.cpp new file mode 100644 index 0000000000..3341cdd196 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.cpp @@ -0,0 +1,100 @@ +#include "../pch.h" +#include "LayoutTemplates.h" + +#include + +#include +#include + +namespace JsonUtils +{ + struct TemplateLayoutJSON + { + static std::optional 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(json.GetNamedNumber(NonLocalizable::LayoutTemplatesIds::SpacingID, DefaultValues::Spacing)); + data.zoneCount = static_cast(json.GetNamedNumber(NonLocalizable::LayoutTemplatesIds::ZoneCountID, DefaultValues::ZoneCount)); + data.sensitivityRadius = static_cast(json.GetNamedNumber(NonLocalizable::LayoutTemplatesIds::SensitivityRadiusID, DefaultValues::SensitivityRadius)); + + return data; + } + catch (const winrt::hresult_error&) + { + return std::nullopt; + } + } + }; + + std::vector ParseJson(const json::JsonObject& json) + { + std::vector 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(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 LayoutTemplates::GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept +{ + for (const auto& layout : m_layouts) + { + if (layout.type == type) + { + return layout; + } + } + + return std::nullopt; +} + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.h index 06ce93cd30..f783057b76 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LayoutTemplates.h @@ -1,7 +1,9 @@ #pragma once +#include #include +#include #include 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 GetLayout(FancyZonesDataTypes::ZoneSetLayoutType type) const noexcept; + +private: + LayoutTemplates(); + ~LayoutTemplates() = default; + + std::unique_ptr m_fileWatcher; + std::vector m_layouts; }; \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.cpp index 519d33c3f9..0408854f6a 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.cpp @@ -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; - } } diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h index da34225cf5..26d34b5982 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h @@ -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) diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj index ea7147628f..ae2969ca78 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj @@ -38,9 +38,12 @@ + + + @@ -79,6 +82,14 @@ + + ../pch.h + ../pch.h + + + ../pch.h + ../pch.h + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters index 79f395d59e..f36e8e5336 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters @@ -93,6 +93,9 @@ Header Files + + Header Files + Header Files @@ -105,6 +108,12 @@ Header Files + + Header Files + + + Header Files + @@ -173,6 +182,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp index c5e7a44625..708054e920 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.cpp @@ -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}"); diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h index a78557cde2..108608ead1 100644 --- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h +++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWinHookEventIDs.h @@ -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 diff --git a/src/modules/fancyzones/FancyZonesLib/JsonHelpers.cpp b/src/modules/fancyzones/FancyZonesLib/JsonHelpers.cpp index 125bf325e9..0d19491b9e 100644 --- a/src/modules/fancyzones/FancyZonesLib/JsonHelpers.cpp +++ b/src/modules/fancyzones/FancyZonesLib/JsonHelpers.cpp @@ -1,12 +1,13 @@ #include "pch.h" #include "JsonHelpers.h" -#include "FancyZonesData.h" #include "FancyZonesDataTypes.h" #include "trace.h" #include "util.h" +#include #include +#include #include #include @@ -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 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) diff --git a/src/modules/fancyzones/FancyZonesLib/JsonHelpers.h b/src/modules/fancyzones/FancyZonesLib/JsonHelpers.h index 5cf0e5439b..0bfe583f60 100644 --- a/src/modules/fancyzones/FancyZonesLib/JsonHelpers.h +++ b/src/modules/fancyzones/FancyZonesLib/JsonHelpers.h @@ -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 ParseDeviceInfos(const json::JsonObject& fancyZonesDataJSON); + void SaveAppliedLayouts(const TDeviceInfoMap& deviceInfoMap); // replace zones-settings: layout hotkeys std::optional ParseQuickKeys(const json::JsonObject& fancyZonesDataJSON); diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp index 28a9a4326b..d296820b2b 100644 --- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp +++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp @@ -4,7 +4,7 @@ #include #include -#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); } } } diff --git a/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp b/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp index a4c19e7a32..6357e6a629 100644 --- a/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp +++ b/src/modules/fancyzones/FancyZonesLib/ZoneSet.cpp @@ -2,7 +2,6 @@ #include "ZoneSet.h" -#include "FancyZonesData.h" #include #include "FancyZonesDataTypes.h" #include "FancyZonesWindowProperties.h" diff --git a/src/modules/fancyzones/FancyZonesLib/trace.cpp b/src/modules/fancyzones/FancyZonesLib/trace.cpp index 1c8fa3f091..4d74bcc516 100644 --- a/src/modules/fancyzones/FancyZonesLib/trace.cpp +++ b/src/modules/fancyzones/FancyZonesLib/trace.cpp @@ -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(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 customZonesArray(new (std::nothrow) INT32[customZones.size()]); @@ -157,7 +156,7 @@ void Trace::FancyZones::DataChanged() noexcept return; } - auto getCustomZoneCount = [&data](const std::variant& layoutInfo) -> int { + auto getCustomZoneCount = [](const std::variant& layoutInfo) -> int { if (std::holds_alternative(layoutInfo)) { const auto& info = std::get(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(customZones.size()), CustomZoneSetCountKey), TraceLoggingInt32Array(customZonesArray.get(), static_cast(customZones.size()), NumberOfZonesForEachCustomZoneSetKey), - TraceLoggingInt32(static_cast(devices.size()), ActiveZoneSetsCountKey), + TraceLoggingInt32(static_cast(layouts.size()), ActiveZoneSetsCountKey), TraceLoggingWideString(activeZoneSetInfo.c_str(), ActiveZoneSetsListKey), TraceLoggingInt32(static_cast(quickKeysCount), LayoutUsingQuickKeyCountKey)); } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp new file mode 100644 index 0000000000..5d899ada98 --- /dev/null +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp @@ -0,0 +1,374 @@ +#include "pch.h" +#include + +#include +#include +#include + +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()); + } + }; +} \ No newline at end of file diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp index aa15679ad9..1731dbb62d 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/CustomLayoutsTests.Spec.cpp @@ -96,7 +96,6 @@ namespace FancyZonesUnitTests TEST_METHOD_INITIALIZE(Init) { - m_fzData.clear_data(); m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests"); } diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp index 9228245268..cfc160d751 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/FancyZones.Spec.cpp @@ -3,9 +3,10 @@ #include #include -#include #include +#include + #include "util.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -63,8 +64,6 @@ namespace FancyZonesUnitTests winrt::com_ptr m_settings = nullptr; winrt::com_ptr 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(); - Assert::IsTrue(m_fzCallback != nullptr); + m_fzCallback = fancyZones.as(); + 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) { diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/JsonHelpers.Tests.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/JsonHelpers.Tests.cpp index 04bead184a..ba49b2ea2d 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/JsonHelpers.Tests.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/JsonHelpers.Tests.cpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include #include #include #include @@ -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{ 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) diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutHotkeysTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutHotkeysTests.Spec.cpp index e1eb49e6ec..e10c0b36b3 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutHotkeysTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutHotkeysTests.Spec.cpp @@ -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) diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutTemplatesTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutTemplatesTests.Spec.cpp index f5951462b6..6c06097230 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutTemplatesTests.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/LayoutTemplatesTests.Spec.cpp @@ -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) diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj index 7bde9eb25b..54b1173b39 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj @@ -41,6 +41,7 @@ + diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters index cb3fe292e9..8026f74576 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj.filters @@ -54,6 +54,9 @@ Source Files + + Source Files + diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp index 972bdf5bac..c80a64afb8 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkArea.Spec.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -35,8 +35,6 @@ namespace FancyZonesUnitTests OverlappingZonesAlgorithm m_overlappingAlgorithm = OverlappingZonesAlgorithm::Positional; bool m_showZoneText = true; - FancyZonesData& m_fancyZonesData = FancyZonesDataInstance(); - void testWorkArea(winrt::com_ptr 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(ZoneSetLayoutType::PriorityGrid), static_cast(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(ZoneSetLayoutType::PriorityGrid), static_cast(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: diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/ZoneSet.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/ZoneSet.Spec.cpp index 391c6f43c5..f30895e611 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/ZoneSet.Spec.cpp +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/ZoneSet.Spec.cpp @@ -1,5 +1,5 @@ #include "pch.h" -#include "FancyZonesLib\FancyZonesData.h" +#include #include "FancyZonesLib\FancyZonesDataTypes.h" #include "FancyZonesLib\JsonHelpers.h" #include "FancyZonesLib\VirtualDesktop.h" diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs index fcea50dd0d..ad05ff7983 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/App.xaml.cs @@ -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; diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs index 065204db33..6bd3f3b652 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/EditorWindow.cs @@ -33,6 +33,7 @@ namespace FancyZonesEditor App.FancyZonesEditorIO.SerializeLayoutTemplates(); App.FancyZonesEditorIO.SerializeCustomLayouts(); + App.FancyZonesEditorIO.SerializeAppliedLayouts(); Close(); } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs index cef5762889..92a3b683dc 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/MainWindow.xaml.cs @@ -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(); } diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs index 8256cf6803..6d55120876 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.Designer.cs @@ -375,6 +375,15 @@ namespace FancyZonesEditor.Properties { } } + /// + /// Looks up a localized string similar to An error occurred while parsing applied layouts.. + /// + public static string Error_Parsing_Applied_Layouts_Message { + get { + return ResourceManager.GetString("Error_Parsing_Applied_Layouts_Message", resourceCulture); + } + } + /// /// Looks up a localized string similar to An error occurred while parsing custom layouts.. /// @@ -384,6 +393,15 @@ namespace FancyZonesEditor.Properties { } } + /// + /// Looks up a localized string similar to Editor data parsing error.. + /// + public static string Error_Parsing_Data_Title { + get { + return ResourceManager.GetString("Error_Parsing_Data_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Error parsing device info data.. /// @@ -411,24 +429,6 @@ namespace FancyZonesEditor.Properties { } } - /// - /// Looks up a localized string similar to A layout that contained invalid data has been removed.. - /// - public static string Error_Parsing_Zones_Settings_Message { - get { - return ResourceManager.GetString("Error_Parsing_Zones_Settings_Message", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Editor data parsing error.. - /// - public static string Error_Parsing_Zones_Settings_Title { - get { - return ResourceManager.GetString("Error_Parsing_Zones_Settings_Title", resourceCulture); - } - } - /// /// Looks up a localized string similar to Error persisting custom layout. /// diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx index 488e2640ae..a353b38b90 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Properties/Resources.resx @@ -304,11 +304,11 @@ Delete zone A tooltip on a button that allows the user to delete a zone - + Editor data parsing error. - - A layout that contained invalid data has been removed. + + An error occurred while parsing applied layouts. Apply layout diff --git a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs index 02f6130644..4238c8f580 100644 --- a/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs +++ b/src/modules/fancyzones/editor/FancyZonesEditor/Utils/FancyZonesEditorIO.cs @@ -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 _unusedDevices = new List(); + private List _unusedLayouts = new List(); - 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 AppliedLayouts { get; set; } } // custom-layouts.json @@ -228,12 +235,6 @@ namespace FancyZonesEditor.Utils public List LayoutHotkeys { get; set; } } - // zones-settings - private struct ZoneSettingsWrapper - { - public List 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(settingsString, _options); + settingsString = ReadFile(FancyZonesAppliedLayoutsFile); + appliedLayouts = JsonSerializer.Deserialize(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(); + AppliedLayoutsListWrapper layouts = new AppliedLayoutsListWrapper { }; + layouts.AppliedLayouts = new List(); - // 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 devices) + private bool SetAppliedLayouts(List 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); } }