diff --git a/Directory.Packages.props b/Directory.Packages.props
index fd80a3230c..b870164a8b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -40,7 +40,7 @@
-
+
diff --git a/NOTICE.md b/NOTICE.md
index ec5753366d..9a53e853e1 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -341,7 +341,7 @@ SOFTWARE.
- Microsoft.Windows.CsWinRT 2.0.3
- Microsoft.Windows.SDK.BuildTools 10.0.22621.756
- Microsoft.Windows.SDK.Contracts 10.0.19041.1
-- Microsoft.WindowsAppSDK 1.4.230822000
+- Microsoft.WindowsAppSDK 1.4.230913002
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
- ModernWpfUI 0.9.4
diff --git a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml
index e889c0381e..f62eef0a98 100644
--- a/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml
+++ b/src/modules/Hosts/Hosts/HostsXAML/Views/MainPage.xaml
@@ -199,7 +199,7 @@
+ IsEnabled="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
@@ -207,7 +207,7 @@
+ IsEnabled="{x:Bind ViewModel.Filtered, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}">
diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
index 4271e03a55..ad51f8998b 100644
--- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
+++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj
@@ -1,6 +1,6 @@
-
+
@@ -147,7 +147,7 @@
-
+
@@ -158,7 +158,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config
index 817fac0812..ca3592e5b0 100644
--- a/src/modules/MeasureTool/MeasureToolCore/packages.config
+++ b/src/modules/MeasureTool/MeasureToolCore/packages.config
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp b/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp
index 26397eb1ba..b7a7ea4523 100644
--- a/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/EditorParameters.cpp
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
#include
#include
@@ -36,6 +37,31 @@ namespace JsonUtils
int monitorHeight{};
bool isSelected = false;
+ bool FillFromWorkArea(const WorkArea* const workArea)
+ {
+ const auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(workArea->UniqueId().virtualDesktopId);
+ if (!virtualDesktopIdStr)
+ {
+ return false;
+ }
+
+ const auto& monitorId = workArea->UniqueId().monitorId;
+ monitorName = monitorId.deviceId.id;
+ monitorInstanceId = monitorId.deviceId.instanceId;
+ monitorNumber = monitorId.deviceId.number;
+ monitorSerialNumber = monitorId.serialNumber;
+
+ const auto& rect = workArea->GetWorkAreaRect();
+ top = rect.top();
+ left = rect.left();
+ workAreaWidth = rect.width();
+ workAreaHeight = rect.height();
+
+ virtualDesktop = virtualDesktopIdStr.value();
+
+ return true;
+ }
+
static json::JsonObject ToJson(const MonitorInfo& monitor)
{
json::JsonObject result{};
@@ -84,21 +110,8 @@ namespace JsonUtils
};
}
-bool EditorParameters::Save() noexcept
+bool EditorParameters::Save(const WorkAreaConfiguration& configuration, OnThreadExecutor& dpiUnawareThread) noexcept
{
- const auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(VirtualDesktop::instance().GetCurrentVirtualDesktopId());
- if (!virtualDesktopIdStr)
- {
- Logger::error(L"Save editor params: invalid virtual desktop id");
- return false;
- }
-
- OnThreadExecutor dpiUnawareThread;
- dpiUnawareThread.submit(OnThreadExecutor::task_t{ [] {
- SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
- SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
- } }).wait();
-
const bool spanZonesAcrossMonitors = FancyZonesSettings::settings().spanZonesAcrossMonitors;
JsonUtils::EditorArgs argsJson;
@@ -107,23 +120,31 @@ bool EditorParameters::Save() noexcept
if (spanZonesAcrossMonitors)
{
- RECT combinedWorkArea;
- dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&]() {
- combinedWorkArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
-
- } }).wait();
-
- RECT combinedMonitorArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcMonitor>();
+ const auto& workArea = configuration.GetWorkArea(nullptr);
+ if (!workArea)
+ {
+ return false;
+ }
JsonUtils::MonitorInfo monitorJson;
- monitorJson.monitorName = ZonedWindowProperties::MultiMonitorName;
- monitorJson.monitorInstanceId = ZonedWindowProperties::MultiMonitorInstance;
- monitorJson.monitorNumber = 0;
- monitorJson.virtualDesktop = virtualDesktopIdStr.value();
+ if (!monitorJson.FillFromWorkArea(workArea))
+ {
+ return false;
+ }
+
+ RECT combinedWorkArea;
+ dpiUnawareThread.submit(OnThreadExecutor::task_t{
+ [&]() {
+ combinedWorkArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
+ } }).wait();
+ RECT combinedMonitorArea = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcMonitor>();
+
+ // use dpi-unaware values
monitorJson.top = combinedWorkArea.top;
monitorJson.left = combinedWorkArea.left;
monitorJson.workAreaWidth = combinedWorkArea.right - combinedWorkArea.left;
monitorJson.workAreaHeight = combinedWorkArea.bottom - combinedWorkArea.top;
+
monitorJson.monitorWidth = combinedMonitorArea.right - combinedMonitorArea.left;
monitorJson.monitorHeight = combinedMonitorArea.bottom - combinedMonitorArea.top;
monitorJson.isSelected = true;
@@ -133,8 +154,6 @@ bool EditorParameters::Save() noexcept
}
else
{
- auto monitors = MonitorUtils::IdentifyMonitors();
-
HMONITOR targetMonitor{};
if (FancyZonesSettings::settings().use_cursorpos_editor_startupscreen)
{
@@ -153,10 +172,25 @@ bool EditorParameters::Save() noexcept
return false;
}
- for (auto& monitorData : monitors)
+ const auto& config = configuration.GetAllWorkAreas();
+ for (auto& [monitor, workArea] : config)
{
- HMONITOR monitor = monitorData.monitor;
+ JsonUtils::MonitorInfo monitorJson;
+ if (!monitorJson.FillFromWorkArea(workArea.get()))
+ {
+ continue;
+ }
+ monitorJson.isSelected = monitor == targetMonitor; /* Is monitor selected for the main editor window opening */
+
+ UINT dpi = 0;
+ if (DPIAware::GetScreenDPIForMonitor(monitor, dpi) != S_OK)
+ {
+ continue;
+ }
+
+ monitorJson.dpi = dpi;
+
MONITORINFOEX monitorInfo{};
dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
monitorInfo.cbSize = sizeof(monitorInfo);
@@ -166,31 +200,6 @@ bool EditorParameters::Save() noexcept
}
} }).wait();
- JsonUtils::MonitorInfo monitorJson;
-
- if (monitor == targetMonitor)
- {
- monitorJson.isSelected = true; /* Is monitor selected for the main editor window opening */
- }
-
- monitorJson.monitorName = monitorData.deviceId.id;
- monitorJson.monitorInstanceId = monitorData.deviceId.instanceId;
- monitorJson.monitorNumber = monitorData.deviceId.number;
- monitorJson.monitorSerialNumber = monitorData.serialNumber;
- monitorJson.virtualDesktop = virtualDesktopIdStr.value();
-
- UINT dpi = 0;
- if (DPIAware::GetScreenDPIForMonitor(monitor, dpi) != S_OK)
- {
- continue;
- }
-
- monitorJson.dpi = dpi;
- monitorJson.top = monitorInfo.rcWork.top;
- monitorJson.left = monitorInfo.rcWork.left;
- monitorJson.workAreaWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
- monitorJson.workAreaHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
-
float width = static_cast(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
float height = static_cast(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
DPIAware::Convert(monitor, width, height);
@@ -198,6 +207,12 @@ bool EditorParameters::Save() noexcept
monitorJson.monitorWidth = static_cast(std::roundf(width));
monitorJson.monitorHeight = static_cast(std::roundf(height));
+ // use dpi-unaware values
+ monitorJson.top = monitorInfo.rcWork.top;
+ monitorJson.left = monitorInfo.rcWork.left;
+ monitorJson.workAreaWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
+ monitorJson.workAreaHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
+
argsJson.monitors.emplace_back(std::move(monitorJson));
}
}
diff --git a/src/modules/fancyzones/FancyZonesLib/EditorParameters.h b/src/modules/fancyzones/FancyZonesLib/EditorParameters.h
index 70124613d6..74415e9c28 100644
--- a/src/modules/fancyzones/FancyZonesLib/EditorParameters.h
+++ b/src/modules/fancyzones/FancyZonesLib/EditorParameters.h
@@ -1,5 +1,8 @@
#pragma once
+#include
+#include
+
namespace NonLocalizable
{
namespace EditorParametersIds
@@ -26,5 +29,5 @@ namespace NonLocalizable
class EditorParameters
{
public:
- static bool Save() noexcept;
+ static bool Save(const WorkAreaConfiguration& configuration, OnThreadExecutor& dpiUnawareThread) noexcept;
};
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp
index 76f99175d5..d2314d7816 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZones.cpp
@@ -15,20 +15,22 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
+#include
#include
#include
#include
+#include
enum class DisplayChangeType
{
@@ -87,6 +89,7 @@ public:
AppliedLayouts::instance().LoadData();
AppZoneHistory::instance().LoadData();
DefaultLayouts::instance().LoadData();
+ LastUsedVirtualDesktop::instance().LoadData();
}
// IFancyZones
@@ -169,7 +172,7 @@ private:
HWND m_window{};
std::unique_ptr m_windowMouseSnapper{};
WindowKeyboardSnap m_windowKeyboardSnapper{};
- MonitorWorkAreaMap m_workAreaHandler;
+ WorkAreaConfiguration m_workAreaConfiguration;
DraggingState m_draggingState;
wil::unique_handle m_terminateEditorEvent; // Handle of FancyZonesEditor.exe we launch and wait on
@@ -253,6 +256,7 @@ FancyZones::Run() noexcept
}
});
+ VirtualDesktop::instance().UpdateVirtualDesktopId();
SyncVirtualDesktops();
// id format of applied-layouts and app-zone-history was changed in 0.60
@@ -267,7 +271,7 @@ FancyZones::Run() noexcept
IFACEMETHODIMP_(void)
FancyZones::Destroy() noexcept
{
- m_workAreaHandler.Clear();
+ m_workAreaConfiguration.Clear();
BufferedPaintUnInit();
if (m_window)
{
@@ -289,7 +293,7 @@ FancyZones::VirtualDesktopChanged() noexcept
void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
{
- m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaHandler.GetAllWorkAreas());
+ m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaConfiguration.GetAllWorkAreas());
if (m_windowMouseSnapper)
{
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
@@ -329,7 +333,7 @@ void FancyZones::MoveSizeEnd()
bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
{
- const auto& workAreas = m_workAreaHandler.GetAllWorkAreas();
+ const auto& workAreas = m_workAreaConfiguration.GetAllWorkAreas();
WorkArea* workArea{ nullptr };
ZoneIndexSet indexes{};
@@ -397,12 +401,6 @@ void FancyZones::WindowCreated(HWND window) noexcept
return;
}
- const bool isCandidateForLastKnownZone = FancyZonesWindowUtils::IsCandidateForZoning(window);
- if (!isCandidateForLastKnownZone)
- {
- return;
- }
-
HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
HMONITOR active = primary;
@@ -523,7 +521,7 @@ void FancyZones::ToggleEditor() noexcept
m_terminateEditorEvent.reset(CreateEvent(nullptr, true, false, nullptr));
- if (!EditorParameters::Save())
+ if (!EditorParameters::Save(m_workAreaConfiguration, m_dpiUnawareThread))
{
Logger::error(L"Failed to save editor startup parameters");
return;
@@ -623,11 +621,11 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
// Check whether Alt is used in the shortcut key combination
if (GetAsyncKeyState(VK_MENU) & 0x8000)
{
- m_windowKeyboardSnapper.Extend(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaHandler.GetAllWorkAreas());
+ m_windowKeyboardSnapper.Extend(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaConfiguration.GetAllWorkAreas());
}
else
{
- m_windowKeyboardSnapper.Snap(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaHandler.GetAllWorkAreas(), monitors);
+ m_windowKeyboardSnapper.Snap(foregroundWindow, windowRect, monitor, static_cast(lparam), m_workAreaConfiguration.GetAllWorkAreas(), monitors);
}
}
else
@@ -637,7 +635,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
}
else
{
- m_windowKeyboardSnapper.Snap(foregroundWindow, monitor, static_cast(lparam), m_workAreaHandler.GetAllWorkAreas(), FancyZonesUtils::GetMonitorsOrdered());
+ m_windowKeyboardSnapper.Snap(foregroundWindow, monitor, static_cast(lparam), m_workAreaConfiguration.GetAllWorkAreas(), FancyZonesUtils::GetMonitorsOrdered());
}
}
else if (message == WM_PRIV_INIT)
@@ -732,6 +730,7 @@ void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
updateWindowsPositions = FancyZonesSettings::settings().displayChange_moveWindows;
break;
case DisplayChangeType::VirtualDesktop: // Switched virtual desktop
+ SyncVirtualDesktops();
break;
case DisplayChangeType::Initialization: // Initialization
updateWindowsPositions = FancyZonesSettings::settings().zoneSetChange_moveWindows;
@@ -760,15 +759,18 @@ bool FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr
{
rect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFO::rcWork>();
}
-
- auto workArea = WorkArea::Create(m_hinstance, id, m_workAreaHandler.GetParent(monitor), rect);
+
+ auto parentWorkAreaId = id;
+ parentWorkAreaId.virtualDesktopId = LastUsedVirtualDesktop::instance().GetId();
+
+ auto workArea = WorkArea::Create(m_hinstance, id, parentWorkAreaId, rect);
if (!workArea)
{
Logger::error(L"Failed to create work area {}", id.toString());
return false;
}
- m_workAreaHandler.AddWorkArea(monitor, std::move(workArea));
+ m_workAreaConfiguration.AddWorkArea(monitor, std::move(workArea));
return true;
}
@@ -790,8 +792,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
{
Logger::debug(L"Update work areas, update windows positions: {}", updateWindowPositions);
- m_workAreaHandler.SaveParentIds();
- m_workAreaHandler.Clear();
+ m_workAreaConfiguration.Clear();
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
{
@@ -829,7 +830,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
if (FancyZonesSettings::settings().spanZonesAcrossMonitors) // one work area across monitors
{
- const auto workArea = m_workAreaHandler.GetWorkArea(nullptr);
+ const auto workArea = m_workAreaConfiguration.GetWorkArea(nullptr);
if (workArea)
{
for (const auto& [window, zones] : windowsToSnap)
@@ -846,7 +847,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
const auto window = iter->first;
const auto zones = iter->second;
const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
- const auto workAreaForMonitor = m_workAreaHandler.GetWorkArea(monitor);
+ const auto workAreaForMonitor = m_workAreaConfiguration.GetWorkArea(monitor);
if (workAreaForMonitor && AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaForMonitor->UniqueId(), workAreaForMonitor->GetLayoutId()) == zones)
{
workAreaForMonitor->Snap(window, zones, false);
@@ -861,7 +862,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
// snap rest of the windows to other work areas (in case they were moved after the monitor unplug)
for (const auto& [window, zones] : windowsToSnap)
{
- for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
+ for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas())
{
const auto savedIndexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId());
if (savedIndexes == zones)
@@ -874,7 +875,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
if (updateWindowPositions)
{
- for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
+ for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas())
{
if (workArea)
{
@@ -889,7 +890,7 @@ void FancyZones::CycleWindows(bool reverse) noexcept
auto window = GetForegroundWindow();
HMONITOR current = WorkAreaKeyFromWindow(window);
- auto workArea = m_workAreaHandler.GetWorkArea(current);
+ auto workArea = m_workAreaConfiguration.GetWorkArea(current);
if (workArea)
{
workArea->CycleWindows(window, reverse);
@@ -898,15 +899,23 @@ void FancyZones::CycleWindows(bool reverse) noexcept
void FancyZones::SyncVirtualDesktops() noexcept
{
+ // 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).
+
+ auto lastUsed = LastUsedVirtualDesktop::instance().GetId();
+ auto current = VirtualDesktop::instance().GetCurrentVirtualDesktopId();
auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry();
- if (guids.has_value())
+
+ if (current != lastUsed)
{
- AppZoneHistory::instance().RemoveDeletedVirtualDesktops(*guids);
- AppliedLayouts::instance().RemoveDeletedVirtualDesktops(*guids);
+ LastUsedVirtualDesktop::instance().SetId(current);
+ LastUsedVirtualDesktop::instance().SaveData();
}
- AppZoneHistory::instance().SyncVirtualDesktops();
- AppliedLayouts::instance().SyncVirtualDesktops();
+ AppliedLayouts::instance().SyncVirtualDesktops(current, lastUsed, guids);
+ AppZoneHistory::instance().SyncVirtualDesktops(current, lastUsed, guids);
}
void FancyZones::UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept
@@ -960,7 +969,7 @@ void FancyZones::SettingsUpdate(SettingId id)
break;
case SettingId::SpanZonesAcrossMonitors:
{
- m_workAreaHandler.Clear();
+ m_workAreaConfiguration.Clear();
PostMessageW(m_window, WM_PRIV_INIT, NULL, NULL);
}
break;
@@ -971,7 +980,7 @@ void FancyZones::SettingsUpdate(SettingId id)
void FancyZones::RefreshLayouts() noexcept
{
- for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
+ for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas())
{
if (workArea)
{
@@ -994,11 +1003,11 @@ bool FancyZones::ShouldProcessSnapHotkey(DWORD vkCode) noexcept
return false;
}
- if (FancyZonesSettings::settings().overrideSnapHotkeys && FancyZonesWindowUtils::IsCandidateForZoning(window))
+ if (FancyZonesSettings::settings().overrideSnapHotkeys)
{
HMONITOR monitor = WorkAreaKeyFromWindow(window);
- auto workArea = m_workAreaHandler.GetWorkArea(monitor);
+ auto workArea = m_workAreaConfiguration.GetWorkArea(monitor);
if (!workArea)
{
Logger::error(L"No work area for processing snap hotkey");
@@ -1043,13 +1052,15 @@ void FancyZones::ApplyQuickLayout(int key) noexcept
return;
}
- auto workArea = m_workAreaHandler.GetWorkAreaFromCursor();
+ auto workArea = m_workAreaConfiguration.GetWorkAreaFromCursor();
if (workArea)
{
- AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value());
- AppliedLayouts::instance().SaveData();
- RefreshLayouts();
- FlashZones();
+ if (AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()))
+ {
+ RefreshLayouts();
+ FlashZones();
+ AppliedLayouts::instance().SaveData();
+ }
}
}
@@ -1057,7 +1068,7 @@ void FancyZones::FlashZones() noexcept
{
if (FancyZonesSettings::settings().flashZonesOnQuickSwitch && !m_draggingState.IsDragging())
{
- for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
+ for (const auto& [_, workArea] : m_workAreaConfiguration.GetAllWorkAreas())
{
if (workArea)
{
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h
index 565abb3194..db7fe004a9 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData.h
@@ -26,13 +26,7 @@ public:
return settingsFileName;
}
-private:
#if defined(UNIT_TESTS)
- friend class FancyZonesUnitTests::LayoutHotkeysUnitTests;
- friend class FancyZonesUnitTests::LayoutTemplatesUnitTests;
- friend class FancyZonesUnitTests::CustomLayoutsUnitTests;
- friend class FancyZonesUnitTests::AppliedLayoutsUnitTests;
-
inline void SetSettingsModulePath(std::wstring_view moduleName)
{
std::wstring result = PTSettingsHelper::get_module_save_folder_location(moduleName);
@@ -46,6 +40,8 @@ private:
return result + L"\\" + std::wstring(L"zones-settings.json");
}
#endif
+
+private:
std::wstring settingsFileName;
std::wstring zonesSettingsFileName;
std::wstring appZoneHistoryFileName;
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp
index 886dec5a84..c633f0c0f7 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.cpp
@@ -591,67 +591,43 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
return {};
}
-void AppZoneHistory::SyncVirtualDesktops()
+void AppZoneHistory::SyncVirtualDesktops(const GUID& currentVirtualDesktop, const GUID& lastUsedVirtualDesktop, std::optional> desktops)
{
- // 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).
-
- auto savedInRegistryVirtualDesktopID = VirtualDesktop::instance().GetCurrentVirtualDesktopIdFromRegistry();
- if (!savedInRegistryVirtualDesktopID.has_value() || savedInRegistryVirtualDesktopID.value() == GUID_NULL)
+ TAppZoneHistoryMap history;
+
+ std::unordered_set activeDesktops{};
+ if (desktops.has_value())
{
- return;
+ activeDesktops = std::unordered_set(std::begin(desktops.value()), std::end(desktops.value()));
}
- auto currentVirtualDesktopStr = FancyZonesUtils::GuidToString(savedInRegistryVirtualDesktopID.value());
- if (!currentVirtualDesktopStr.has_value())
- {
- Logger::error(L"Failed to convert virtual desktop GUID to string");
- return;
- }
-
- Logger::info(L"AppZoneHistory Sync virtual desktops: current {}", currentVirtualDesktopStr.value());
-
- bool dirtyFlag = false;
-
- for (auto& [path, perDesktopData] : m_history)
- {
- for (auto& data : perDesktopData)
+ auto findCurrentVirtualDesktopInSavedHistory = [&](const std::pair>& val) -> bool
+ {
+ for (auto& data : val.second)
{
- if (data.workAreaId.virtualDesktopId == GUID_NULL)
+ if (data.workAreaId.virtualDesktopId == currentVirtualDesktop)
{
- data.workAreaId.virtualDesktopId = savedInRegistryVirtualDesktopID.value();
- dirtyFlag = true;
+ return true;
}
}
- }
+ return false;
+ };
+ bool replaceLastUsedWithCurrent = !desktops.has_value() || currentVirtualDesktop == GUID_NULL || lastUsedVirtualDesktop == GUID_NULL || std::find_if(m_history.begin(), m_history.end(), findCurrentVirtualDesktopInSavedHistory) == m_history.end();
- if (dirtyFlag)
- {
- Logger::info(L"Update Virtual Desktop id to {}", currentVirtualDesktopStr.value());
- 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->workAreaId.virtualDesktopId != GUID_NULL && !active.contains(desktopIt->workAreaId.virtualDesktopId))
+ if (replaceLastUsedWithCurrent && desktopIt->workAreaId.virtualDesktopId == lastUsedVirtualDesktop)
{
- auto virtualDesktopIdStr = FancyZonesUtils::GuidToString(desktopIt->workAreaId.virtualDesktopId);
- if (virtualDesktopIdStr)
- {
- Logger::info(L"Remove Virtual Desktop id {} from app-zone-history", virtualDesktopIdStr.value());
- }
+ desktopIt->workAreaId.virtualDesktopId = currentVirtualDesktop;
+ dirtyFlag = true;
+ }
+ if (desktopIt->workAreaId.virtualDesktopId != currentVirtualDesktop && !activeDesktops.contains(desktopIt->workAreaId.virtualDesktopId))
+ {
desktopIt = perDesktopData.erase(desktopIt);
dirtyFlag = true;
}
@@ -664,6 +640,7 @@ void AppZoneHistory::RemoveDeletedVirtualDesktops(const std::vector& activ
if (perDesktopData.empty())
{
it = m_history.erase(it);
+ dirtyFlag = true;
}
else
{
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h
index 18ec84ac93..6356ab50fa 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppZoneHistory.h
@@ -41,6 +41,13 @@ public:
#endif
}
+#if defined(UNIT_TESTS)
+ inline void SetAppZoneHistory(const TAppZoneHistoryMap& history)
+ {
+ m_history = history;
+ }
+#endif
+
void LoadData();
void SaveData();
void AdjustWorkAreaIds(const std::vector& ids);
@@ -56,9 +63,8 @@ public:
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept;
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId) const;
- void SyncVirtualDesktops();
- void RemoveDeletedVirtualDesktops(const std::vector& activeDesktops);
-
+ void SyncVirtualDesktops(const GUID& currentVirtualDesktop, const GUID& lastUsedVirtualDesktop, std::optional> desktops);
+
private:
AppZoneHistory();
~AppZoneHistory() = default;
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp
index a6c86952d6..b05cd3da0b 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.cpp
@@ -279,29 +279,7 @@ void AppliedLayouts::LoadData()
void AppliedLayouts::SaveData()
{
- bool dirtyFlag = false;
- TAppliedLayoutsMap updatedMap;
-
- for (const auto& [id, data] : m_layouts)
- {
- auto updatedId = id;
- if (!VirtualDesktop::instance().IsVirtualDesktopIdSavedInRegistry(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));
- }
+ json::to_file(AppliedLayoutsFileName(), JsonUtils::SerializeJson(m_layouts));
}
void AppliedLayouts::AdjustWorkAreaIds(const std::vector& ids)
@@ -344,86 +322,75 @@ void AppliedLayouts::AdjustWorkAreaIds(const std::vector> desktops)
{
- // 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).
+ TAppliedLayoutsMap layouts;
- auto savedInRegistryVirtualDesktopID = VirtualDesktop::instance().GetCurrentVirtualDesktopIdFromRegistry();
- if (!savedInRegistryVirtualDesktopID.has_value() || savedInRegistryVirtualDesktopID.value() == GUID_NULL)
+ auto findCurrentVirtualDesktopInSavedLayouts = [&](const std::pair& val) -> bool { return val.first.virtualDesktopId == currentVirtualDesktop; };
+ bool replaceLastUsedWithCurrent = !desktops.has_value() || currentVirtualDesktop == GUID_NULL ||
+ std::find_if(m_layouts.begin(), m_layouts.end(), findCurrentVirtualDesktopInSavedLayouts) == m_layouts.end();
+ bool copyToOtherVirtualDesktops = lastUsedVirtualDesktop == GUID_NULL && currentVirtualDesktop != GUID_NULL && desktops.has_value();
+
+ for (const auto& [workAreaId, layout] : m_layouts)
{
- return;
- }
-
- auto currentVirtualDesktopStr = FancyZonesUtils::GuidToString(savedInRegistryVirtualDesktopID.value());
- if (!currentVirtualDesktopStr.has_value())
- {
- Logger::error(L"Failed to convert virtual desktop GUID to string");
- return;
- }
-
- Logger::info(L"AppliedLayouts Sync virtual desktops: current {}", currentVirtualDesktopStr.value());
-
- bool dirtyFlag = false;
-
- std::vector replaceWithCurrentId{};
-
- for (const auto& [id, data] : m_layouts)
- {
- if (id.virtualDesktopId == GUID_NULL)
+ if (replaceLastUsedWithCurrent && workAreaId.virtualDesktopId == lastUsedVirtualDesktop)
{
- replaceWithCurrentId.push_back(id);
- dirtyFlag = true;
- }
- }
+ // replace "lastUsedVirtualDesktop" with "currentVirtualDesktop"
+ auto updatedWorkAreaId = workAreaId;
+ updatedWorkAreaId.virtualDesktopId = currentVirtualDesktop;
+ layouts.insert({ updatedWorkAreaId, layout });
- for (const auto& id : replaceWithCurrentId)
- {
- auto mapEntry = m_layouts.extract(id);
- mapEntry.key().virtualDesktopId = savedInRegistryVirtualDesktopID.value();
- m_layouts.insert(std::move(mapEntry));
- }
-
- if (dirtyFlag)
- {
- Logger::info(L"Update Virtual Desktop id to {}", currentVirtualDesktopStr.value());
- 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))
+ if (copyToOtherVirtualDesktops)
{
- wil::unique_cotaskmem_string virtualDesktopIdStr;
- if (SUCCEEDED(StringFromCLSID(desktopId, &virtualDesktopIdStr)))
+ // Copy to other virtual desktops on the 1st VD creation.
+ // If we just replace the id, we'll lose the layout on the other desktops.
+ // Usage scenario:
+ // apply the layout to the single virtual desktop with id = GUID_NULL,
+ // create the 2nd virtual desktop and switch to it,
+ // so virtual desktop id changes from GUID_NULL to a valid value of the 2nd VD.
+ // Then change the layout on the 2nd VD and switch back to the 1st VD.
+ // Layout on the initial VD will be changed too without initializing it beforehand.
+ for (const auto& id : desktops.value())
{
- Logger::info(L"Remove Virtual Desktop id {}", virtualDesktopIdStr.get());
+ if (id != currentVirtualDesktop)
+ {
+ auto copyWorkAreaId = workAreaId;
+ copyWorkAreaId.virtualDesktopId = id;
+ layouts.insert({ copyWorkAreaId, layout });
+ }
}
-
- it = m_layouts.erase(it);
- dirtyFlag = true;
- continue;
}
}
- ++it;
+
+ if (workAreaId.virtualDesktopId == currentVirtualDesktop || (desktops.has_value() &&
+ std::find(desktops.value().begin(), desktops.value().end(), workAreaId.virtualDesktopId) != desktops.value().end()))
+ {
+ // keep only actual virtual desktop values
+ layouts.insert({ workAreaId, layout });
+ }
}
- if (dirtyFlag)
+ if (layouts != m_layouts)
{
+ m_layouts = layouts;
SaveData();
+
+ std::wstring currentStr = FancyZonesUtils::GuidToString(currentVirtualDesktop).value_or(L"incorrect guid");
+ std::wstring lastUsedStr = FancyZonesUtils::GuidToString(lastUsedVirtualDesktop).value_or(L"incorrect guid");
+ std::wstring registryStr{};
+ if (desktops.has_value())
+ {
+ for (const auto& id : desktops.value())
+ {
+ registryStr += FancyZonesUtils::GuidToString(id).value_or(L"incorrect guid") + L" ";
+ }
+ }
+ else
+ {
+ registryStr = L"empty";
+ }
+
+ Logger::info(L"Synced virtual desktops. Current: {}, last used: {}, registry: {}", currentStr, lastUsedStr, registryStr);
}
}
@@ -449,9 +416,9 @@ bool AppliedLayouts::IsLayoutApplied(const FancyZonesDataTypes::WorkAreaId& id)
return iter != m_layouts.end();
}
-bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::WorkAreaId& deviceId, LayoutData layout)
+bool AppliedLayouts::ApplyLayout(const FancyZonesDataTypes::WorkAreaId& workAreaId, LayoutData layout)
{
- m_layouts[deviceId] = std::move(layout);
+ m_layouts[workAreaId] = layout;
return true;
}
@@ -496,9 +463,5 @@ bool AppliedLayouts::CloneLayout(const FancyZonesDataTypes::WorkAreaId& srcId, c
}
Logger::info(L"Clone layout from {} to {}", dstId.toString(), srcId.toString());
- m_layouts[dstId] = m_layouts[srcId];
-
- SaveData();
-
- return true;
+ return ApplyLayout(dstId, m_layouts[srcId]);
}
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h
index bea37c6d92..e4f796913b 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/AppliedLayouts.h
@@ -49,12 +49,18 @@ public:
#endif
}
+#if defined(UNIT_TESTS)
+ inline void SetAppliedLayouts(TAppliedLayoutsMap layouts)
+ {
+ m_layouts = layouts;
+ }
+#endif
+
void LoadData();
void SaveData();
void AdjustWorkAreaIds(const std::vector& ids);
- void SyncVirtualDesktops();
- void RemoveDeletedVirtualDesktops(const std::vector& activeDesktops);
+ void SyncVirtualDesktops(const GUID& currentVirtualDesktop, const GUID& lastUsedVirtualDesktop, std::optional> desktops);
std::optional GetDeviceLayout(const FancyZonesDataTypes::WorkAreaId& id) const noexcept;
const TAppliedLayoutsMap& GetAppliedLayoutMap() const noexcept;
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp
new file mode 100644
index 0000000000..3c62df9e88
--- /dev/null
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.cpp
@@ -0,0 +1,78 @@
+#include "../pch.h"
+#include "LastUsedVirtualDesktop.h"
+
+#include
+
+#include
+
+namespace JsonUtils
+{
+ GUID ParseJson(const json::JsonObject& json)
+ {
+ auto idStr = json.GetNamedString(NonLocalizable::LastUsedVirtualDesktop::LastUsedVirtualDesktopID);
+ auto idOpt = FancyZonesUtils::GuidFromString(idStr.c_str());
+
+ if (!idOpt.has_value())
+ {
+ return {};
+ }
+
+ return idOpt.value();
+ }
+
+ json::JsonObject SerializeJson(const GUID& id)
+ {
+ json::JsonObject result{};
+
+ auto virtualDesktopStr = FancyZonesUtils::GuidToString(id);
+ if (virtualDesktopStr)
+ {
+ result.SetNamedValue(NonLocalizable::LastUsedVirtualDesktop::LastUsedVirtualDesktopID, json::value(virtualDesktopStr.value()));
+ }
+
+ return result;
+ }
+}
+
+
+LastUsedVirtualDesktop& LastUsedVirtualDesktop::instance()
+{
+ static LastUsedVirtualDesktop self;
+ return self;
+}
+
+void LastUsedVirtualDesktop::LoadData()
+{
+ auto data = json::from_file(LastUsedVirtualDesktopFileName());
+
+ try
+ {
+ if (data)
+ {
+ m_id = JsonUtils::ParseJson(data.value());
+ }
+ else
+ {
+ m_id = GUID_NULL;
+ }
+ }
+ catch (const winrt::hresult_error& e)
+ {
+ Logger::error(L"Parsing last-used-virtual-desktop error: {}", e.message());
+ }
+}
+
+void LastUsedVirtualDesktop::SaveData() const
+{
+ json::to_file(LastUsedVirtualDesktopFileName(), JsonUtils::SerializeJson(m_id));
+}
+
+GUID LastUsedVirtualDesktop::GetId() const
+{
+ return m_id;
+}
+
+void LastUsedVirtualDesktop::SetId(GUID id)
+{
+ m_id = id;
+}
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h
new file mode 100644
index 0000000000..da9e07eca4
--- /dev/null
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesData/LastUsedVirtualDesktop.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include
+
+#include
+
+namespace NonLocalizable
+{
+ namespace LastUsedVirtualDesktop
+ {
+ const static wchar_t* LastUsedVirtualDesktopID = L"last-used-virtual-desktop";
+ }
+}
+
+class LastUsedVirtualDesktop
+{
+public:
+ static LastUsedVirtualDesktop& instance();
+
+ inline static std::wstring LastUsedVirtualDesktopFileName()
+ {
+ std::wstring saveFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
+#if defined(UNIT_TESTS)
+ return saveFolderPath + L"\\test-last-used-virtual-desktop.json";
+#else
+ return saveFolderPath + L"\\last-used-virtual-desktop.json";
+#endif
+ }
+
+ void LoadData();
+ void SaveData() const;
+
+ GUID GetId() const;
+ void SetId(GUID id);
+
+private:
+ LastUsedVirtualDesktop() = default;
+ ~LastUsedVirtualDesktop() = default;
+
+ GUID m_id{};
+};
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h
index 6f61b3e125..58673c7012 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesDataTypes.h
@@ -209,14 +209,12 @@ namespace FancyZonesDataTypes
inline bool operator==(const WorkAreaId& lhs, const WorkAreaId& rhs)
{
- bool vdEqual = (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL);
- return vdEqual && lhs.monitorId == rhs.monitorId;
+ return lhs.virtualDesktopId == rhs.virtualDesktopId && lhs.monitorId == rhs.monitorId;
}
inline bool operator!=(const WorkAreaId& lhs, const WorkAreaId& rhs)
{
- bool vdEqual = (lhs.virtualDesktopId == rhs.virtualDesktopId || lhs.virtualDesktopId == GUID_NULL || rhs.virtualDesktopId == GUID_NULL);
- return !vdEqual || lhs.monitorId != rhs.monitorId;
+ return lhs.virtualDesktopId != rhs.virtualDesktopId || lhs.monitorId != rhs.monitorId;
}
inline bool operator<(const WorkAreaId& lhs, const WorkAreaId& rhs)
@@ -240,6 +238,11 @@ namespace FancyZonesDataTypes
return lhs.monitorId.deviceId < rhs.monitorId.deviceId;
}
+
+ inline bool operator==(const AppZoneHistoryData& lhs, const AppZoneHistoryData& rhs)
+ {
+ return lhs.layoutId == rhs.layoutId && lhs.workAreaId == rhs.workAreaId && lhs.zoneIndexSet == rhs.zoneIndexSet && lhs.processIdToHandleMap == rhs.processIdToHandleMap;
+ }
}
namespace std
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj
index 9bb26ccd95..faf124b3ef 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj
@@ -43,6 +43,7 @@
+
@@ -59,7 +60,7 @@
-
+
@@ -100,9 +101,13 @@
../pch.h
+
+ ../pch.h
+
../pch.h
+
@@ -114,7 +119,7 @@
-
+
Create
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters
index e8012868f2..220b0cc22f 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesLib.vcxproj.filters
@@ -54,7 +54,7 @@
Header Files
-
+
Header Files
@@ -168,6 +168,9 @@
Header Files
+
+ Header Files\FancyZonesData
+
@@ -200,7 +203,7 @@
Source Files
-
+
Source Files
@@ -272,6 +275,12 @@
Source Files
+
+ Source Files\FancyZonesData
+
+
+ Source Files
+
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp
new file mode 100644
index 0000000000..102392ba91
--- /dev/null
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.cpp
@@ -0,0 +1,56 @@
+#include "pch.h"
+#include "FancyZonesWindowProcessing.h"
+
+#include
+#include
+#include
+
+bool FancyZonesWindowProcessing::IsProcessable(HWND window) noexcept
+{
+ const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window);
+ if (isSplashScreen)
+ {
+ return false;
+ }
+
+ const bool windowMinimized = IsIconic(window);
+ if (windowMinimized)
+ {
+ return false;
+ }
+
+ const bool standard = FancyZonesWindowUtils::IsStandardWindow(window);
+ if (!standard)
+ {
+ return false;
+ }
+
+ // popup could be the window we don't want to snap: start menu, notification popup, tray window, etc.
+ // also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram
+ bool isPopup = FancyZonesWindowUtils::IsPopupWindow(window) && !FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(window);
+ if (isPopup && !FancyZonesSettings::settings().allowSnapPopupWindows)
+ {
+ return false;
+ }
+
+ // allow child windows
+ auto hasOwner = FancyZonesWindowUtils::HasVisibleOwner(window);
+ if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows)
+ {
+ return false;
+ }
+
+ if (FancyZonesWindowUtils::IsExcluded(window))
+ {
+ return false;
+ }
+
+ // Switch between virtual desktops results with posting same windows messages that also indicate
+ // creation of new window. We need to check if window being processed is on currently active desktop.
+ if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window))
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h
index fc2108ffbb..21fbcfbad2 100644
--- a/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h
+++ b/src/modules/fancyzones/FancyZonesLib/FancyZonesWindowProcessing.h
@@ -1,39 +1,6 @@
#pragma once
-#include
-#include
-
namespace FancyZonesWindowProcessing
{
- inline bool IsProcessable(HWND window) noexcept
- {
- const bool isSplashScreen = FancyZonesWindowUtils::IsSplashScreen(window);
- if (isSplashScreen)
- {
- return false;
- }
-
- const bool windowMinimized = IsIconic(window);
- if (windowMinimized)
- {
- return false;
- }
-
- // For windows that FancyZones shouldn't process (start menu, tray, popup menus)
- // VirtualDesktopManager is unable to retrieve virtual desktop id and returns an error.
- auto desktopId = VirtualDesktop::instance().GetDesktopId(window);
- if (!desktopId.has_value())
- {
- return false;
- }
-
- // Switch between virtual desktops results with posting same windows messages that also indicate
- // creation of new window. We need to check if window being processed is on currently active desktop.
- if (!VirtualDesktop::instance().IsWindowOnCurrentDesktop(window))
- {
- return false;
- }
-
- return true;
- }
+ bool IsProcessable(HWND window) noexcept;
}
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp
index 67bf726d2b..dbd1511d5a 100644
--- a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.cpp
@@ -270,30 +270,16 @@ GUID VirtualDesktop::GetCurrentVirtualDesktopId() const noexcept
return m_currentVirtualDesktopId;
}
-GUID VirtualDesktop::GetPreviousVirtualDesktopId() const noexcept
-{
- return m_previousDesktopId;
-}
-
void VirtualDesktop::UpdateVirtualDesktopId() noexcept
{
- m_previousDesktopId = m_currentVirtualDesktopId;
-
auto currentVirtualDesktopId = GetCurrentVirtualDesktopIdFromRegistry();
- if (!currentVirtualDesktopId.has_value())
- {
- Logger::info("No Virtual Desktop Id found in registry");
- currentVirtualDesktopId = VirtualDesktop::instance().GetDesktopIdByTopLevelWindows();
- }
-
if (currentVirtualDesktopId.has_value())
{
- m_currentVirtualDesktopId = *currentVirtualDesktopId;
-
- if (m_currentVirtualDesktopId == GUID_NULL)
- {
- Logger::warn("Couldn't retrieve virtual desktop id");
- }
+ m_currentVirtualDesktopId = currentVirtualDesktopId.value();
+ }
+ else
+ {
+ m_currentVirtualDesktopId = GUID_NULL;
}
Trace::VirtualDesktopChanged();
diff --git a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h
index 069e1883ea..4c25518ce8 100644
--- a/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h
+++ b/src/modules/fancyzones/FancyZonesLib/VirtualDesktop.h
@@ -7,7 +7,6 @@ public:
// saved values
GUID GetCurrentVirtualDesktopId() const noexcept;
- GUID GetPreviousVirtualDesktopId() const noexcept;
void UpdateVirtualDesktopId() noexcept;
// IVirtualDesktopManager
@@ -29,7 +28,6 @@ private:
IVirtualDesktopManager* m_vdManager{nullptr};
GUID m_currentVirtualDesktopId{};
- GUID m_previousDesktopId{};
std::optional> GetVirtualDesktopIdsFromRegistry(HKEY hKey) const;
};
diff --git a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
index c6b8f8cedd..a69aaa7ef7 100644
--- a/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/WindowMouseSnap.cpp
@@ -32,7 +32,6 @@ WindowMouseSnap::~WindowMouseSnap()
std::unique_ptr WindowMouseSnap::Create(HWND window, const std::unordered_map>& activeWorkAreas)
{
if (!FancyZonesWindowProcessing::IsProcessable(window) ||
- !FancyZonesWindowUtils::IsCandidateForZoning(window) ||
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
{
return nullptr;
diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
index dbad77732c..ef895cf830 100644
--- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
@@ -166,6 +166,8 @@ bool FancyZonesWindowUtils::HasVisibleOwner(HWND window) noexcept
bool FancyZonesWindowUtils::IsStandardWindow(HWND window)
{
+ // True if from the styles the window looks like a standard window
+
if (GetAncestor(window, GA_ROOT) != window)
{
return false;
@@ -181,13 +183,6 @@ bool FancyZonesWindowUtils::IsStandardWindow(HWND window)
return false;
}
- std::array class_name;
- GetClassNameA(window, class_name.data(), static_cast(class_name.size()));
- if (is_system_window(window, class_name.data()))
- {
- return false;
- }
-
return true;
}
@@ -203,44 +198,6 @@ bool FancyZonesWindowUtils::HasThickFrameAndMinimizeMaximizeButtons(HWND window)
return ((style & WS_THICKFRAME) == WS_THICKFRAME && (style & WS_MINIMIZEBOX) == WS_MINIMIZEBOX && (style & WS_MAXIMIZEBOX) == WS_MAXIMIZEBOX);
}
-bool FancyZonesWindowUtils::IsCandidateForZoning(HWND window)
-{
- bool isStandard = IsStandardWindow(window);
- if (!isStandard)
- {
- return false;
- }
-
- // popup could be the window we don't want to snap: start menu, notification popup, tray window, etc.
- // also, popup could be the windows we want to snap disregarding the "allowSnapPopupWindows" setting, e.g. Telegram
- bool isPopup = IsPopupWindow(window);
- if (isPopup && !HasThickFrameAndMinimizeMaximizeButtons(window) && !FancyZonesSettings::settings().allowSnapPopupWindows)
- {
- return false;
- }
-
- // allow child windows
- auto hasOwner = HasVisibleOwner(window);
- if (hasOwner && !FancyZonesSettings::settings().allowSnapChildWindows)
- {
- return false;
- }
-
- std::wstring processPath = get_process_path_waiting_uwp(window);
- CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length()));
- if (IsExcludedByUser(window, processPath))
- {
- return false;
- }
-
- if (IsExcludedByDefault(window, processPath))
- {
- return false;
- }
-
- return true;
-}
-
bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
{
DWORD pid = 0;
@@ -268,6 +225,23 @@ bool FancyZonesWindowUtils::IsProcessOfWindowElevated(HWND window)
return false;
}
+bool FancyZonesWindowUtils::IsExcluded(HWND window)
+{
+ std::wstring processPath = get_process_path_waiting_uwp(window);
+ CharUpperBuffW(const_cast(processPath).data(), static_cast(processPath.length()));
+ if (IsExcludedByUser(window, processPath))
+ {
+ return true;
+ }
+
+ if (IsExcludedByDefault(window, processPath))
+ {
+ return true;
+ }
+
+ return false;
+}
+
bool FancyZonesWindowUtils::IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept
{
return (check_excluded_app(hwnd, processPath, FancyZonesSettings::settings().excludedAppsArray));
@@ -281,6 +255,13 @@ bool FancyZonesWindowUtils::IsExcludedByDefault(const HWND& hwnd, std::wstring&
return true;
}
+ std::array class_name;
+ GetClassNameA(hwnd, class_name.data(), static_cast(class_name.size()));
+ if (is_system_window(hwnd, class_name.data()))
+ {
+ return true;
+ }
+
static std::vector defaultExcludedApps = { NonLocalizable::PowerToysAppFZEditor, NonLocalizable::CoreWindow, NonLocalizable::SearchUI };
return (check_excluded_app(hwnd, processPath, defaultExcludedApps));
}
diff --git a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h
index 9659d78b9c..4ac973bacb 100644
--- a/src/modules/fancyzones/FancyZonesLib/WindowUtils.h
+++ b/src/modules/fancyzones/FancyZonesLib/WindowUtils.h
@@ -21,8 +21,9 @@ namespace FancyZonesWindowUtils
bool IsStandardWindow(HWND window);
bool IsPopupWindow(HWND window) noexcept;
bool HasThickFrameAndMinimizeMaximizeButtons(HWND window) noexcept;
- bool IsCandidateForZoning(HWND window);
bool IsProcessOfWindowElevated(HWND window); // If HWND is already dead, we assume it wasn't elevated
+
+ bool IsExcluded(HWND window);
bool IsExcludedByUser(const HWND& hwnd, std::wstring& processPath) noexcept;
bool IsExcludedByDefault(const HWND& hwnd, std::wstring& processPath) noexcept;
diff --git a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp
index 2d4b6adf90..638776c25a 100644
--- a/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/WorkArea.cpp
@@ -244,14 +244,12 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId)
const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId);
if (!isLayoutAlreadyApplied)
{
- if (parentUniqueId.virtualDesktopId != GUID_NULL)
- {
- AppliedLayouts::instance().CloneLayout(parentUniqueId, m_uniqueId);
- }
- else
+ if (!AppliedLayouts::instance().CloneLayout(parentUniqueId, m_uniqueId))
{
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
}
+
+ AppliedLayouts::instance().SaveData();
}
CalculateZoneSet();
diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp
similarity index 57%
rename from src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp
rename to src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp
index 486e2739ea..a3cfa7cf45 100644
--- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.cpp
+++ b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp
@@ -1,9 +1,9 @@
#include "pch.h"
-#include "MonitorWorkAreaMap.h"
+#include "WorkAreaConfiguration.h"
#include
-WorkArea* const MonitorWorkAreaMap::GetWorkArea(HMONITOR monitor) const
+WorkArea* const WorkAreaConfiguration::GetWorkArea(HMONITOR monitor) const
{
auto iter = m_workAreaMap.find(monitor);
if (iter != m_workAreaMap.end())
@@ -14,7 +14,7 @@ WorkArea* const MonitorWorkAreaMap::GetWorkArea(HMONITOR monitor) const
return nullptr;
}
-WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromCursor() const
+WorkArea* const WorkAreaConfiguration::GetWorkAreaFromCursor() const
{
const auto allMonitorsWorkArea = GetWorkArea(nullptr);
if (allMonitorsWorkArea)
@@ -35,7 +35,7 @@ WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromCursor() const
}
}
-WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromWindow(HWND window) const
+WorkArea* const WorkAreaConfiguration::GetWorkAreaFromWindow(HWND window) const
{
const auto allMonitorsWorkArea = GetWorkArea(nullptr);
if (allMonitorsWorkArea)
@@ -51,39 +51,17 @@ WorkArea* const MonitorWorkAreaMap::GetWorkAreaFromWindow(HWND window) const
}
}
-const std::unordered_map>& MonitorWorkAreaMap::GetAllWorkAreas() const noexcept
+const std::unordered_map>& WorkAreaConfiguration::GetAllWorkAreas() const noexcept
{
return m_workAreaMap;
}
-void MonitorWorkAreaMap::AddWorkArea(HMONITOR monitor, std::unique_ptr workArea)
+void WorkAreaConfiguration::AddWorkArea(HMONITOR monitor, std::unique_ptr workArea)
{
m_workAreaMap.insert({ monitor, std::move(workArea) });
}
-FancyZonesDataTypes::WorkAreaId MonitorWorkAreaMap::GetParent(HMONITOR monitor) const
-{
- if (m_workAreaParents.contains(monitor))
- {
- return m_workAreaParents.at(monitor);
- }
-
- return FancyZonesDataTypes::WorkAreaId{};
-}
-
-void MonitorWorkAreaMap::SaveParentIds()
-{
- m_workAreaParents.clear();
- for (const auto& [monitor, workArea] : m_workAreaMap)
- {
- if (workArea)
- {
- m_workAreaParents.insert({ monitor, workArea->UniqueId() });
- }
- }
-}
-
-void MonitorWorkAreaMap::Clear() noexcept
+void WorkAreaConfiguration::Clear() noexcept
{
m_workAreaMap.clear();
}
diff --git a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.h
similarity index 78%
rename from src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h
rename to src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.h
index 45fabda709..9530dbdae3 100644
--- a/src/modules/fancyzones/FancyZonesLib/MonitorWorkAreaMap.h
+++ b/src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.h
@@ -1,15 +1,14 @@
#pragma once
-#include "GuidUtils.h"
#include
class WorkArea;
-class MonitorWorkAreaMap
+class WorkAreaConfiguration
{
public:
/**
- * Get work area based on virtual desktop id and monitor handle.
+ * Get work area based on monitor handle.
*
* @param[in] monitor Monitor handle.
*
@@ -19,7 +18,7 @@ public:
WorkArea* const GetWorkArea(HMONITOR monitor) const;
/**
- * Get work area based on virtual desktop id and the current cursor position.
+ * Get work area based on the current cursor position.
*
* @returns Object representing single work area, interface to all actions available on work area
* (e.g. moving windows through zone layout specified for that work area).
@@ -49,13 +48,6 @@ public:
*/
void AddWorkArea(HMONITOR monitor, std::unique_ptr workArea);
- FancyZonesDataTypes::WorkAreaId GetParent(HMONITOR monitor) const;
-
- /**
- * Saving current work area IDs as parents for later use.
- */
- void SaveParentIds();
-
/**
* Clear all persisted work area related data.
*/
@@ -63,5 +55,4 @@ public:
private:
std::unordered_map> m_workAreaMap;
- std::unordered_map m_workAreaParents{};
};
diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp
index c3e954fb00..c3eea23236 100644
--- a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp
+++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppZoneHistoryTests.Spec.cpp
@@ -339,4 +339,159 @@ namespace FancyZonesUnitTests
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(nullptr, workAreaId, layoutId));
}
};
+
+ TEST_CLASS (AppZoneHistorySyncVirtualDesktops)
+ {
+ const GUID virtualDesktop1 = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value();
+ const GUID virtualDesktop2 = FancyZonesUtils::GuidFromString(L"{65F6343A-868F-47EE-838E-55A178A7FB7A}").value();
+ const GUID deletedVirtualDesktop = FancyZonesUtils::GuidFromString(L"{2D9F3E2D-F61D-4618-B35D-85C9B8DFDFD8}").value();
+
+ FancyZonesDataTypes::WorkAreaId GetWorkAreaID(GUID virtualDesktop)
+ {
+ return FancyZonesDataTypes::WorkAreaId{
+ .monitorId = {
+ .deviceId = { .id = L"id", .instanceId = L"id", .number = 1 },
+ .serialNumber = L"serial-number"
+ },
+ .virtualDesktopId = virtualDesktop
+ };
+ }
+
+ FancyZonesDataTypes::AppZoneHistoryData GetAppZoneHistoryData(GUID virtualDesktop, const std::wstring& layoutId, const ZoneIndexSet& zones)
+ {
+ return FancyZonesDataTypes::AppZoneHistoryData{
+ .layoutId = FancyZonesUtils::GuidFromString(layoutId).value(),
+ .workAreaId = GetWorkAreaID(virtualDesktop),
+ .zoneIndexSet = zones
+ };
+ };
+
+ TEST_METHOD_INITIALIZE(Init)
+ {
+ AppZoneHistory::instance().LoadData();
+ }
+
+ TEST_METHOD_CLEANUP(CleanUp)
+ {
+ std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_SwitchVirtualDesktop)
+ {
+ AppZoneHistory::TAppZoneHistoryMap history{};
+ const std::wstring app = L"app";
+ history.insert({ app, std::vector{
+ GetAppZoneHistoryData(virtualDesktop1, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }),
+ GetAppZoneHistoryData(virtualDesktop2, L"{EAC1BB3B-13D6-4839-BBF7-58C3E8AB7229}", { 1 }),
+ } });
+ AppZoneHistory::instance().SetAppZoneHistory(history);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = virtualDesktop2;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } };
+ AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(history.at(app)[0] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value());
+ Assert::IsTrue(history.at(app)[1] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop2)).value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_CurrentVirtualDesktopDeleted)
+ {
+ AppZoneHistory::TAppZoneHistoryMap history{};
+ const std::wstring app = L"app";
+ history.insert({ app, std::vector{
+ GetAppZoneHistoryData(virtualDesktop1, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }),
+ GetAppZoneHistoryData(deletedVirtualDesktop, L"{EAC1BB3B-13D6-4839-BBF7-58C3E8AB7229}", { 1 }),
+ } });
+ AppZoneHistory::instance().SetAppZoneHistory(history);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = deletedVirtualDesktop;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } };
+ AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(history.at(app)[0] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value());
+ Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_NotCurrentVirtualDesktopDeleted)
+ {
+ AppZoneHistory::TAppZoneHistoryMap history{};
+ const std::wstring app = L"app";
+ history.insert({ app, std::vector{
+ GetAppZoneHistoryData(virtualDesktop1, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }),
+ GetAppZoneHistoryData(deletedVirtualDesktop, L"{EAC1BB3B-13D6-4839-BBF7-58C3E8AB7229}", { 1 }),
+ } });
+ AppZoneHistory::instance().SetAppZoneHistory(history);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = virtualDesktop1;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } };
+ AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(history.at(app)[0] == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value());
+ Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_AllIdsFromRegistryAreNew)
+ {
+ AppZoneHistory::TAppZoneHistoryMap history{};
+ const std::wstring app = L"app";
+ history.insert({ app, std::vector{
+ GetAppZoneHistoryData(deletedVirtualDesktop, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }),
+ } });
+ AppZoneHistory::instance().SetAppZoneHistory(history);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = deletedVirtualDesktop;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } };
+ AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ auto expected = history.at(app)[0];
+ expected.workAreaId.virtualDesktopId = currentVirtualDesktop;
+ Assert::IsTrue(expected == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop1)).value());
+ Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(virtualDesktop2)).has_value());
+ Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktop_NoDesktopsInRegistry)
+ {
+ AppZoneHistory::TAppZoneHistoryMap history{};
+ const std::wstring app = L"app";
+ history.insert({ app, std::vector{
+ GetAppZoneHistoryData(deletedVirtualDesktop, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }),
+ } });
+ AppZoneHistory::instance().SetAppZoneHistory(history);
+
+ GUID currentVirtualDesktop = GUID_NULL;
+ GUID lastUsedVirtualDesktop = deletedVirtualDesktop;
+ std::optional> virtualDesktopsInRegistry = std::nullopt;
+ AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ auto expected = history.at(app)[0];
+ expected.workAreaId.virtualDesktopId = currentVirtualDesktop;
+ Assert::IsTrue(expected == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(currentVirtualDesktop)).value());
+ Assert::IsFalse(AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktop_SwithVirtualDesktopFirstTime)
+ {
+ AppZoneHistory::TAppZoneHistoryMap history{};
+ const std::wstring app = L"app";
+ history.insert({ app, std::vector{
+ GetAppZoneHistoryData(GUID_NULL, L"{147243D0-1111-4225-BCD3-31029FE384FC}", { 0 }),
+ } });
+ AppZoneHistory::instance().SetAppZoneHistory(history);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = GUID_NULL;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } };
+ AppZoneHistory::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ auto expected = history.at(app)[0];
+ expected.workAreaId.virtualDesktopId = currentVirtualDesktop;
+ Assert::IsTrue(expected == AppZoneHistory::instance().GetZoneHistory(app, GetWorkAreaID(currentVirtualDesktop)).value());
+ }
+ };
+
}
diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp
index 87f5da5a3c..ec9256664b 100644
--- a/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp
+++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/AppliedLayoutsTests.Spec.cpp
@@ -14,24 +14,14 @@ namespace FancyZonesUnitTests
{
TEST_CLASS (AppliedLayoutsUnitTests)
{
- FancyZonesData& m_fzData = FancyZonesDataInstance();
- std::wstring m_testFolder = L"FancyZonesUnitTests";
- std::wstring m_testFolderPath = PTSettingsHelper::get_module_save_folder_location(m_testFolder);
-
TEST_METHOD_INITIALIZE(Init)
{
- m_fzData.SetSettingsModulePath(L"FancyZonesUnitTests");
+ AppliedLayouts::instance().LoadData();
}
TEST_METHOD_CLEANUP(CleanUp)
{
- // Move...FromZonesSettings creates all of these files, clean up
- std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
- std::filesystem::remove(CustomLayouts::CustomLayoutsFileName());
- std::filesystem::remove(LayoutHotkeys::LayoutHotkeysFileName());
- std::filesystem::remove(LayoutTemplates::LayoutTemplatesFileName());
- std::filesystem::remove_all(m_testFolderPath);
- AppliedLayouts::instance().LoadData(); // clean data
+ std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
}
TEST_METHOD (AppliedLayoutsParse)
@@ -75,7 +65,7 @@ namespace FancyZonesUnitTests
Assert::IsTrue(AppliedLayouts::instance().IsLayoutApplied(id));
}
- TEST_METHOD(AppliedLayoutsParseDataWithResolution)
+ TEST_METHOD (AppliedLayoutsParseDataWithResolution)
{
// prepare
json::JsonObject root{};
@@ -242,143 +232,94 @@ namespace FancyZonesUnitTests
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
}
- TEST_METHOD (MoveAppliedLayoutsFromZonesSettings)
+ TEST_METHOD (Save)
{
- // 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::WorkAreaId id{
- .monitorId = { .deviceId = { .id = L"VSC9636", .instanceId = L"5&37ac4db&0&UID160005" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
+ FancyZonesDataTypes::WorkAreaId workAreaId1{
+ .monitorId = {
+ .deviceId = { .id = L"id-1", .instanceId = L"id-1", .number = 1 },
+ .serialNumber = L"serial-number-1"
+ },
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value()
+ };
+ FancyZonesDataTypes::WorkAreaId workAreaId2{
+ .monitorId = {
+ .deviceId = { .id = L"id-2", .instanceId = L"id-2", .number = 2 },
+ .serialNumber = L"serial-number-2" },
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value()
+ };
+ FancyZonesDataTypes::WorkAreaId workAreaId3{
+ .monitorId = {
+ .deviceId = { .id = L"id-1", .instanceId = L"id-1", .number = 1 },
+ .serialNumber = L"serial-number-1" },
+ .virtualDesktopId = GUID_NULL
+ };
+ FancyZonesDataTypes::WorkAreaId workAreaId4{
+ .monitorId = {
+ .deviceId = { .id = L"id-2", .instanceId = L"id-2", .number = 2 },
+ .serialNumber = L"serial-number-2" },
+ .virtualDesktopId = GUID_NULL
};
- 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);
+ LayoutData layout1{ .uuid = FancyZonesUtils::GuidFromString(L"{D7DBECFA-23FC-4F45-9B56-51CFA9F6ABA2}").value() };
+ LayoutData layout2{ .uuid = FancyZonesUtils::GuidFromString(L"{B9EDB48C-EC48-4E82-993F-A15DC1FF09D3}").value() };
+ LayoutData layout3{ .uuid = FancyZonesUtils::GuidFromString(L"{94CF0000-7814-4D72-9624-794060FA269C}").value() };
+ LayoutData layout4{ .uuid = FancyZonesUtils::GuidFromString(L"{13FA7ADF-1B6C-4FB6-8142-254B77C128E2}").value() };
+
+ AppliedLayouts::TAppliedLayoutsMap expected{};
+ expected.insert({ workAreaId1, layout1 });
+ expected.insert({ workAreaId2, layout2 });
+ expected.insert({ workAreaId3, layout3 });
+ expected.insert({ workAreaId4, layout4 });
+
+ AppliedLayouts::instance().SetAppliedLayouts(expected);
+ AppliedLayouts::instance().SaveData();
- // 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());
+ auto actual = AppliedLayouts::instance().GetAppliedLayoutMap();
+ Assert::AreEqual(expected.size(), actual.size());
+ Assert::IsTrue(expected.at(workAreaId1) == actual.at(workAreaId1));
+ Assert::IsTrue(expected.at(workAreaId2) == actual.at(workAreaId2));
+ Assert::IsTrue(expected.at(workAreaId3) == actual.at(workAreaId3));
+ Assert::IsTrue(expected.at(workAreaId4) == actual.at(workAreaId4));
}
TEST_METHOD (CloneDeviceInfo)
{
FancyZonesDataTypes::WorkAreaId deviceSrc{
.monitorId = { .deviceId = { .id = L"Device1", .instanceId = L"" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EA6B6934-D55F-49F5-A9A5-CFADE21FFFB8}").value()
};
FancyZonesDataTypes::WorkAreaId deviceDst{
.monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EF1A8099-7D1E-4738-805A-571B31B02674}").value()
};
- Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc));
- Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst));
-
+ LayoutData layout { .uuid = FancyZonesUtils::GuidFromString(L"{361F96DD-FD10-4D01-ABAC-CC1C857294DD}").value() };
+ Assert::IsTrue(AppliedLayouts::instance().ApplyLayout(deviceSrc, layout));
+
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::WorkAreaId deviceSrc{
- .monitorId = { .deviceId = { .id = L"Device1", .instanceId = L"" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
- };
- FancyZonesDataTypes::WorkAreaId deviceDst{
- .monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" },
- .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);
+ Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceSrc));
+ Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceDst));
}
TEST_METHOD (CloneDeviceInfoFromUnknownDevice)
{
FancyZonesDataTypes::WorkAreaId deviceSrc{
.monitorId = { .deviceId = { .id = L"Device1", .instanceId = L"" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EA6B6934-D55F-49F5-A9A5-CFADE21FFFB8}").value()
};
FancyZonesDataTypes::WorkAreaId deviceDst{
.monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EF1A8099-7D1E-4738-805A-571B31B02674}").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());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(deviceDst).has_value());
}
TEST_METHOD (CloneDeviceInfoNullVirtualDesktopId)
@@ -389,35 +330,25 @@ namespace FancyZonesUnitTests
};
FancyZonesDataTypes::WorkAreaId deviceDst{
.monitorId = { .deviceId = { .id = L"Device2", .instanceId = L"" }, .serialNumber = L"" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}").value()
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{EF1A8099-7D1E-4738-805A-571B31B02674}").value()
};
- Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceSrc));
- Assert::IsTrue(AppliedLayouts::instance().ApplyDefaultLayout(deviceDst));
-
+ LayoutData layout{ .uuid = FancyZonesUtils::GuidFromString(L"{361F96DD-FD10-4D01-ABAC-CC1C857294DD}").value() };
+ Assert::IsTrue(AppliedLayouts::instance().ApplyLayout(deviceSrc, layout));
+
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);
+ Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceSrc));
+ Assert::IsTrue(layout == AppliedLayouts::instance().GetDeviceLayout(deviceDst));
}
TEST_METHOD (ApplyLayout)
{
- // prepare
- FancyZonesDataTypes::WorkAreaId deviceId {
+ FancyZonesDataTypes::WorkAreaId workAreaId {
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" }, .serialNumber = L"" },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value()
};
- // test
LayoutData expectedLayout {
.uuid = FancyZonesUtils::GuidFromString(L"{33A2B101-06E0-437B-A61E-CDBECF502906}").value(),
.type = FancyZonesDataTypes::ZoneSetLayoutType::Focus,
@@ -427,47 +358,30 @@ namespace FancyZonesUnitTests
.sensitivityRadius = 30
};
- AppliedLayouts::instance().ApplyLayout(deviceId, expectedLayout);
+ AppliedLayouts::instance().ApplyLayout(workAreaId, expectedLayout);
- Assert::IsFalse(AppliedLayouts::instance().GetAppliedLayoutMap().empty());
- Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(deviceId).has_value());
-
- auto actual = AppliedLayouts::instance().GetAppliedLayoutMap().find(deviceId)->second;
- Assert::IsTrue(expectedLayout.type == actual.type);
- Assert::AreEqual(expectedLayout.showSpacing, actual.showSpacing);
- Assert::AreEqual(expectedLayout.spacing, actual.spacing);
- Assert::AreEqual(expectedLayout.zoneCount, actual.zoneCount);
- Assert::AreEqual(expectedLayout.sensitivityRadius, actual.sensitivityRadius);
+ Assert::IsTrue(AppliedLayouts::instance().GetDeviceLayout(workAreaId).has_value());
+ Assert::IsTrue(expectedLayout == AppliedLayouts::instance().GetAppliedLayoutMap().find(workAreaId)->second);
}
TEST_METHOD (ApplyLayoutReplace)
{
// prepare
- FancyZonesDataTypes::WorkAreaId deviceId{
+ FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" }, .serialNumber = L"" },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC57}").value()
};
+
+ LayoutData layout{
+ .uuid = FancyZonesUtils::GuidFromString(L"{ACE817FD-2C51-4E13-903A-84CAB86FD17C}").value(),
+ .type = FancyZonesDataTypes::ZoneSetLayoutType::Rows,
+ .showSpacing = true,
+ .spacing = 3,
+ .zoneCount = 4,
+ .sensitivityRadius = 22
+ };
- 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();
+ AppliedLayouts::instance().SetAppliedLayouts({ {workAreaId, layout} });
// test
LayoutData expectedLayout{
@@ -479,18 +393,8 @@ namespace FancyZonesUnitTests
.sensitivityRadius = 30
};
- AppliedLayouts::instance().ApplyLayout(deviceId, expectedLayout);
-
- 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(FancyZonesUtils::GuidToString(expectedLayout.uuid).value().c_str(), FancyZonesUtils::GuidToString(actual.uuid).value().c_str());
- Assert::IsTrue(expectedLayout.type == actual.type);
- Assert::AreEqual(expectedLayout.showSpacing, actual.showSpacing);
- Assert::AreEqual(expectedLayout.spacing, actual.spacing);
- Assert::AreEqual(expectedLayout.zoneCount, actual.zoneCount);
- Assert::AreEqual(expectedLayout.sensitivityRadius, actual.sensitivityRadius);
+ AppliedLayouts::instance().ApplyLayout(workAreaId, expectedLayout);
+ Assert::IsTrue(expectedLayout == AppliedLayouts::instance().GetDeviceLayout(workAreaId));
}
TEST_METHOD (ApplyDefaultLayout)
@@ -553,4 +457,245 @@ namespace FancyZonesUnitTests
Assert::IsFalse(AppliedLayouts::instance().IsLayoutApplied(id2));
}
};
+
+ TEST_CLASS (AppliedLayoutsSyncVirtualDesktops)
+ {
+ const GUID virtualDesktop1 = FancyZonesUtils::GuidFromString(L"{30387C86-BB15-476D-8683-AF93F6D73E99}").value();
+ const GUID virtualDesktop2 = FancyZonesUtils::GuidFromString(L"{65F6343A-868F-47EE-838E-55A178A7FB7A}").value();
+ const GUID deletedVirtualDesktop = FancyZonesUtils::GuidFromString(L"{2D9F3E2D-F61D-4618-B35D-85C9B8DFDFD8}").value();
+
+ LayoutData layout1{ .uuid = FancyZonesUtils::GuidFromString(L"{D7DBECFA-23FC-4F45-9B56-51CFA9F6ABA2}").value() };
+ LayoutData layout2{ .uuid = FancyZonesUtils::GuidFromString(L"{B9EDB48C-EC48-4E82-993F-A15DC1FF09D3}").value() };
+ LayoutData layout3{ .uuid = FancyZonesUtils::GuidFromString(L"{94CF0000-7814-4D72-9624-794060FA269C}").value() };
+ LayoutData layout4{ .uuid = FancyZonesUtils::GuidFromString(L"{13FA7ADF-1B6C-4FB6-8142-254B77C128E2}").value() };
+
+ FancyZonesDataTypes::WorkAreaId GetWorkAreaID(int number, GUID virtualDesktop)
+ {
+ return FancyZonesDataTypes::WorkAreaId{
+ .monitorId = {
+ .deviceId = {
+ .id = std::wstring(L"id-") + std::to_wstring(number),
+ .instanceId = std::wstring(L"id-") + std::to_wstring(number),
+ .number = number
+ },
+ .serialNumber = std::wstring(L"serial-number-") + std::to_wstring(number)
+ },
+ .virtualDesktopId = virtualDesktop
+ };
+ }
+
+ TEST_METHOD_INITIALIZE(Init)
+ {
+ AppliedLayouts::instance().LoadData();
+ }
+
+ TEST_METHOD_CLEANUP(CleanUp)
+ {
+ std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
+ }
+
+ TEST_METHOD(SyncVirtualDesktops_SwitchVirtualDesktop)
+ {
+ AppliedLayouts::TAppliedLayoutsMap layouts{};
+ layouts.insert({ GetWorkAreaID(1, virtualDesktop1), layout1 });
+ layouts.insert({ GetWorkAreaID(2, virtualDesktop1), layout2 });
+ layouts.insert({ GetWorkAreaID(1, virtualDesktop2), layout3 });
+ layouts.insert({ GetWorkAreaID(2, virtualDesktop2), layout4 });
+ AppliedLayouts::instance().SetAppliedLayouts(layouts);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = virtualDesktop2;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } };
+ AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1)));
+ Assert::IsTrue(layout3 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop2)));
+ Assert::IsTrue(layout4 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop2)));
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_CurrentVirtualDesktopDeleted)
+ {
+ AppliedLayouts::TAppliedLayoutsMap layouts{};
+ layouts.insert({ GetWorkAreaID(1, virtualDesktop1), layout1 });
+ layouts.insert({ GetWorkAreaID(2, virtualDesktop1), layout2 });
+ layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout3 });
+ layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout4 });
+ AppliedLayouts::instance().SetAppliedLayouts(layouts);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = deletedVirtualDesktop;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } };
+ AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1)));
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_NotCurrentVirtualDesktopDeleted)
+ {
+ AppliedLayouts::TAppliedLayoutsMap layouts{};
+ layouts.insert({ GetWorkAreaID(1, virtualDesktop1), layout1 });
+ layouts.insert({ GetWorkAreaID(2, virtualDesktop1), layout2 });
+ layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout3 });
+ layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout4 });
+ AppliedLayouts::instance().SetAppliedLayouts(layouts);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = virtualDesktop1;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1 } };
+ AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1)));
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktops_AllIdsFromRegistryAreNew)
+ {
+ AppliedLayouts::TAppliedLayoutsMap layouts{};
+ layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout1 });
+ layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout2 });
+ AppliedLayouts::instance().SetAppliedLayouts(layouts);
+
+ GUID currentVirtualDesktop = virtualDesktop1;
+ GUID lastUsedVirtualDesktop = deletedVirtualDesktop;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } };
+ AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1)));
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop2)).has_value());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop2)).has_value());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD (SyncVirtualDesktop_NoDesktopsInRegistry)
+ {
+ AppliedLayouts::TAppliedLayoutsMap layouts{};
+ layouts.insert({ GetWorkAreaID(1, deletedVirtualDesktop), layout1 });
+ layouts.insert({ GetWorkAreaID(2, deletedVirtualDesktop), layout2 });
+ AppliedLayouts::instance().SetAppliedLayouts(layouts);
+
+ GUID currentVirtualDesktop = GUID_NULL;
+ GUID lastUsedVirtualDesktop = deletedVirtualDesktop;
+ std::optional> virtualDesktopsInRegistry = std::nullopt;
+ AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, GUID_NULL)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, GUID_NULL)));
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, deletedVirtualDesktop)).has_value());
+ Assert::IsFalse(AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, deletedVirtualDesktop)).has_value());
+ }
+
+ TEST_METHOD(SyncVirtualDesktops_SwithVirtualDesktopFirstTime)
+ {
+ AppliedLayouts::TAppliedLayoutsMap layouts{};
+ layouts.insert({ GetWorkAreaID(1, GUID_NULL), layout1 });
+ layouts.insert({ GetWorkAreaID(2, GUID_NULL), layout2 });
+ AppliedLayouts::instance().SetAppliedLayouts(layouts);
+
+ GUID currentVirtualDesktop = virtualDesktop2;
+ GUID lastUsedVirtualDesktop = GUID_NULL;
+ std::optional> virtualDesktopsInRegistry = { { virtualDesktop1, virtualDesktop2 } };
+ AppliedLayouts::instance().SyncVirtualDesktops(currentVirtualDesktop, lastUsedVirtualDesktop, virtualDesktopsInRegistry);
+
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop1)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop1)));
+ Assert::IsTrue(layout1 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(1, virtualDesktop2)));
+ Assert::IsTrue(layout2 == AppliedLayouts::instance().GetDeviceLayout(GetWorkAreaID(2, virtualDesktop2)));
+ }
+ };
+
+ TEST_CLASS (AppliedLayoutsFromOutdatedFileMappingUnitTests)
+ {
+ FancyZonesData& m_fzData = FancyZonesDataInstance();
+ std::wstring m_testFolder = L"FancyZonesUnitTests";
+ std::wstring m_testFolderPath = PTSettingsHelper::get_module_save_folder_location(m_testFolder);
+
+ TEST_METHOD_INITIALIZE(Init)
+ {
+ m_fzData.SetSettingsModulePath(m_testFolder);
+ }
+
+ TEST_METHOD_CLEANUP(CleanUp)
+ {
+ // MoveAppliedLayoutsFromZonesSettings creates all of these files, clean up
+ std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
+ std::filesystem::remove(CustomLayouts::CustomLayoutsFileName());
+ std::filesystem::remove(LayoutHotkeys::LayoutHotkeysFileName());
+ std::filesystem::remove(LayoutTemplates::LayoutTemplatesFileName());
+ std::filesystem::remove_all(m_testFolderPath);
+ AppliedLayouts::instance().LoadData(); // clean data
+ }
+
+ 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::WorkAreaId id{
+ .monitorId = { .deviceId = { .id = L"VSC9636", .instanceId = L"5&37ac4db&0&UID160005" }, .serialNumber = L"" },
+ .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());
+ }
+ };
}
\ No newline at end of file
diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp
index db5c660db0..5b024b718d 100644
--- a/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp
+++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/WorkAreaIdTests.Spec.cpp
@@ -43,11 +43,11 @@ namespace FancyZonesUnitTests
Assert::IsFalse(id1 == id2);
}
- TEST_METHOD (VirtualDesktopNull)
+ TEST_METHOD (VirtualDesktopDifferent)
{
FancyZonesDataTypes::WorkAreaId id1{
.monitorId = { .deviceId = { .id = L"device", .instanceId = L"instance-id" }, .serialNumber = L"serial-number" },
- .virtualDesktopId = GUID_NULL
+ .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{F21F6F29-76FD-4FC1-8970-17AB8AD64847}").value()
};
FancyZonesDataTypes::WorkAreaId id2{
@@ -55,14 +55,14 @@ namespace FancyZonesUnitTests
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{E21F6F29-76FD-4FC1-8970-17AB8AD64847}").value()
};
- Assert::IsTrue(id1 == id2);
+ Assert::IsFalse(id1 == id2);
}
- TEST_METHOD (VirtualDesktopDifferent)
+ TEST_METHOD (VirtualDesktopNull)
{
FancyZonesDataTypes::WorkAreaId id1{
.monitorId = { .deviceId = { .id = L"device", .instanceId = L"instance-id" }, .serialNumber = L"serial-number" },
- .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{F21F6F29-76FD-4FC1-8970-17AB8AD64847}").value()
+ .virtualDesktopId = GUID_NULL
};
FancyZonesDataTypes::WorkAreaId id2{
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs
index 9e93f88743..817f54b78b 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Main.cs
@@ -52,7 +52,7 @@ namespace Microsoft.Plugin.Shell
{
Key = "ShellCommandExecution",
DisplayLabel = Resources.wox_shell_command_execution,
- SelectionTypeValue = (int)PluginAdditionalOption.SelectionType.Combobox,
+ PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
ComboBoxOptions = new List
{
Resources.run_command_in_command_prompt,
@@ -60,7 +60,7 @@ namespace Microsoft.Plugin.Shell
Resources.find_executable_file_and_run_it,
Resources.run_command_in_windows_terminal,
},
- Option = (int)_settings.Shell,
+ ComboBoxValue = (int)_settings.Shell,
},
};
@@ -439,7 +439,7 @@ namespace Microsoft.Plugin.Shell
_settings.LeaveShellOpen = leaveShellOpen;
var optionShell = settings.AdditionalOptions.FirstOrDefault(x => x.Key == "ShellCommandExecution");
- shellOption = optionShell?.Option ?? shellOption;
+ shellOption = optionShell?.ComboBoxValue ?? shellOption;
_settings.Shell = (ExecutionShell)shellOption;
}
diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs
index 39f94af55e..1fbe78a166 100644
--- a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs
+++ b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs
@@ -24,6 +24,8 @@ namespace Peek.FilePreviewer.Controls
///
private Uri? _navigatedUri;
+ private Color originalBackgroundColor;
+
public delegate void NavigationCompletedHandler(WebView2? sender, CoreWebView2NavigationCompletedEventArgs? args);
public delegate void DOMContentLoadedHandler(CoreWebView2? sender, CoreWebView2DOMContentLoadedEventArgs? args);
@@ -120,7 +122,11 @@ namespace Peek.FilePreviewer.Controls
{
await PreviewBrowser.EnsureCoreWebView2Async();
- // transparent background when loading the page
+ // Storing the original background color so it can be reset later for specific file types like HTML.
+ originalBackgroundColor = PreviewBrowser.DefaultBackgroundColor;
+
+ // Setting the background color to transparent when initially loading the WebView2 component.
+ // This ensures that non-HTML files are displayed with a transparent background.
PreviewBrowser.DefaultBackgroundColor = Color.FromArgb(0, 0, 0, 0);
PreviewBrowser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;
@@ -150,6 +156,15 @@ namespace Peek.FilePreviewer.Controls
private void CoreWebView2_DOMContentLoaded(CoreWebView2 sender, CoreWebView2DOMContentLoadedEventArgs args)
{
+ // If the file being previewed is HTML or HTM, reset the background color to its original state.
+ // This is done to ensure that HTML and HTM files are displayed as intended, with their own background settings.
+ if (Source?.ToString().EndsWith(".html", StringComparison.OrdinalIgnoreCase) == true ||
+ Source?.ToString().EndsWith(".htm", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ // Reset to default behavior for HTML files
+ PreviewBrowser.DefaultBackgroundColor = originalBackgroundColor;
+ }
+
DOMContentLoaded?.Invoke(sender, args);
}
diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs
index 4a611f2082..c20d9c8264 100644
--- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs
+++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs
@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Collections.Concurrent;
using PowerToys.PowerAccentKeyboardService;
namespace PowerAccent.Core
@@ -80,11 +81,15 @@ namespace PowerAccent.Core
};
}
+ // Store the computed letters for each key, so that subsequent calls don't take as long.
+ private static ConcurrentDictionary _allLanguagesCache = new ConcurrentDictionary();
+
// All
private static string[] GetDefaultLetterKeyALL(LetterKey letter)
{
- // would be even better to loop through Languages and call these functions dynamically, but I don't know how to do that!
- return GetDefaultLetterKeyCA(letter)
+ if (!_allLanguagesCache.ContainsKey(letter))
+ {
+ _allLanguagesCache[letter] = GetDefaultLetterKeyCA(letter)
.Union(GetDefaultLetterKeyCUR(letter))
.Union(GetDefaultLetterKeyCY(letter))
.Union(GetDefaultLetterKeyCZ(letter))
@@ -113,7 +118,10 @@ namespace PowerAccent.Core
.Union(GetDefaultLetterKeySR(letter))
.Union(GetDefaultLetterKeySV(letter))
.Union(GetDefaultLetterKeyTK(letter))
- .ToArray();
+ .ToArray();
+ }
+
+ return _allLanguagesCache[letter];
}
// Currencies (source: https://www.eurochange.co.uk/travel-money/world-currency-abbreviations-symbols-and-codes-travel-money)
diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj
index 8287fd9597..c4051b8f03 100644
--- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj
+++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj
@@ -1,7 +1,7 @@
-
+
true
@@ -208,7 +208,7 @@
-
+
@@ -220,8 +220,8 @@
-
-
+
+
diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config
index eb4be241a8..53ee497bdd 100644
--- a/src/modules/powerrename/PowerRenameUILib/packages.config
+++ b/src/modules/powerrename/PowerRenameUILib/packages.config
@@ -5,5 +5,5 @@
-
+
\ No newline at end of file
diff --git a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs
index 07f0837fdd..bffaa7a3b6 100644
--- a/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs
+++ b/src/modules/registrypreview/RegistryPreviewUI/MainWindow.Utilities.cs
@@ -8,7 +8,9 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Reflection;
+using System.Text;
using System.Threading.Tasks;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
@@ -413,21 +415,75 @@ namespace RegistryPreview
value += registryLine;
}
- // Clean out any escaped characters in the value, only for the preview
- value = StripEscapedCharacters(value);
-
// update the ListViewItem with the loaded value, based off REG value type
switch (registryValue.Type)
{
case "ERROR":
// do nothing
break;
+ case "REG_SZ":
+ if (value == "\"")
+ {
+ // Value is most likely missing an end quote
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidString");
+ }
+ else
+ {
+ for (int i = 1; i < value.Length; i++)
+ {
+ if (value[i - 1] == '\\')
+ {
+ // Only allow these escape characters
+ if (value[i] != '"' && value[i] != '\\')
+ {
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidString");
+ break;
+ }
+
+ i++;
+ }
+
+ if (value[i - 1] != '\\' && value[i] == '"')
+ {
+ // Don't allow non-escaped quotes
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidString");
+ break;
+ }
+ }
+
+ if (registryValue.Type != "ERROR")
+ {
+ // Clean out any escaped characters in the value, only for the preview
+ value = StripEscapedCharacters(value);
+ }
+ }
+
+ registryValue.Value = value;
+ break;
case "REG_BINARY":
case "REG_NONE":
if (value.Length <= 0)
{
value = resourceLoader.GetString("ZeroLength");
}
+ else
+ {
+ try
+ {
+ // Hexes are usually two characters (00), it's invalid if less or more than 2
+ var bytes = value.Split(',').Select(
+ c => c.Length == 2 ? byte.Parse(c, NumberStyles.HexNumber, CultureInfo.InvariantCulture) : throw null);
+ value = string.Join(' ', bytes.Select(b => b.ToString("x2", CultureInfo.CurrentCulture)));
+ }
+ catch
+ {
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidBinary");
+ }
+ }
registryValue.Value = value;
@@ -436,6 +492,19 @@ namespace RegistryPreview
if (value.Length <= 0)
{
registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidDword");
+ }
+ else
+ {
+ if (uint.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint dword))
+ {
+ value = $"0x{dword:x8} ({dword})";
+ }
+ else
+ {
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidDword");
+ }
}
registryValue.Value = value;
@@ -444,8 +513,55 @@ namespace RegistryPreview
case "REG_QWORD":
if (value.Length <= 0)
{
+ registryValue.Type = "ERROR";
value = resourceLoader.GetString("InvalidQword");
}
+ else
+ {
+ try
+ {
+ // Hexes are usually two characters (00), it's invalid if less or more than 2
+ var bytes = value.Split(',').Select(
+ c => c.Length == 2 ? byte.Parse(c, NumberStyles.HexNumber, CultureInfo.InvariantCulture) : throw null).ToArray();
+ ulong qword = BitConverter.ToUInt64(bytes);
+ value = $"0x{qword:x8} ({qword})";
+ }
+ catch
+ {
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidQword");
+ }
+ }
+
+ registryValue.Value = value;
+ break;
+ case "REG_EXPAND_SZ":
+ case "REG_MULTI_SZ":
+ try
+ {
+ // Hexes are usually two characters (00), it's invalid if less or more than 2
+ var bytes = value.Split(',').Select(
+ c => c.Length == 2 ? byte.Parse(c, NumberStyles.HexNumber, CultureInfo.InvariantCulture) : throw null).ToArray();
+
+ if (registryValue.Type == "REG_MULTI_SZ")
+ {
+ // Replace zeros (00,00) with spaces
+ for (int i = 0; i < bytes.Length; i += 2)
+ {
+ if (bytes[i] == 0 && bytes[i + 1] == 0)
+ {
+ bytes[i] = 0x20;
+ }
+ }
+ }
+
+ value = Encoding.Unicode.GetString(bytes);
+ }
+ catch
+ {
+ registryValue.Type = "ERROR";
+ value = resourceLoader.GetString("InvalidString");
+ }
registryValue.Value = value;
break;
@@ -1036,7 +1152,7 @@ namespace RegistryPreview
value = value.Remove(indexOf, value.Length - indexOf);
}
- return value;
+ return value.TrimEnd();
}
///
diff --git a/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs
index a7df53f1f8..161e9cbedd 100644
--- a/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs
+++ b/src/modules/registrypreview/RegistryPreviewUI/RegistryValue.xaml.cs
@@ -33,7 +33,7 @@ namespace RegistryPreview
switch (Type)
{
case "REG_SZ":
- case "REG_EXAND_SZ":
+ case "REG_EXPAND_SZ":
case "REG_MULTI_SZ":
return uriStringValue;
case "ERROR":
diff --git a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw
index 6b766a6570..d637b2e187 100644
--- a/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw
+++ b/src/modules/registrypreview/RegistryPreviewUI/Strings/en-US/Resources.resw
@@ -138,6 +138,12 @@
Registry files (*.reg)
+
+ (Invalid binary value)
+
+
+ (Invalid DWORD (32-bit) value)
+
(Invalid QWORD (64-bit) value)
@@ -147,6 +153,9 @@
File was not a Registry file
+
+ (Invalid string value)
+
is larger than 10MB which is too large for this application.
@@ -228,7 +237,7 @@
User Account Control
- Value
+ Data
Write to Registry
diff --git a/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs b/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs
index 362384c534..54e0c7868e 100644
--- a/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs
+++ b/src/settings-ui/Settings.UI.Library/PluginAdditionalOption.cs
@@ -3,17 +3,28 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class PluginAdditionalOption
{
- public enum SelectionType
+ public enum AdditionalOptionType
{
Checkbox = 0,
Combobox = 1,
+ Textbox = 2,
+ Numberbox = 3,
+ CheckboxAndCombobox = 11,
+ CheckboxAndTextbox = 12,
+ CheckboxAndNumberbox = 13,
}
+ ///
+ /// Gets or sets the layout type of the option in settings ui (Optional; Default is checkbox)
+ ///
+ public AdditionalOptionType PluginOptionType { get; set; }
+
public string Key { get; set; }
public string DisplayLabel { get; set; }
@@ -21,14 +32,64 @@ namespace Microsoft.PowerToys.Settings.UI.Library
///
/// Gets or sets a value to show a description of this setting in the settings ui. (Optional)
///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string DisplayDescription { get; set; }
+ ///
+ /// Gets or sets a value to show a label for the second setting if two combined settings are shown
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string SecondDisplayLabel { get; set; }
+
+ ///
+ /// Gets or sets a value to show a description for the second setting in the settings ui if two combined settings are shown. (Optional)
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string SecondDisplayDescription { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the checkbox is set or not set
+ ///
public bool Value { get; set; }
+ public int ComboBoxValue { get; set; }
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List ComboBoxOptions { get; set; }
- public int Option { get; set; }
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string TextValue { get; set; }
- public int SelectionTypeValue { get; set; }
+ ///
+ /// Gets or sets the value that specifies the maximum number of characters allowed for user input in the text box. (Optional; Default is 0 which means no limit.)
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public int? TextBoxMaxLength { get; set; }
+
+ public double NumberValue { get; set; }
+
+ ///
+ /// Gets or sets a minimal value for the number box. (Optional; Default is Double.MinValue)
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public double? NumberBoxMin { get; set; }
+
+ ///
+ /// Gets or sets a maximal value for the number box. (Optional; Default is Double.MaxValue)
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public double? NumberBoxMax { get; set; }
+
+ ///
+ /// Gets or sets the value for small changes of the number box. (Optional; Default is 1)
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public double? NumberBoxSmallChange { get; set; }
+
+ ///
+ /// Gets or sets the value for large changes of the number box. (Optional; Default is 10)
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public double? NumberBoxLargeChange { get; set; }
}
}
diff --git a/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs b/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs
index 9784a70340..71bf23a92a 100644
--- a/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs
+++ b/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs
@@ -33,6 +33,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
var options = new JsonSerializerOptions
{
WriteIndented = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
ArgumentNullException.ThrowIfNull(settingsUtils);
diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml
index 94fd68d5bc..9e62c42206 100644
--- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml
+++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml
@@ -360,6 +360,7 @@
+
-
+ CornerRadius="0"
+ Visibility="{x:Bind Path=ShowCheckBox, Converter={StaticResource BoolToVisibilityConverter}}">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IsChecked="{x:Bind Path=Value, Mode=TwoWay}" />
+
+
+
+
+
+
+
+
-
-
+ IsChecked="{x:Bind Path=Value, Mode=TwoWay}" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
_additionalOption.DisplayLabel;
public string DisplayDescription => _additionalOption.DisplayDescription;
+ // Labels of second setting of combined types
+ public string SecondDisplayLabel => _additionalOption.SecondDisplayLabel;
+
+ public string SecondDisplayDescription => _additionalOption.SecondDisplayDescription;
+
+ // Bool checkbox setting
+ public bool ShowCheckBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Checkbox;
+
public bool Value
{
get => _additionalOption.Value;
@@ -31,29 +40,83 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
_additionalOption.Value = value;
NotifyPropertyChanged();
+ NotifyPropertyChanged(nameof(SecondSettingIsEnabled));
}
}
}
+ // ComboBox setting
+ public bool ShowComboBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Combobox &&
+ _additionalOption.ComboBoxOptions != null && _additionalOption.ComboBoxOptions.Count > 0;
+
public List ComboBoxOptions => _additionalOption.ComboBoxOptions;
- public int Option
+ public int ComboBoxValue
{
- get => _additionalOption.Option;
+ get => _additionalOption.ComboBoxValue;
set
{
- if (value != _additionalOption.Option)
+ if (value != _additionalOption.ComboBoxValue)
{
- _additionalOption.Option = value;
+ _additionalOption.ComboBoxValue = value;
NotifyPropertyChanged();
}
}
}
- public bool ShowComboBox => _additionalOption.SelectionTypeValue == (int)PluginAdditionalOption.SelectionType.Combobox && _additionalOption.ComboBoxOptions != null && _additionalOption.ComboBoxOptions.Count > 0;
+ // TextBox setting
+ public bool ShowTextBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Textbox;
- public bool ShowCheckBox => _additionalOption.SelectionTypeValue == (int)PluginAdditionalOption.SelectionType.Checkbox;
+ public int TextBoxMaxLength => (_additionalOption.TextBoxMaxLength == null) ? 0 : _additionalOption.TextBoxMaxLength.Value; // 0 ist the default and means no limit.
+ public string TextValue
+ {
+ get => _additionalOption.TextValue;
+ set
+ {
+ if (value != _additionalOption.TextValue)
+ {
+ _additionalOption.TextValue = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+
+ // NumberBox setting
+ public bool ShowNumberBox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.Numberbox;
+
+ public double NumberBoxMin => (_additionalOption.NumberBoxMin == null) ? double.MinValue : _additionalOption.NumberBoxMin.Value;
+
+ public double NumberBoxMax => (_additionalOption.NumberBoxMax == null) ? double.MaxValue : _additionalOption.NumberBoxMax.Value;
+
+ public double NumberBoxSmallChange => (_additionalOption.NumberBoxSmallChange == null) ? 1 : _additionalOption.NumberBoxSmallChange.Value;
+
+ public double NumberBoxLargeChange => (_additionalOption.NumberBoxLargeChange == null) ? 10 : _additionalOption.NumberBoxLargeChange.Value;
+
+ public double NumberValue
+ {
+ get => _additionalOption.NumberValue;
+ set
+ {
+ if (value != _additionalOption.NumberValue)
+ {
+ _additionalOption.NumberValue = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+
+ // Show combined settings cards
+ public bool ShowCheckboxAndCombobox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.CheckboxAndCombobox;
+
+ public bool ShowCheckboxAndTextbox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.CheckboxAndTextbox;
+
+ public bool ShowCheckboxAndNumberbox => _additionalOption.PluginOptionType == PluginAdditionalOption.AdditionalOptionType.CheckboxAndNumberbox;
+
+ // Enabled state of ComboBox, TextBox, NumberBox (If combined with checkbox then checkbox value decides it.)
+ public bool SecondSettingIsEnabled => (int)_additionalOption.PluginOptionType > 10 ? _additionalOption.Value : true;
+
+ // Handle property changes
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")