[FancyZones] Improve code quality (part 6) (#28034)

This commit is contained in:
Seraphima Zykova
2023-08-22 15:57:45 +03:00
committed by GitHub
parent 7c7f6cabf7
commit 6acae53e2c
29 changed files with 2669 additions and 1239 deletions

View File

@@ -26,7 +26,8 @@
#include <FancyZonesLib/Settings.h> #include <FancyZonesLib/Settings.h>
#include <FancyZonesLib/SettingsObserver.h> #include <FancyZonesLib/SettingsObserver.h>
#include <FancyZonesLib/trace.h> #include <FancyZonesLib/trace.h>
#include <FancyZonesLib/WindowDrag.h> #include <FancyZonesLib/WindowKeyboardSnap.h>
#include <FancyZonesLib/WindowMouseSnap.h>
#include <FancyZonesLib/WorkArea.h> #include <FancyZonesLib/WorkArea.h>
enum class DisplayChangeType enum class DisplayChangeType
@@ -142,10 +143,6 @@ protected:
private: private:
void UpdateWorkAreas(bool updateWindowPositions) noexcept; void UpdateWorkAreas(bool updateWindowPositions) noexcept;
void CycleWindows(bool reverse) noexcept; void CycleWindows(bool reverse) noexcept;
bool OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept;
bool OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept;
bool OnSnapHotkey(DWORD vkCode) noexcept;
bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept;
void SyncVirtualDesktops() noexcept; void SyncVirtualDesktops() noexcept;
@@ -153,13 +150,11 @@ private:
bool MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept; bool MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept;
void UpdateActiveLayouts() noexcept; void RefreshLayouts() noexcept;
bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept; bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept;
void ApplyQuickLayout(int key) noexcept; void ApplyQuickLayout(int key) noexcept;
void FlashZones() noexcept; void FlashZones() noexcept;
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
HMONITOR WorkAreaKeyFromWindow(HWND window) noexcept; HMONITOR WorkAreaKeyFromWindow(HWND window) noexcept;
virtual void SettingsUpdate(SettingId type) override; virtual void SettingsUpdate(SettingId type) override;
@@ -167,7 +162,8 @@ private:
const HINSTANCE m_hinstance{}; const HINSTANCE m_hinstance{};
HWND m_window{}; HWND m_window{};
std::unique_ptr<WindowDrag> m_windowDrag{}; std::unique_ptr<WindowMouseSnap> m_windowMouseSnapper{};
WindowKeyboardSnap m_windowKeyboardSnapper{};
MonitorWorkAreaMap m_workAreaHandler; MonitorWorkAreaMap m_workAreaHandler;
DraggingState m_draggingState; DraggingState m_draggingState;
@@ -288,8 +284,8 @@ FancyZones::VirtualDesktopChanged() noexcept
void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor) void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
{ {
m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetAllWorkAreas()); m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaHandler.GetAllWorkAreas());
if (m_windowDrag) if (m_windowMouseSnapper)
{ {
if (FancyZonesSettings::settings().spanZonesAcrossMonitors) if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
{ {
@@ -298,13 +294,13 @@ void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
m_draggingState.Enable(); m_draggingState.Enable();
m_draggingState.UpdateDraggingState(); m_draggingState.UpdateDraggingState();
m_windowDrag->MoveSizeStart(monitor, m_draggingState.IsDragging()); m_windowMouseSnapper->MoveSizeStart(monitor, m_draggingState.IsDragging());
} }
} }
void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen) void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen)
{ {
if (m_windowDrag) if (m_windowMouseSnapper)
{ {
if (FancyZonesSettings::settings().spanZonesAcrossMonitors) if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
{ {
@@ -312,17 +308,17 @@ void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen)
} }
m_draggingState.UpdateDraggingState(); m_draggingState.UpdateDraggingState();
m_windowDrag->MoveSizeUpdate(monitor, ptScreen, m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState()); m_windowMouseSnapper->MoveSizeUpdate(monitor, ptScreen, m_draggingState.IsDragging(), m_draggingState.IsSelectManyZonesState());
} }
} }
void FancyZones::MoveSizeEnd() void FancyZones::MoveSizeEnd()
{ {
if (m_windowDrag) if (m_windowMouseSnapper)
{ {
m_windowDrag->MoveSizeEnd(); m_windowMouseSnapper->MoveSizeEnd();
m_draggingState.Disable(); m_draggingState.Disable();
m_windowDrag = nullptr; m_windowMouseSnapper = nullptr;
} }
} }
@@ -339,7 +335,7 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
workArea = workAreas.at(monitor).get(); workArea = workAreas.at(monitor).get();
if (workArea) if (workArea)
{ {
indexes = workArea->GetWindowZoneIndexes(window); indexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId());
} }
} }
else else
@@ -353,7 +349,7 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
{ {
if (secondaryWorkArea) if (secondaryWorkArea)
{ {
indexes = secondaryWorkArea->GetWindowZoneIndexes(window); indexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, secondaryWorkArea->UniqueId(), secondaryWorkArea->GetLayoutId());
workArea = secondaryWorkArea.get(); workArea = secondaryWorkArea.get();
if (!indexes.empty()) if (!indexes.empty())
{ {
@@ -365,8 +361,8 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
if (!indexes.empty() && workArea) if (!indexes.empty() && workArea)
{ {
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get()); Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
workArea->MoveWindowIntoZoneByIndexSet(window, indexes); workArea->Snap(window, indexes);
return true; return true;
} }
@@ -604,7 +600,40 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
if (message == WM_PRIV_SNAP_HOTKEY) if (message == WM_PRIV_SNAP_HOTKEY)
{ {
OnSnapHotkey(static_cast<DWORD>(lparam)); // We already checked in ShouldProcessSnapHotkey whether the foreground window is a candidate for zoning
auto foregroundWindow = GetForegroundWindow();
HMONITOR monitor{ nullptr };
if (!FancyZonesSettings::settings().spanZonesAcrossMonitors)
{
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTONULL);
}
if (FancyZonesSettings::settings().moveWindowsBasedOnPosition)
{
auto monitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
RECT windowRect;
if (GetWindowRect(foregroundWindow, &windowRect))
{
// Check whether Alt is used in the shortcut key combination
if (GetAsyncKeyState(VK_MENU) & 0x8000)
{
m_windowKeyboardSnapper.Extend(foregroundWindow, windowRect, monitor, static_cast<DWORD>(lparam), m_workAreaHandler.GetAllWorkAreas());
}
else
{
m_windowKeyboardSnapper.Snap(foregroundWindow, windowRect, monitor, static_cast<DWORD>(lparam), m_workAreaHandler.GetAllWorkAreas(), monitors);
}
}
else
{
Logger::error("Error snapping window by keyboard shortcut: failed to get window rect");
}
}
else
{
m_windowKeyboardSnapper.Snap(foregroundWindow, monitor, static_cast<DWORD>(lparam), m_workAreaHandler.GetAllWorkAreas(), FancyZonesUtils::GetMonitorsOrdered());
}
} }
else if (message == WM_PRIV_INIT) else if (message == WM_PRIV_INIT)
{ {
@@ -661,7 +690,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE) else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE)
{ {
AppliedLayouts::instance().LoadData(); AppliedLayouts::instance().LoadData();
UpdateActiveLayouts(); RefreshLayouts();
} }
else if (message == WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE) else if (message == WM_PRIV_DEFAULT_LAYOUTS_FILE_UPDATE)
{ {
@@ -800,7 +829,7 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
{ {
for (const auto& [window, zones] : windowsToSnap) for (const auto& [window, zones] : windowsToSnap)
{ {
workArea->SnapWindow(window, zones, false); workArea->Snap(window, zones, false);
} }
} }
} }
@@ -813,9 +842,9 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
const auto zones = iter->second; const auto zones = iter->second;
const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
const auto workAreaForMonitor = m_workAreaHandler.GetWorkArea(monitor); const auto workAreaForMonitor = m_workAreaHandler.GetWorkArea(monitor);
if (workAreaForMonitor && workAreaForMonitor->GetWindowZoneIndexes(window) == zones) if (workAreaForMonitor && AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaForMonitor->UniqueId(), workAreaForMonitor->GetLayoutId()) == zones)
{ {
workAreaForMonitor->SnapWindow(window, zones, false); workAreaForMonitor->Snap(window, zones, false);
iter = windowsToSnap.erase(iter); iter = windowsToSnap.erase(iter);
} }
else else
@@ -829,10 +858,10 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
{ {
for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
{ {
const auto savedIndexes = workArea->GetWindowZoneIndexes(window); const auto savedIndexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId());
if (savedIndexes == zones) if (savedIndexes == zones)
{ {
workArea->SnapWindow(window, zones, false); workArea->Snap(window, zones, false);
} }
} }
} }
@@ -862,257 +891,6 @@ void FancyZones::CycleWindows(bool reverse) noexcept
} }
} }
bool FancyZones::OnSnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode) noexcept
{
HMONITOR current = WorkAreaKeyFromWindow(window);
std::vector<HMONITOR> monitorInfo = GetMonitorsSorted();
if (current && monitorInfo.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
{
// Multi monitor environment.
auto currMonitorInfo = std::find(std::begin(monitorInfo), std::end(monitorInfo), current);
do
{
auto workArea = m_workAreaHandler.GetWorkArea(*currMonitorInfo);
if (workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */))
{
// unassign from previous work area
for (auto& [_, prevWorkArea] : m_workAreaHandler.GetAllWorkAreas())
{
if (prevWorkArea && workArea != prevWorkArea.get())
{
prevWorkArea->UnsnapWindow(window);
}
}
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
return true;
}
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
if (vkCode == VK_RIGHT)
{
currMonitorInfo = std::next(currMonitorInfo);
if (currMonitorInfo == std::end(monitorInfo))
{
currMonitorInfo = std::begin(monitorInfo);
}
}
else if (vkCode == VK_LEFT)
{
if (currMonitorInfo == std::begin(monitorInfo))
{
currMonitorInfo = std::end(monitorInfo);
}
currMonitorInfo = std::prev(currMonitorInfo);
}
} while (*currMonitorInfo != current);
}
else
{
auto workArea = m_workAreaHandler.GetWorkArea(current);
// Single monitor environment, or combined multi-monitor environment.
if (FancyZonesSettings::settings().restoreSize)
{
bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, false /* cycle through zones */);
if (!moved)
{
FancyZonesWindowUtils::RestoreWindowOrigin(window);
FancyZonesWindowUtils::RestoreWindowSize(window);
}
else if (workArea)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
}
return moved;
}
else
{
bool moved = workArea && workArea->MoveWindowIntoZoneByDirectionAndIndex(window, vkCode, true /* cycle through zones */);
if (moved)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
}
return moved;
}
}
return false;
}
bool FancyZones::OnSnapHotkeyBasedOnPosition(HWND window, DWORD vkCode) noexcept
{
HMONITOR current = WorkAreaKeyFromWindow(window);
auto allMonitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
if (current && allMonitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
{
// Multi monitor environment.
// First, try to stay on the same monitor
bool success = ProcessDirectedSnapHotkey(window, vkCode, false, m_workAreaHandler.GetWorkArea(current));
if (success)
{
return true;
}
// If that didn't work, extract zones from all other monitors and target one of them
std::vector<RECT> zoneRects;
std::vector<std::pair<ZoneIndex, WorkArea*>> zoneRectsInfo;
RECT currentMonitorRect{ .top = 0, .bottom = -1 };
for (const auto& [monitor, monitorRect] : allMonitors)
{
if (monitor == current)
{
currentMonitorRect = monitorRect;
}
else
{
auto workArea = m_workAreaHandler.GetWorkArea(monitor);
if (workArea)
{
const auto& layout = workArea->GetLayout();
if (layout)
{
const auto& zones = layout->Zones();
for (const auto& [zoneId, zone] : zones)
{
RECT zoneRect = zone.GetZoneRect();
zoneRect.left += monitorRect.left;
zoneRect.right += monitorRect.left;
zoneRect.top += monitorRect.top;
zoneRect.bottom += monitorRect.top;
zoneRects.emplace_back(zoneRect);
zoneRectsInfo.emplace_back(zoneId, workArea);
}
}
}
}
}
// Ensure we can get the windowRect, if not, just quit
RECT windowRect;
if (!GetWindowRect(window, &windowRect))
{
return false;
}
auto chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (chosenIdx < zoneRects.size())
{
// Moving to another monitor succeeded
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
if (workArea)
{
workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx });
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
}
return true;
}
// We reached the end of all monitors.
// Try again, cycling on all monitors.
// First, add zones from the origin monitor to zoneRects
// Sanity check: the current monitor is valid
if (currentMonitorRect.top <= currentMonitorRect.bottom)
{
auto workArea = m_workAreaHandler.GetWorkArea(current);
if (workArea)
{
const auto& layout = workArea->GetLayout();
if (layout)
{
const auto& zones = layout->Zones();
for (const auto& [zoneId, zone] : zones)
{
RECT zoneRect = zone.GetZoneRect();
zoneRect.left += currentMonitorRect.left;
zoneRect.right += currentMonitorRect.left;
zoneRect.top += currentMonitorRect.top;
zoneRect.bottom += currentMonitorRect.top;
zoneRects.emplace_back(zoneRect);
zoneRectsInfo.emplace_back(zoneId, workArea);
}
}
}
}
else
{
return false;
}
RECT combinedRect = FancyZonesUtils::GetAllMonitorsCombinedRect<&MONITORINFOEX::rcWork>();
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, combinedRect, vkCode);
chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (chosenIdx < zoneRects.size())
{
// Moving to another monitor succeeded
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
if (workArea)
{
workArea->MoveWindowIntoZoneByIndexSet(window, { trueZoneIdx });
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
}
return true;
}
else
{
// Giving up
return false;
}
}
else
{
// Single monitor environment, or combined multi-monitor environment.
return ProcessDirectedSnapHotkey(window, vkCode, true, m_workAreaHandler.GetWorkArea(current));
}
}
bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
{
// We already checked in ShouldProcessSnapHotkey whether the foreground window is a candidate for zoning
auto window = GetForegroundWindow();
if (FancyZonesSettings::settings().moveWindowsBasedOnPosition)
{
return OnSnapHotkeyBasedOnPosition(window, vkCode);
}
return (vkCode == VK_LEFT || vkCode == VK_RIGHT) && OnSnapHotkeyBasedOnZoneNumber(window, vkCode);
}
bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept
{
// Check whether Alt is used in the shortcut key combination
if (GetAsyncKeyState(VK_MENU) & 0x8000)
{
bool result = workArea && workArea->ExtendWindowByDirectionAndPosition(window, vkCode);
if (result)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
}
return result;
}
else
{
bool result = workArea && workArea->MoveWindowIntoZoneByDirectionAndPosition(window, vkCode, cycle);
if (result)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
}
return result;
}
}
void FancyZones::SyncVirtualDesktops() noexcept void FancyZones::SyncVirtualDesktops() noexcept
{ {
auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry(); auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry();
@@ -1186,13 +964,13 @@ void FancyZones::SettingsUpdate(SettingId id)
} }
} }
void FancyZones::UpdateActiveLayouts() noexcept void FancyZones::RefreshLayouts() noexcept
{ {
for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas()) for (const auto& [_, workArea] : m_workAreaHandler.GetAllWorkAreas())
{ {
if (workArea) if (workArea)
{ {
workArea->UpdateActiveZoneSet(); workArea->InitLayout();
if (FancyZonesSettings::settings().zoneSetChange_moveWindows) if (FancyZonesSettings::settings().zoneSetChange_moveWindows)
{ {
@@ -1265,7 +1043,7 @@ void FancyZones::ApplyQuickLayout(int key) noexcept
{ {
AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value()); AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value());
AppliedLayouts::instance().SaveData(); AppliedLayouts::instance().SaveData();
UpdateActiveLayouts(); RefreshLayouts();
FlashZones(); FlashZones();
} }
} }
@@ -1284,32 +1062,6 @@ void FancyZones::FlashZones() noexcept
} }
} }
std::vector<HMONITOR> FancyZones::GetMonitorsSorted() noexcept
{
auto monitorInfo = GetRawMonitorData();
FancyZonesUtils::OrderMonitors(monitorInfo);
std::vector<HMONITOR> output;
std::transform(std::begin(monitorInfo), std::end(monitorInfo), std::back_inserter(output), [](const auto& info) { return info.first; });
return output;
}
std::vector<std::pair<HMONITOR, RECT>> FancyZones::GetRawMonitorData() noexcept
{
std::vector<std::pair<HMONITOR, RECT>> monitorInfo;
const auto& activeWorkAreaMap = m_workAreaHandler.GetAllWorkAreas();
for (const auto& [monitor, workArea] : activeWorkAreaMap)
{
if (workArea && workArea->GetLayout() != nullptr)
{
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(monitor, &mi);
monitorInfo.push_back({ monitor, mi.rcMonitor });
}
}
return monitorInfo;
}
HMONITOR FancyZones::WorkAreaKeyFromWindow(HWND window) noexcept HMONITOR FancyZones::WorkAreaKeyFromWindow(HWND window) noexcept
{ {
if (FancyZonesSettings::settings().spanZonesAcrossMonitors) if (FancyZonesSettings::settings().spanZonesAcrossMonitors)

View File

@@ -103,13 +103,14 @@ namespace JsonUtils
} }
data.workAreaId = deviceIdOpt.value(); data.workAreaId = deviceIdOpt.value();
data.zoneSetUuid = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID); std::wstring layoutIdStr = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID).c_str();
auto layoutIdOpt = FancyZonesUtils::GuidFromString(layoutIdStr);
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid)) if (!layoutIdOpt.has_value())
{ {
return std::nullopt; return std::nullopt;
} }
data.layoutId = layoutIdOpt.value();
return data; return data;
} }
@@ -187,7 +188,11 @@ namespace JsonUtils
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID, jsonIndexSet); desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID, jsonIndexSet);
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::DeviceID, device); desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::DeviceID, device);
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIdID, json::value(data.zoneSetUuid)); auto layoutIdStr = FancyZonesUtils::GuidToString(data.layoutId);
if (layoutIdStr)
{
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIdID, json::value(layoutIdStr.value()));
}
appHistoryArray.Append(desktopData); appHistoryArray.Append(desktopData);
} }
@@ -334,7 +339,7 @@ void AppZoneHistory::AdjustWorkAreaIds(const std::vector<FancyZonesDataTypes::Mo
} }
} }
bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet) bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId, const ZoneIndexSet& zoneIndexSet)
{ {
if (IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId)) if (IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
{ {
@@ -347,7 +352,11 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
return false; return false;
} }
Logger::info(L"Add app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId); auto layoutIdStr = FancyZonesUtils::GuidToString(layoutId);
if (layoutIdStr)
{
Logger::info(L"Add app zone history, device: {}, layout: {}", workAreaId.toString(), layoutIdStr.value());
}
DWORD processId = 0; DWORD processId = 0;
GetWindowThreadProcessId(window, &processId); GetWindowThreadProcessId(window, &processId);
@@ -362,7 +371,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
{ {
// application already has history on this work area, update it with new window position // application already has history on this work area, update it with new window position
data.processIdToHandleMap[processId] = window; data.processIdToHandleMap[processId] = window;
data.zoneSetUuid = zoneSetId; data.layoutId = layoutId;
data.zoneIndexSet = zoneIndexSet; data.zoneIndexSet = zoneIndexSet;
SaveData(); SaveData();
return true; return true;
@@ -373,7 +382,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
std::unordered_map<DWORD, HWND> processIdToHandleMap{}; std::unordered_map<DWORD, HWND> processIdToHandleMap{};
processIdToHandleMap[processId] = window; processIdToHandleMap[processId] = window;
FancyZonesDataTypes::AppZoneHistoryData data{ .processIdToHandleMap = processIdToHandleMap, FancyZonesDataTypes::AppZoneHistoryData data{ .processIdToHandleMap = processIdToHandleMap,
.zoneSetUuid = zoneSetId, .layoutId = layoutId,
.workAreaId = workAreaId, .workAreaId = workAreaId,
.zoneIndexSet = zoneIndexSet }; .zoneIndexSet = zoneIndexSet };
@@ -392,20 +401,33 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
return true; return true;
} }
bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId) bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId)
{ {
Logger::info(L"Remove app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId);
auto processPath = get_process_path_waiting_uwp(window); auto processPath = get_process_path_waiting_uwp(window);
if (!processPath.empty()) if (processPath.empty())
{ {
return false;
}
auto history = m_history.find(processPath); auto history = m_history.find(processPath);
if (history != std::end(m_history)) if (history == std::end(m_history))
{ {
return false;
}
auto layoutIdStrOpt = FancyZonesUtils::GuidToString(layoutId);
if (!layoutIdStrOpt)
{
Logger::error("Invalid layout id");
return false;
}
Logger::info(L"Remove app zone history, device: {}, layout: {}", workAreaId.toString(), layoutIdStrOpt.value());
auto& perDesktopData = history->second; auto& perDesktopData = history->second;
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);) for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
{ {
if (data->workAreaId == workAreaId && data->zoneSetUuid == zoneSetId) if (data->workAreaId == workAreaId && data->layoutId == layoutId)
{ {
if (!IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId)) if (!IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
{ {
@@ -439,9 +461,6 @@ bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::W
++data; ++data;
} }
} }
}
}
return false; return false;
} }
@@ -532,7 +551,7 @@ bool AppZoneHistory::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
return false; return false;
} }
ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId) const
{ {
auto processPath = get_process_path_waiting_uwp(window); auto processPath = get_process_path_waiting_uwp(window);
if (processPath.empty()) if (processPath.empty())
@@ -559,7 +578,7 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
const auto& perDesktopData = history->second; const auto& perDesktopData = history->second;
for (const auto& data : perDesktopData) for (const auto& data : perDesktopData)
{ {
if (data.zoneSetUuid == zoneSetId && data.workAreaId == workAreaId) if (data.layoutId == layoutId && data.workAreaId == workAreaId)
{ {
if (data.workAreaId.virtualDesktopId == workAreaId.virtualDesktopId || data.workAreaId.virtualDesktopId == GUID_NULL) if (data.workAreaId.virtualDesktopId == workAreaId.virtualDesktopId || data.workAreaId.virtualDesktopId == GUID_NULL)
{ {

View File

@@ -45,8 +45,8 @@ public:
void SaveData(); void SaveData();
void AdjustWorkAreaIds(const std::vector<FancyZonesDataTypes::MonitorId>& ids); void AdjustWorkAreaIds(const std::vector<FancyZonesDataTypes::MonitorId>& ids);
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet); bool SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId, const ZoneIndexSet& zoneIndexSet);
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId); bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId);
void RemoveApp(const std::wstring& appPath); void RemoveApp(const std::wstring& appPath);
@@ -54,7 +54,7 @@ public:
std::optional<FancyZonesDataTypes::AppZoneHistoryData> GetZoneHistory(const std::wstring& appPath, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept; std::optional<FancyZonesDataTypes::AppZoneHistoryData> GetZoneHistory(const std::wstring& appPath, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept;
bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept; bool IsAnotherWindowOfApplicationInstanceZoned(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId) const noexcept;
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const; ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId) const;
void SyncVirtualDesktops(); void SyncVirtualDesktops();
void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops); void RemoveDeletedVirtualDesktops(const std::vector<GUID>& activeDesktops);

View File

@@ -144,9 +144,9 @@ namespace FancyZonesDataTypes
{ {
std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND) std::unordered_map<DWORD, HWND> processIdToHandleMap; // Maps process id(DWORD) of application to zoned window handle(HWND)
std::wstring zoneSetUuid; GUID layoutId = {};
WorkAreaId workAreaId; WorkAreaId workAreaId = {};
ZoneIndexSet zoneIndexSet; ZoneIndexSet zoneIndexSet = {};
}; };
struct DeviceInfoData struct DeviceInfoData

View File

@@ -71,7 +71,8 @@
<ClInclude Include="trace.h" /> <ClInclude Include="trace.h" />
<ClInclude Include="util.h" /> <ClInclude Include="util.h" />
<ClInclude Include="VirtualDesktop.h" /> <ClInclude Include="VirtualDesktop.h" />
<ClInclude Include="WindowDrag.h" /> <ClInclude Include="WindowKeyboardSnap.h" />
<ClInclude Include="WindowMouseSnap.h" />
<ClInclude Include="FancyZonesWindowProperties.h" /> <ClInclude Include="FancyZonesWindowProperties.h" />
<ClInclude Include="WindowUtils.h" /> <ClInclude Include="WindowUtils.h" />
<ClInclude Include="Zone.h" /> <ClInclude Include="Zone.h" />
@@ -123,7 +124,8 @@
<ClCompile Include="trace.cpp" /> <ClCompile Include="trace.cpp" />
<ClCompile Include="util.cpp" /> <ClCompile Include="util.cpp" />
<ClCompile Include="VirtualDesktop.cpp" /> <ClCompile Include="VirtualDesktop.cpp" />
<ClCompile Include="WindowDrag.cpp" /> <ClCompile Include="WindowKeyboardSnap.cpp" />
<ClCompile Include="WindowMouseSnap.cpp" />
<ClCompile Include="WindowUtils.cpp" /> <ClCompile Include="WindowUtils.cpp" />
<ClCompile Include="Zone.cpp" /> <ClCompile Include="Zone.cpp" />
<ClCompile Include="WorkArea.cpp" /> <ClCompile Include="WorkArea.cpp" />

View File

@@ -162,7 +162,10 @@
<ClInclude Include="DraggingState.h"> <ClInclude Include="DraggingState.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="WindowDrag.h"> <ClInclude Include="WindowMouseSnap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WindowKeyboardSnap.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>
@@ -263,7 +266,10 @@
<ClCompile Include="DraggingState.cpp"> <ClCompile Include="DraggingState.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="WindowDrag.cpp"> <ClCompile Include="WindowMouseSnap.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WindowKeyboardSnap.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>

View File

@@ -15,7 +15,7 @@ namespace ZonedWindowProperties
const wchar_t PropertySortKeyWithinZone[] = L"FancyZones_TabSortKeyWithinZone"; const wchar_t PropertySortKeyWithinZone[] = L"FancyZones_TabSortKeyWithinZone";
} }
void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet) bool FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet)
{ {
RemoveZoneIndexProperty(window); RemoveZoneIndexProperty(window);
ZoneIndexSetBitmask bitmask = ZoneIndexSetBitmask::FromIndexSet(zoneSet); ZoneIndexSetBitmask bitmask = ZoneIndexSetBitmask::FromIndexSet(zoneSet);
@@ -33,6 +33,7 @@ void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneI
if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone64ID, rawData)) if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone64ID, rawData))
{ {
Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError())); Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError()));
return false;
} }
} }
@@ -49,8 +50,11 @@ void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneI
if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone128ID, rawData)) if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone128ID, rawData))
{ {
Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError())); Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError()));
return false;
} }
} }
return true;
} }
void FancyZonesWindowProperties::RemoveZoneIndexProperty(HWND window) void FancyZonesWindowProperties::RemoveZoneIndexProperty(HWND window)

View File

@@ -18,7 +18,7 @@ namespace ZonedWindowProperties
namespace FancyZonesWindowProperties namespace FancyZonesWindowProperties
{ {
void StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet); bool StampZoneIndexProperty(HWND window, const ZoneIndexSet& zoneSet);
void RemoveZoneIndexProperty(HWND window); void RemoveZoneIndexProperty(HWND window);
ZoneIndexSet RetrieveZoneIndexProperty(HWND window); ZoneIndexSet RetrieveZoneIndexProperty(HWND window);

View File

@@ -277,13 +277,16 @@ namespace
.monitorId = { .deviceId = MonitorUtils::Display::ConvertObsoleteDeviceId(deviceId->deviceName) }, .monitorId = { .deviceId = MonitorUtils::Display::ConvertObsoleteDeviceId(deviceId->deviceName) },
.virtualDesktopId = deviceId->virtualDesktopId .virtualDesktopId = deviceId->virtualDesktopId
}; };
data.zoneSetUuid = json.GetNamedString(NonLocalizable::ZoneSetUuidStr);
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid)) std::wstring layoutIdStr = json.GetNamedString(NonLocalizable::ZoneSetUuidStr).c_str();
auto layoutIdOpt = FancyZonesUtils::GuidFromString(layoutIdStr);
if (!layoutIdOpt.has_value())
{ {
return std::nullopt; return std::nullopt;
} }
data.layoutId = layoutIdOpt.value();
return data; return data;
} }

View File

@@ -6,37 +6,10 @@
#include <FancyZonesLib/VirtualDesktop.h> #include <FancyZonesLib/VirtualDesktop.h>
#include <FancyZonesLib/WindowUtils.h> #include <FancyZonesLib/WindowUtils.h>
LayoutAssignedWindows::LayoutAssignedWindows()
{
m_extendData = std::make_unique<ExtendWindowModeData>();
}
void LayoutAssignedWindows::Assign(HWND window, const ZoneIndexSet& zones) void LayoutAssignedWindows::Assign(HWND window, const ZoneIndexSet& zones)
{ {
Dismiss(window); Dismiss(window);
// clear info about extension
std::erase_if(m_extendData->windowInitialIndexSet, [window](const auto& item) { return item.first == window; });
std::erase_if(m_extendData->windowFinalIndex, [window](const auto& item) { return item.first == window; });
for (const auto& index : zones)
{
m_windowIndexSet[window].push_back(index);
}
if (FancyZonesSettings::settings().disableRoundCorners)
{
FancyZonesWindowUtils::DisableRoundCorners(window);
}
auto tabSortKeyWithinZone = FancyZonesWindowProperties::GetTabSortKeyWithinZone(window);
InsertWindowIntoZone(window, tabSortKeyWithinZone, zones);
}
void LayoutAssignedWindows::Extend(HWND window, const ZoneIndexSet& zones)
{
Dismiss(window);
for (const auto& index : zones) for (const auto& index : zones)
{ {
m_windowIndexSet[window].push_back(index); m_windowIndexSet[window].push_back(index);
@@ -133,11 +106,6 @@ void LayoutAssignedWindows::CycleWindows(HWND window, bool reverse)
} }
} }
const std::unique_ptr<LayoutAssignedWindows::ExtendWindowModeData>& LayoutAssignedWindows::ExtendWindowData()
{
return m_extendData;
}
void LayoutAssignedWindows::InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet) void LayoutAssignedWindows::InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet)
{ {
if (tabSortKeyWithinZone.has_value()) if (tabSortKeyWithinZone.has_value())

View File

@@ -5,18 +5,10 @@
class LayoutAssignedWindows class LayoutAssignedWindows
{ {
public : public :
struct ExtendWindowModeData LayoutAssignedWindows() = default;
{
std::map<HWND, ZoneIndexSet> windowInitialIndexSet;
std::map<HWND, ZoneIndex> windowFinalIndex;
};
public :
LayoutAssignedWindows();
~LayoutAssignedWindows() = default; ~LayoutAssignedWindows() = default;
void Assign(HWND window, const ZoneIndexSet& zones); void Assign(HWND window, const ZoneIndexSet& zones);
void Extend(HWND window, const ZoneIndexSet& zones);
void Dismiss(HWND window); void Dismiss(HWND window);
std::map<HWND, ZoneIndexSet> SnappedWindows() const noexcept; std::map<HWND, ZoneIndexSet> SnappedWindows() const noexcept;
@@ -25,12 +17,9 @@ public :
void CycleWindows(HWND window, bool reverse); void CycleWindows(HWND window, bool reverse);
const std::unique_ptr<ExtendWindowModeData>& ExtendWindowData();
private: private:
std::map<HWND, ZoneIndexSet> m_windowIndexSet{}; std::map<HWND, ZoneIndexSet> m_windowIndexSet{};
std::map<ZoneIndexSet, std::vector<HWND>> m_windowsByIndexSets{}; std::map<ZoneIndexSet, std::vector<HWND>> m_windowsByIndexSets{};
std::unique_ptr<ExtendWindowModeData> m_extendData{}; // Needed for ExtendWindowByDirectionAndPosition
void InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet); void InsertWindowIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet);
HWND GetNextZoneWindow(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept; HWND GetNextZoneWindow(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;

View File

@@ -80,6 +80,13 @@ public:
#endif #endif
} }
#if defined(UNIT_TESTS)
inline void SetSettings(const Settings& settings)
{
m_settings = settings;
}
#endif
void AddObserver(SettingsObserver& observer); void AddObserver(SettingsObserver& observer);
void RemoveObserver(SettingsObserver& observer); void RemoveObserver(SettingsObserver& observer);

View File

@@ -0,0 +1,489 @@
#include "pch.h"
#include "WindowKeyboardSnap.h"
#include <FancyZonesLib/Settings.h>
#include <FancyZonesLib/trace.h>
#include <FancyZonesLib/WindowUtils.h>
#include <FancyZonesLib/WorkArea.h>
#include <FancyZonesLib/util.h>
#include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
bool WindowKeyboardSnap::Snap(HWND window, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<HMONITOR>& monitors)
{
return (vkCode == VK_LEFT || vkCode == VK_RIGHT) && SnapHotkeyBasedOnZoneNumber(window, vkCode, monitor, activeWorkAreas, monitors);
}
bool WindowKeyboardSnap::Snap(HWND window, RECT windowRect, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<std::pair<HMONITOR, RECT>>& monitors)
{
if (!activeWorkAreas.contains(monitor))
{
return false;
}
// clean previous extension data
m_extendData.Reset();
const auto& currentWorkArea = activeWorkAreas.at(monitor);
if (monitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
{
// Multi monitor environment.
// First, try to stay on the same monitor
bool success = MoveByDirectionAndPosition(window, windowRect, vkCode, false, currentWorkArea.get());
if (success)
{
return true;
}
// Try to snap on another monitor
success = SnapBasedOnPositionOnAnotherMonitor(window, windowRect, vkCode, monitor, activeWorkAreas, monitors);
if (success)
{
// Unsnap from previous work area
currentWorkArea->Unsnap(window);
}
return success;
}
else
{
// Single monitor environment, or combined multi-monitor environment.
return MoveByDirectionAndPosition(window, windowRect, vkCode, true, currentWorkArea.get());
}
}
bool WindowKeyboardSnap::Extend(HWND window, RECT windowRect, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
{
if (!activeWorkAreas.contains(monitor))
{
return false;
}
// continue extension process
const auto& workArea = activeWorkAreas.at(monitor);
return Extend(window, windowRect, vkCode, workArea.get());
}
bool WindowKeyboardSnap::SnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode, HMONITOR current, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<HMONITOR>& monitors)
{
// clean previous extension data
m_extendData.Reset();
if (current && monitors.size() > 1 && FancyZonesSettings::settings().moveWindowAcrossMonitors)
{
// Multi monitor environment.
auto currMonitor = std::find(std::begin(monitors), std::end(monitors), current);
do
{
if (activeWorkAreas.contains(*currMonitor))
{
const auto& workArea = activeWorkAreas.at(*currMonitor);
if (MoveByDirectionAndIndex(window, vkCode, false /* cycle through zones */, workArea.get()))
{
// unassign from previous work area
for (auto& [_, prevWorkArea] : activeWorkAreas)
{
if (prevWorkArea && workArea != prevWorkArea)
{
prevWorkArea->Unsnap(window);
}
}
return true;
}
// We iterated through all zones in current monitor zone layout, move on to next one (or previous depending on direction).
if (vkCode == VK_RIGHT)
{
currMonitor = std::next(currMonitor);
if (currMonitor == std::end(monitors))
{
currMonitor = std::begin(monitors);
}
}
else if (vkCode == VK_LEFT)
{
if (currMonitor == std::begin(monitors))
{
currMonitor = std::end(monitors);
}
currMonitor = std::prev(currMonitor);
}
}
} while (*currMonitor != current);
}
else
{
if (activeWorkAreas.contains(current))
{
const auto& workArea = activeWorkAreas.at(current);
bool moved = MoveByDirectionAndIndex(window, vkCode, FancyZonesSettings::settings().moveWindowAcrossMonitors /* cycle through zones */, workArea.get());
if (FancyZonesSettings::settings().restoreSize && !moved)
{
FancyZonesWindowUtils::RestoreWindowOrigin(window);
FancyZonesWindowUtils::RestoreWindowSize(window);
}
return moved;
}
}
return false;
}
bool WindowKeyboardSnap::SnapBasedOnPositionOnAnotherMonitor(HWND window, RECT windowRect, DWORD vkCode, HMONITOR current, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<std::pair<HMONITOR, RECT>>& monitors)
{
// Extract zones from all other monitors and target one of them
std::vector<RECT> zoneRects;
std::vector<std::pair<ZoneIndex, WorkArea*>> zoneRectsInfo;
RECT currentMonitorRect{ .top = 0, .bottom = -1 };
for (const auto& [monitor, monitorRect] : monitors)
{
if (monitor == current)
{
currentMonitorRect = monitorRect;
}
else
{
if (activeWorkAreas.contains(monitor))
{
const auto& workArea = activeWorkAreas.at(monitor);
const auto& layout = workArea->GetLayout();
if (layout)
{
const auto& zones = layout->Zones();
for (const auto& [zoneId, zone] : zones)
{
RECT zoneRect = zone.GetZoneRect();
zoneRect.left += monitorRect.left;
zoneRect.right += monitorRect.left;
zoneRect.top += monitorRect.top;
zoneRect.bottom += monitorRect.top;
zoneRects.emplace_back(zoneRect);
zoneRectsInfo.emplace_back(zoneId, workArea.get());
}
}
}
}
}
auto chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (chosenIdx < zoneRects.size())
{
// Moving to another monitor succeeded
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
bool snapped = false;
if (workArea)
{
snapped = workArea->Snap(window, { trueZoneIdx });
}
if (snapped)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
}
return snapped;
}
// We reached the end of all monitors.
// Try again, cycling on all monitors.
// First, add zones from the origin monitor to zoneRects
// Sanity check: the current monitor is valid
if (currentMonitorRect.top <= currentMonitorRect.bottom)
{
const auto& currentWorkArea = activeWorkAreas.at(current);
if (currentWorkArea)
{
const auto& layout = currentWorkArea->GetLayout();
if (layout)
{
const auto& zones = layout->Zones();
for (const auto& [zoneId, zone] : zones)
{
RECT zoneRect = zone.GetZoneRect();
zoneRect.left += currentMonitorRect.left;
zoneRect.right += currentMonitorRect.left;
zoneRect.top += currentMonitorRect.top;
zoneRect.bottom += currentMonitorRect.top;
zoneRects.emplace_back(zoneRect);
zoneRectsInfo.emplace_back(zoneId, currentWorkArea.get());
}
}
}
}
else
{
return false;
}
RECT combinedRect = FancyZonesUtils::GetMonitorsCombinedRect<&MONITORINFOEX::rcWork>(monitors);
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, combinedRect, vkCode);
chosenIdx = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (chosenIdx < zoneRects.size())
{
// Moving to another monitor succeeded
const auto& [trueZoneIdx, workArea] = zoneRectsInfo[chosenIdx];
bool snapped = false;
if (workArea)
{
snapped = workArea->Snap(window, { trueZoneIdx });
}
if (snapped)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
}
return snapped;
}
else
{
// Giving up
return false;
}
}
bool WindowKeyboardSnap::MoveByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea)
{
if (!workArea)
{
return false;
}
const auto& layout = workArea->GetLayout();
const auto& zones = layout->Zones();
const auto& layoutWindows = workArea->GetLayoutWindows();
if (!layout || zones.empty())
{
return false;
}
auto zoneIndexes = layoutWindows.GetZoneIndexSetFromWindow(window);
const auto numZones = zones.size();
bool snapped = false;
// The window was not assigned to any zone here
if (zoneIndexes.size() == 0)
{
const ZoneIndex zone = vkCode == VK_LEFT ? numZones - 1 : 0;
snapped = workArea->Snap(window, { zone });
}
else
{
const ZoneIndex oldId = zoneIndexes[0];
// We reached the edge
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast<int64_t>(numZones) - 1))
{
if (!cycle)
{
return false;
}
const ZoneIndex zone = vkCode == VK_LEFT ? numZones - 1 : 0;
snapped = workArea->Snap(window, { zone });
}
else
{
// We didn't reach the edge
if (vkCode == VK_LEFT)
{
snapped = workArea->Snap(window, { oldId - 1 });
}
else
{
snapped = workArea->Snap(window, { oldId + 1 });
}
}
}
if (snapped)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
}
return snapped;
}
bool WindowKeyboardSnap::MoveByDirectionAndPosition(HWND window, RECT windowRect, DWORD vkCode, bool cycle, WorkArea* const workArea)
{
if (!workArea)
{
return false;
}
const auto& layout = workArea->GetLayout();
const auto& zones = layout->Zones();
const auto& layoutWindows = workArea->GetLayoutWindows();
if (!layout || zones.empty())
{
return false;
}
std::vector<bool> usedZoneIndices(zones.size(), false);
auto windowZones = layoutWindows.GetZoneIndexSetFromWindow(window);
for (const ZoneIndex id : windowZones)
{
usedZoneIndices[id] = true;
}
std::vector<RECT> zoneRects;
ZoneIndexSet freeZoneIndices;
for (const auto& [zoneId, zone] : zones)
{
if (!usedZoneIndices[zoneId])
{
zoneRects.emplace_back(zones.at(zoneId).GetZoneRect());
freeZoneIndices.emplace_back(zoneId);
}
}
// Move to coordinates relative to windowZone
const auto& workAreaRect = workArea->GetWorkAreaRect();
windowRect.top -= workAreaRect.top();
windowRect.bottom -= workAreaRect.top();
windowRect.left -= workAreaRect.left();
windowRect.right -= workAreaRect.left();
ZoneIndex result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (static_cast<size_t>(result) < zoneRects.size())
{
bool success = workArea->Snap(window, { freeZoneIndices[result] });
if (success)
{
Trace::FancyZones::KeyboardSnapWindowToZone(layout.get(), layoutWindows);
}
return success;
}
else if (cycle)
{
// Try again from the position off the screen in the opposite direction to vkCode
// Consider all zones as available
zoneRects.resize(zones.size());
std::transform(zones.begin(), zones.end(), zoneRects.begin(), [](auto zone) { return zone.second.GetZoneRect(); });
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, RECT(workAreaRect.left(), workAreaRect.top(), workAreaRect.right(), workAreaRect.bottom()), vkCode);
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (static_cast<size_t>(result) < zoneRects.size())
{
bool success = workArea->Snap(window, { result });
if (success)
{
Trace::FancyZones::KeyboardSnapWindowToZone(layout.get(), layoutWindows);
}
return success;
}
}
return false;
}
bool WindowKeyboardSnap::Extend(HWND window, RECT windowRect, DWORD vkCode, WorkArea* const workArea)
{
if (!workArea)
{
return false;
}
const auto& layout = workArea->GetLayout();
const auto& layoutWindows = workArea->GetLayoutWindows();
if (!layout || layout->Zones().empty())
{
return false;
}
const auto& zones = layout->Zones();
auto appliedZones = layoutWindows.GetZoneIndexSetFromWindow(window);
std::vector<bool> usedZoneIndices(zones.size(), false);
std::vector<RECT> zoneRects;
ZoneIndexSet freeZoneIndices;
// If selectManyZones = true for the second time, use the last zone into which we moved
// instead of the window rect and enable moving to all zones except the old one
if (m_extendData.IsExtended(window))
{
usedZoneIndices[m_extendData.windowFinalIndex] = true;
windowRect = zones.at(m_extendData.windowFinalIndex).GetZoneRect();
}
else
{
for (const ZoneIndex idx : appliedZones)
{
usedZoneIndices[idx] = true;
}
// Move to coordinates relative to windowZone
const auto& workAreaRect = workArea->GetWorkAreaRect();
windowRect.top -= workAreaRect.top();
windowRect.bottom -= workAreaRect.top();
windowRect.left -= workAreaRect.left();
windowRect.right -= workAreaRect.left();
m_extendData.Set(window);
}
for (size_t i = 0; i < zones.size(); i++)
{
if (!usedZoneIndices[i])
{
zoneRects.emplace_back(zones.at(i).GetZoneRect());
freeZoneIndices.emplace_back(i);
}
}
const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (result >= zoneRects.size())
{
return false;
}
ZoneIndex targetZone = freeZoneIndices[result];
ZoneIndexSet resultIndexSet;
// First time with selectManyZones = true for this window?
if (m_extendData.windowFinalIndex == -1)
{
// Already zoned?
if (appliedZones.size())
{
m_extendData.windowInitialIndexSet = appliedZones;
m_extendData.windowFinalIndex = targetZone;
resultIndexSet = layout->GetCombinedZoneRange(appliedZones, { targetZone });
}
else
{
m_extendData.windowInitialIndexSet = { targetZone };
m_extendData.windowFinalIndex = targetZone;
resultIndexSet = { targetZone };
}
}
else
{
auto deletethis = m_extendData.windowInitialIndexSet;
m_extendData.windowFinalIndex = targetZone;
resultIndexSet = layout->GetCombinedZoneRange(m_extendData.windowInitialIndexSet, { targetZone });
}
bool success = workArea->Snap(window, resultIndexSet);
if (success)
{
Trace::FancyZones::KeyboardSnapWindowToZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
}
return success;
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include <FancyZonesLib/Zone.h>
class WorkArea;
class WindowKeyboardSnap
{
struct ExtendWindowModeData
{
HWND window{ nullptr };
ZoneIndexSet windowInitialIndexSet{};
ZoneIndex windowFinalIndex{ -1 };
bool IsExtended(HWND wnd) const
{
return window == wnd && windowFinalIndex != -1;
}
void Set(HWND w)
{
window = w;
windowFinalIndex = -1;
windowInitialIndexSet.clear();
}
void Reset()
{
window = nullptr;
windowFinalIndex = -1;
windowInitialIndexSet.clear();
}
};
public:
WindowKeyboardSnap() = default;
~WindowKeyboardSnap() = default;
bool Snap(HWND window, HMONITOR activeMonitor, DWORD vkCode,
const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas,
const std::vector<HMONITOR>& monitors);
bool Snap(HWND window, RECT windowRect, HMONITOR activeMonitor, DWORD vkCode,
const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas,
const std::vector<std::pair<HMONITOR, RECT>>& monitors);
bool Extend(HWND window, RECT windowRect, HMONITOR monitor, DWORD vkCode, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
private:
bool SnapHotkeyBasedOnZoneNumber(HWND window, DWORD vkCode, HMONITOR monitor, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<HMONITOR>& monitors);
bool SnapBasedOnPositionOnAnotherMonitor(HWND window, RECT windowRect, DWORD vkCode, HMONITOR monitor, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas, const std::vector<std::pair<HMONITOR, RECT>>& monitors);
bool MoveByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea);
bool MoveByDirectionAndPosition(HWND window, RECT windowRect, DWORD vkCode, bool cycle, WorkArea* const workArea);
bool Extend(HWND window, RECT windowRect, DWORD vkCode, WorkArea* const workArea);
ExtendWindowModeData m_extendData{}; // Needed for ExtendWindowByDirectionAndPosition
};

View File

@@ -0,0 +1,244 @@
#include "pch.h"
#include "WindowMouseSnap.h"
#include <FancyZonesLib/FancyZonesData/AppZoneHistory.h>
#include <FancyZonesLib/FancyZonesWindowProcessing.h>
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/NotificationUtil.h>
#include <FancyZonesLib/Settings.h>
#include <FancyZonesLib/WindowUtils.h>
#include <FancyZonesLib/WorkArea.h>
#include <FancyZonesLib/trace.h>
#include <common/utils/elevation.h>
WindowMouseSnap::WindowMouseSnap(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas) :
m_window(window),
m_activeWorkAreas(activeWorkAreas),
m_currentWorkArea(nullptr),
m_snappingMode(false)
{
m_windowProperties.hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
m_windowProperties.isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window) &&
(!FancyZonesWindowUtils::IsPopupWindow(m_window) || FancyZonesSettings::settings().allowSnapPopupWindows);
}
WindowMouseSnap::~WindowMouseSnap()
{
ResetWindowTransparency();
}
std::unique_ptr<WindowMouseSnap> WindowMouseSnap::Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas)
{
if (!FancyZonesWindowProcessing::IsProcessable(window) ||
!FancyZonesWindowUtils::IsCandidateForZoning(window) ||
FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
{
return nullptr;
}
if (!is_process_elevated() && FancyZonesWindowUtils::IsProcessOfWindowElevated(window))
{
// Notifies user if unable to drag elevated window
FancyZonesNotifications::WarnIfElevationIsRequired();
return nullptr;
}
return std::unique_ptr<WindowMouseSnap>(new WindowMouseSnap(window, activeWorkAreas));
}
bool WindowMouseSnap::MoveSizeStart(HMONITOR monitor, bool isSnapping)
{
auto iter = m_activeWorkAreas.find(monitor);
if (iter == end(m_activeWorkAreas))
{
return false;
}
m_currentWorkArea = iter->second.get();
SwitchSnappingMode(isSnapping);
if (m_currentWorkArea)
{
m_currentWorkArea->Unsnap(m_window);
}
return true;
}
void WindowMouseSnap::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState)
{
auto iter = m_activeWorkAreas.find(monitor);
if (isSnapping && iter != m_activeWorkAreas.end())
{
// The drag has moved to a different monitor.
// Change work area
if (iter->second.get() != m_currentWorkArea)
{
m_highlightedZones.Reset();
if (m_currentWorkArea)
{
if (!FancyZonesSettings::settings().showZonesOnAllMonitors)
{
m_currentWorkArea->HideZones();
}
else
{
m_currentWorkArea->ShowZones({}, m_window);
}
}
m_currentWorkArea = iter->second.get();
}
if (m_currentWorkArea)
{
POINT ptClient = ptScreen;
MapWindowPoints(nullptr, m_currentWorkArea->GetWorkAreaWindow(), &ptClient, 1);
const bool redraw = m_highlightedZones.Update(m_currentWorkArea->GetLayout().get(), ptClient, isSelectManyZonesState);
if (redraw)
{
m_currentWorkArea->ShowZones(m_highlightedZones.Zones(), m_window);
}
}
}
SwitchSnappingMode(isSnapping);
}
void WindowMouseSnap::MoveSizeEnd()
{
if (m_snappingMode)
{
const bool hasNoVisibleOwner = !FancyZonesWindowUtils::HasVisibleOwner(m_window);
const bool isStandardWindow = FancyZonesWindowUtils::IsStandardWindow(m_window);
if ((isStandardWindow == false && hasNoVisibleOwner == true &&
m_windowProperties.isStandardWindow == true && m_windowProperties.hasNoVisibleOwner == true) ||
FancyZonesWindowUtils::IsWindowMaximized(m_window))
{
// Abort the zoning, this is a Chromium based tab that is merged back with an existing window
// or if the window is maximized by Windows when the cursor hits the screen top border
}
else if (m_currentWorkArea)
{
m_currentWorkArea->Snap(m_window, m_highlightedZones.Zones());
}
}
else
{
FancyZonesWindowUtils::ResetRoundCornersPreference(m_window);
if (FancyZonesSettings::settings().restoreSize)
{
if (FancyZonesWindowUtils::IsCursorTypeIndicatingSizeEvent())
{
::RemoveProp(m_window, ZonedWindowProperties::PropertyRestoreSizeID);
}
else if (!FancyZonesWindowUtils::IsWindowMaximized(m_window))
{
FancyZonesWindowUtils::RestoreWindowSize(m_window);
}
}
}
SwitchSnappingMode(false);
}
void WindowMouseSnap::SwitchSnappingMode(bool isSnapping)
{
if (!m_snappingMode && isSnapping) // turn on
{
m_highlightedZones.Reset();
SetWindowTransparency();
if (FancyZonesSettings::settings().showZonesOnAllMonitors)
{
for (const auto& [_, workArea] : m_activeWorkAreas)
{
if (workArea)
{
workArea->ShowZones({}, m_window);
}
}
}
else if (m_currentWorkArea)
{
m_currentWorkArea->ShowZones({}, m_window);
}
if (m_currentWorkArea)
{
m_currentWorkArea->Unsnap(m_window);
Trace::WorkArea::MoveOrResizeStarted(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows());
}
}
else if (m_snappingMode && !isSnapping) // turn off
{
ResetWindowTransparency();
m_highlightedZones.Reset();
// Hide all layouts (regardless of settings)
for (auto& [_, workArea] : m_activeWorkAreas)
{
if (workArea)
{
workArea->HideZones();
}
}
if (m_currentWorkArea)
{
Trace::WorkArea::MoveOrResizeEnd(m_currentWorkArea->GetLayout().get(), m_currentWorkArea->GetLayoutWindows());
}
}
m_snappingMode = isSnapping;
}
void WindowMouseSnap::SetWindowTransparency()
{
if (FancyZonesSettings::settings().makeDraggedWindowTransparent)
{
m_windowProperties.exstyle = GetWindowLong(m_window, GWL_EXSTYLE);
SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle | WS_EX_LAYERED);
if (!GetLayeredWindowAttributes(m_window, &m_windowProperties.crKey, &m_windowProperties.alpha, &m_windowProperties.dwFlags))
{
Logger::error(L"Window transparency: GetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
return;
}
if (!SetLayeredWindowAttributes(m_window, 0, (255 * 50) / 100, LWA_ALPHA))
{
Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
return;
}
m_windowProperties.transparencySet = true;
}
}
void WindowMouseSnap::ResetWindowTransparency()
{
if (FancyZonesSettings::settings().makeDraggedWindowTransparent && m_windowProperties.transparencySet)
{
bool reset = true;
if (!SetLayeredWindowAttributes(m_window, m_windowProperties.crKey, m_windowProperties.alpha, m_windowProperties.dwFlags))
{
Logger::error(L"Window transparency: SetLayeredWindowAttributes failed, {}", get_last_error_or_default(GetLastError()));
reset = false;
}
if (SetWindowLong(m_window, GWL_EXSTYLE, m_windowProperties.exstyle) == 0)
{
Logger::error(L"Window transparency: SetWindowLong failed, {}", get_last_error_or_default(GetLastError()));
reset = false;
}
m_windowProperties.transparencySet = !reset;
}
}

View File

@@ -4,13 +4,13 @@
class WorkArea; class WorkArea;
class WindowDrag class WindowMouseSnap
{ {
WindowDrag(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas); WindowMouseSnap(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
public: public:
static std::unique_ptr<WindowDrag> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas); static std::unique_ptr<WindowMouseSnap> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
~WindowDrag(); ~WindowMouseSnap();
bool MoveSizeStart(HMONITOR monitor, bool isSnapping); bool MoveSizeStart(HMONITOR monitor, bool isSnapping);
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState); void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState);

View File

@@ -456,6 +456,9 @@ RECT FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(HWND window, RECT rect
::GetWindowRect(window, &windowRect); ::GetWindowRect(window, &windowRect);
// Take care of borders // Take care of borders
// Skip when windowOfRect is not initialized (in unit tests)
if (windowOfRect)
{
RECT frameRect{}; RECT frameRect{};
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect)))) if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
{ {
@@ -466,6 +469,7 @@ RECT FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(HWND window, RECT rect
newWindowRect.right -= rightMargin; newWindowRect.right -= rightMargin;
newWindowRect.bottom -= bottomMargin; newWindowRect.bottom -= bottomMargin;
} }
}
// Take care of windows that cannot be resized // Take care of windows that cannot be resized
if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0) if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0)
@@ -475,7 +479,10 @@ RECT FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(HWND window, RECT rect
} }
// Convert to screen coordinates // Convert to screen coordinates
if (windowOfRect)
{
MapWindowRect(windowOfRect, nullptr, &newWindowRect); MapWindowRect(windowOfRect, nullptr, &newWindowRect);
}
return newWindowRect; return newWindowRect;
} }

View File

@@ -1,26 +1,16 @@
#include "pch.h" #include "pch.h"
#include "WorkArea.h" #include "WorkArea.h"
#include <common/logger/call_tracer.h>
#include <common/logger/logger.h> #include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
#include "FancyZonesData/AppliedLayouts.h" #include "FancyZonesData/AppliedLayouts.h"
#include "FancyZonesData/AppZoneHistory.h" #include "FancyZonesData/AppZoneHistory.h"
#include "FancyZonesDataTypes.h"
#include "SettingsObserver.h"
#include "ZonesOverlay.h" #include "ZonesOverlay.h"
#include "trace.h"
#include "on_thread_executor.h"
#include "Settings.h" #include "Settings.h"
#include <FancyZonesLib/FancyZonesWindowProperties.h> #include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/VirtualDesktop.h> #include <FancyZonesLib/VirtualDesktop.h>
#include <FancyZonesLib/WindowUtils.h> #include <FancyZonesLib/WindowUtils.h>
#include <ShellScalingApi.h>
#include <mutex>
#include <fileapi.h>
// disabling warning 4458 - declaration of 'identifier' hides class member // disabling warning 4458 - declaration of 'identifier' hides class member
// to avoid warnings from GDI files - can't add winRT directory to external code // to avoid warnings from GDI files - can't add winRT directory to external code
// in the Cpp.Build.props // in the Cpp.Build.props
@@ -127,282 +117,39 @@ WorkArea::~WorkArea()
windowPool.FreeZonesOverlayWindow(m_window); windowPool.FreeZonesOverlayWindow(m_window);
} }
void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index) bool WorkArea::Snap(HWND window, const ZoneIndexSet& zones, bool updatePosition)
{ {
MoveWindowIntoZoneByIndexSet(window, { index }); if (!m_layout || zones.empty())
{
return false;
} }
void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/) m_layoutWindows.Assign(window, zones);
{ AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, m_layout->Id(), zones);
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty())
{
return;
}
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
if (updatePosition) if (updatePosition)
{ {
const auto rect = m_layout->GetCombinedZonesRect(indexSet); const auto rect = m_layout->GetCombinedZonesRect(zones);
if (rect.bottom - rect.top > 0 && rect.right - rect.left > 0)
{
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window); const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect); FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
} }
return FancyZonesWindowProperties::StampZoneIndexProperty(window, zones);
} }
SnapWindow(window, indexSet); bool WorkArea::Unsnap(HWND window)
}
bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle)
{ {
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty()) if (!m_layout)
{ {
return false; return false;
} }
auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window); m_layoutWindows.Dismiss(window);
const auto numZones = m_layout->Zones().size(); AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, m_layout->Id());
// The window was not assigned to any zone here
if (zoneIndexes.size() == 0)
{
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
}
else
{
const ZoneIndex oldId = zoneIndexes[0];
// We reached the edge
if ((vkCode == VK_LEFT && oldId == 0) || (vkCode == VK_RIGHT && oldId == static_cast<int64_t>(numZones) - 1))
{
if (!cycle)
{
return false;
}
MoveWindowIntoZoneByIndex(window, vkCode == VK_LEFT ? numZones - 1 : 0);
}
else
{
// We didn't reach the edge
if (vkCode == VK_LEFT)
{
MoveWindowIntoZoneByIndex(window, oldId - 1);
}
else
{
MoveWindowIntoZoneByIndex(window, oldId + 1);
}
}
}
return true;
}
bool WorkArea::MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle)
{
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
{
return false;
}
const auto& zones = m_layout->Zones();
std::vector<bool> usedZoneIndices(zones.size(), false);
auto windowZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
for (const ZoneIndex id : windowZones)
{
usedZoneIndices[id] = true;
}
std::vector<RECT> zoneRects;
ZoneIndexSet freeZoneIndices;
for (const auto& [zoneId, zone] : zones)
{
if (!usedZoneIndices[zoneId])
{
zoneRects.emplace_back(zones.at(zoneId).GetZoneRect());
freeZoneIndices.emplace_back(zoneId);
}
}
RECT windowRect;
if (!GetWindowRect(window, &windowRect))
{
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
return false;
}
// Move to coordinates relative to windowZone
windowRect.top -= m_workAreaRect.top();
windowRect.bottom -= m_workAreaRect.top();
windowRect.left -= m_workAreaRect.left();
windowRect.right -= m_workAreaRect.left();
auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (result < zoneRects.size())
{
MoveWindowIntoZoneByIndex(window, freeZoneIndices[result]);
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
return true;
}
else if (cycle)
{
// Try again from the position off the screen in the opposite direction to vkCode
// Consider all zones as available
zoneRects.resize(zones.size());
std::transform(zones.begin(), zones.end(), zoneRects.begin(), [](auto zone) { return zone.second.GetZoneRect(); });
windowRect = FancyZonesUtils::PrepareRectForCycling(windowRect, RECT(m_workAreaRect.left(), m_workAreaRect.top(), m_workAreaRect.right(), m_workAreaRect.bottom()), vkCode);
result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (result < zoneRects.size())
{
MoveWindowIntoZoneByIndex(window, result);
Trace::FancyZones::KeyboardSnapWindowToZone(m_layout.get(), m_layoutWindows.get());
return true;
}
}
return false;
}
bool WorkArea::ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode)
{
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
{
return false;
}
RECT windowRect;
if (!GetWindowRect(window, &windowRect))
{
Logger::error(L"GetWindowRect failed, {}", get_last_error_or_default(GetLastError()));
return false;
}
const auto& zones = m_layout->Zones();
auto appliedZones = m_layoutWindows->GetZoneIndexSetFromWindow(window);
const auto& extendModeData = m_layoutWindows->ExtendWindowData();
std::vector<bool> usedZoneIndices(zones.size(), false);
std::vector<RECT> zoneRects;
ZoneIndexSet freeZoneIndices;
// If selectManyZones = true for the second time, use the last zone into which we moved
// instead of the window rect and enable moving to all zones except the old one
auto finalIndexIt = extendModeData->windowFinalIndex.find(window);
if (finalIndexIt != extendModeData->windowFinalIndex.end())
{
usedZoneIndices[finalIndexIt->second] = true;
windowRect = zones.at(finalIndexIt->second).GetZoneRect();
}
else
{
for (const ZoneIndex idx : appliedZones)
{
usedZoneIndices[idx] = true;
}
// Move to coordinates relative to windowZone
windowRect.top -= m_workAreaRect.top();
windowRect.bottom -= m_workAreaRect.top();
windowRect.left -= m_workAreaRect.left();
windowRect.right -= m_workAreaRect.left();
}
for (size_t i = 0; i < zones.size(); i++)
{
if (!usedZoneIndices[i])
{
zoneRects.emplace_back(zones.at(i).GetZoneRect());
freeZoneIndices.emplace_back(i);
}
}
const auto result = FancyZonesUtils::ChooseNextZoneByPosition(vkCode, windowRect, zoneRects);
if (result < zoneRects.size())
{
ZoneIndex targetZone = freeZoneIndices[result];
ZoneIndexSet resultIndexSet;
// First time with selectManyZones = true for this window?
if (finalIndexIt == extendModeData->windowFinalIndex.end())
{
// Already zoned?
if (appliedZones.size())
{
extendModeData->windowInitialIndexSet[window] = appliedZones;
extendModeData->windowFinalIndex[window] = targetZone;
resultIndexSet = m_layout->GetCombinedZoneRange(appliedZones, { targetZone });
}
else
{
extendModeData->windowInitialIndexSet[window] = { targetZone };
extendModeData->windowFinalIndex[window] = targetZone;
resultIndexSet = { targetZone };
}
}
else
{
auto deletethis = extendModeData->windowInitialIndexSet[window];
extendModeData->windowFinalIndex[window] = targetZone;
resultIndexSet = m_layout->GetCombinedZoneRange(extendModeData->windowInitialIndexSet[window], { targetZone });
}
const auto rect = m_layout->GetCombinedZonesRect(resultIndexSet);
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
SnapWindow(window, resultIndexSet, true);
return true;
}
return false;
}
void WorkArea::SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend)
{
if (!m_layoutWindows || !m_layout)
{
return;
}
if (extend)
{
m_layoutWindows->Extend(window, zones);
}
else
{
m_layoutWindows->Assign(window, zones);
}
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
if (guidStr.has_value())
{
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, guidStr.value(), zones);
}
FancyZonesWindowProperties::StampZoneIndexProperty(window, zones);
}
void WorkArea::UnsnapWindow(HWND window)
{
if (!m_layoutWindows || !m_layout)
{
return;
}
m_layoutWindows->Dismiss(window);
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
if (guidStr.has_value())
{
AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, guidStr.value());
}
FancyZonesWindowProperties::RemoveZoneIndexProperty(window); FancyZonesWindowProperties::RemoveZoneIndexProperty(window);
return true;
} }
const GUID WorkArea::GetLayoutId() const noexcept const GUID WorkArea::GetLayoutId() const noexcept
@@ -415,29 +162,7 @@ const GUID WorkArea::GetLayoutId() const noexcept
return GUID{}; return GUID{};
} }
ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const void WorkArea::ShowZones(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/)
{
if (m_layout)
{
auto guidStr = FancyZonesUtils::GuidToString(m_layout->Id());
if (guidStr.has_value())
{
return AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, guidStr.value());
}
else
{
Logger::error(L"Failed to convert to string layout GUID on the requested work area");
}
}
else
{
Logger::error(L"No layout initialized on the requested work area");
}
return {};
}
void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/)
{ {
if (m_layout && m_zonesOverlay) if (m_layout && m_zonesOverlay)
{ {
@@ -447,7 +172,7 @@ void WorkArea::ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindo
} }
} }
void WorkArea::HideZonesOverlay() void WorkArea::HideZones()
{ {
if (m_zonesOverlay) if (m_zonesOverlay)
{ {
@@ -465,15 +190,10 @@ void WorkArea::FlashZones()
} }
} }
void WorkArea::UpdateActiveZoneSet() void WorkArea::InitLayout()
{ {
const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId); InitLayout({});
if (!isLayoutAlreadyApplied)
{
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
}
CalculateZoneSet();
if (m_window && m_layout) if (m_window && m_layout)
{ {
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber); m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
@@ -482,24 +202,16 @@ void WorkArea::UpdateActiveZoneSet()
void WorkArea::UpdateWindowPositions() void WorkArea::UpdateWindowPositions()
{ {
if (!m_layoutWindows) const auto& snappedWindows = m_layoutWindows.SnappedWindows();
{
return;
}
const auto& snappedWindows = m_layoutWindows->SnappedWindows();
for (const auto& [window, zones] : snappedWindows) for (const auto& [window, zones] : snappedWindows)
{ {
MoveWindowIntoZoneByIndexSet(window, zones, true); Snap(window, zones, true);
} }
} }
void WorkArea::CycleWindows(HWND window, bool reverse) void WorkArea::CycleWindows(HWND window, bool reverse)
{ {
if (m_layoutWindows) m_layoutWindows.CycleWindows(window, reverse);
{
m_layoutWindows->CycleWindows(window, reverse);
}
} }
#pragma region private #pragma region private
@@ -537,6 +249,46 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId)
CalculateZoneSet(); CalculateZoneSet();
} }
void WorkArea::InitSnappedWindows()
{
static bool updatePositionOnceOnStartFlag = true;
Logger::info(L"Init work area {} windows, update positions = {}", m_uniqueId.toString(), updatePositionOnceOnStartFlag);
for (const auto& window : VirtualDesktop::instance().GetWindowsFromCurrentDesktop())
{
auto indexes = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
if (indexes.size() == 0)
{
continue;
}
if (!m_uniqueId.monitorId.monitor) // one work area across monitors
{
Snap(window, indexes, updatePositionOnceOnStartFlag);
}
else
{
const auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
if (monitor && m_uniqueId.monitorId.monitor == monitor)
{
// prioritize snapping on the current monitor if the window was snapped to several work areas
Snap(window, indexes, updatePositionOnceOnStartFlag);
}
else
{
// if the window is not snapped on the current monitor, then check the others
auto savedIndexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, m_uniqueId, GetLayoutId());
if (savedIndexes == indexes)
{
Snap(window, indexes, updatePositionOnceOnStartFlag);
}
}
}
}
updatePositionOnceOnStartFlag = false;
}
void WorkArea::CalculateZoneSet() void WorkArea::CalculateZoneSet()
{ {
const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId); const auto appliedLayout = AppliedLayouts::instance().GetDeviceLayout(m_uniqueId);
@@ -548,11 +300,6 @@ void WorkArea::CalculateZoneSet()
m_layout = std::make_unique<Layout>(appliedLayout.value()); m_layout = std::make_unique<Layout>(appliedLayout.value());
m_layout->Init(m_workAreaRect, m_uniqueId.monitorId.monitor); m_layout->Init(m_workAreaRect, m_uniqueId.monitorId.monitor);
if (!m_layoutWindows)
{
m_layoutWindows = std::make_unique<LayoutAssignedWindows>();
}
} }
LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept LRESULT WorkArea::WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <FancyZonesLib/FancyZonesDataTypes.h>
#include <FancyZonesLib/Layout.h> #include <FancyZonesLib/Layout.h>
#include <FancyZonesLib/LayoutAssignedWindows.h> #include <FancyZonesLib/LayoutAssignedWindows.h>
@@ -39,26 +38,20 @@ public:
FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; } FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; }
const std::unique_ptr<Layout>& GetLayout() const noexcept { return m_layout; } const std::unique_ptr<Layout>& GetLayout() const noexcept { return m_layout; }
const std::unique_ptr<LayoutAssignedWindows>& GetLayoutWindows() const noexcept { return m_layoutWindows; } const LayoutAssignedWindows& GetLayoutWindows() const noexcept { return m_layoutWindows; }
const HWND GetWorkAreaWindow() const noexcept { return m_window; } const HWND GetWorkAreaWindow() const noexcept { return m_window; }
const GUID GetLayoutId() const noexcept; const GUID GetLayoutId() const noexcept;
const FancyZonesUtils::Rect& GetWorkAreaRect() const noexcept { return m_workAreaRect; }
ZoneIndexSet GetWindowZoneIndexes(HWND window) const; void InitLayout();
void InitSnappedWindows();
void MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index);
void MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition = true);
bool MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle);
bool MoveWindowIntoZoneByDirectionAndPosition(HWND window, DWORD vkCode, bool cycle);
bool ExtendWindowByDirectionAndPosition(HWND window, DWORD vkCode);
void SnapWindow(HWND window, const ZoneIndexSet& zones, bool extend = false);
void UnsnapWindow(HWND window);
void UpdateActiveZoneSet();
void UpdateWindowPositions(); void UpdateWindowPositions();
void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr); bool Snap(HWND window, const ZoneIndexSet& zones, bool updatePosition = true);
void HideZonesOverlay(); bool Unsnap(HWND window);
void ShowZones(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr);
void HideZones();
void FlashZones(); void FlashZones();
void CycleWindows(HWND window, bool reverse); void CycleWindows(HWND window, bool reverse);
@@ -79,6 +72,6 @@ private:
const FancyZonesDataTypes::WorkAreaId m_uniqueId; const FancyZonesDataTypes::WorkAreaId m_uniqueId;
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area. HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
std::unique_ptr<Layout> m_layout; std::unique_ptr<Layout> m_layout;
std::unique_ptr<LayoutAssignedWindows> m_layoutWindows; LayoutAssignedWindows m_layoutWindows{};
std::unique_ptr<ZonesOverlay> m_zonesOverlay; std::unique_ptr<ZonesOverlay> m_zonesOverlay;
}; };

View File

@@ -90,17 +90,17 @@ struct ZoneSetInfo
}; };
ZoneSetInfo GetZoneSetInfo(_In_opt_ Layout* layout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept ZoneSetInfo GetZoneSetInfo(_In_opt_ Layout* layout, const LayoutAssignedWindows& layoutWindows) noexcept
{ {
ZoneSetInfo info; ZoneSetInfo info;
if (layout && layoutWindows) if (layout)
{ {
auto zones = layout->Zones(); auto zones = layout->Zones();
info.NumberOfZones = zones.size(); info.NumberOfZones = zones.size();
info.NumberOfWindows = 0; info.NumberOfWindows = 0;
for (int i = 0; i < static_cast<int>(zones.size()); i++) for (int i = 0; i < static_cast<int>(zones.size()); i++)
{ {
if (!layoutWindows->IsZoneEmpty(i)) if (!layoutWindows.IsZoneEmpty(i))
{ {
info.NumberOfWindows++; info.NumberOfWindows++;
} }
@@ -258,7 +258,7 @@ void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept
TraceLoggingBoolean(shortcutUsed, QuickLayoutSwitchedWithShortcutUsed)); TraceLoggingBoolean(shortcutUsed, QuickLayoutSwitchedWithShortcutUsed));
} }
void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
{ {
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
TraceLoggingWrite( TraceLoggingWrite(
@@ -271,7 +271,7 @@ void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssign
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey)); TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
} }
void Trace::FancyZones::KeyboardSnapWindowToZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept void Trace::FancyZones::KeyboardSnapWindowToZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
{ {
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
TraceLoggingWrite( TraceLoggingWrite(
@@ -356,7 +356,7 @@ void Trace::WorkArea::KeyUp(WPARAM wParam) noexcept
TraceLoggingValue(wParam, KeyboardValueKey)); TraceLoggingValue(wParam, KeyboardValueKey));
} }
void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
{ {
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
TraceLoggingWrite( TraceLoggingWrite(
@@ -369,7 +369,7 @@ void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey)); TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
} }
void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept
{ {
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
TraceLoggingWrite( TraceLoggingWrite(
@@ -382,7 +382,7 @@ void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ La
TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey)); TraceLoggingValue(zoneInfo.NumberOfWindows, NumberOfWindowsKey));
} }
void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows, InputMode mode) noexcept void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows, InputMode mode) noexcept
{ {
auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows);
TraceLoggingWrite( TraceLoggingWrite(

View File

@@ -19,8 +19,8 @@ public:
static void EditorLaunched(int value) noexcept; static void EditorLaunched(int value) noexcept;
static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept; static void Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept;
static void QuickLayoutSwitched(bool shortcutUsed) noexcept; static void QuickLayoutSwitched(bool shortcutUsed) noexcept;
static void SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept; static void SnapNewWindowIntoZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
static void KeyboardSnapWindowToZone(Layout* activeLayout, LayoutAssignedWindows* layoutWindows) noexcept; static void KeyboardSnapWindowToZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
}; };
static void SettingsTelemetry(const Settings& settings) noexcept; static void SettingsTelemetry(const Settings& settings) noexcept;
@@ -36,8 +36,8 @@ public:
}; };
static void KeyUp(WPARAM wparam) noexcept; static void KeyUp(WPARAM wparam) noexcept;
static void MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept; static void MoveOrResizeStarted(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
static void MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows) noexcept; static void MoveOrResizeEnd(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept;
static void CycleActiveZoneSet(_In_opt_ Layout* activeLayout, _In_opt_ LayoutAssignedWindows* layoutWindows, InputMode mode) noexcept; static void CycleActiveZoneSet(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows, InputMode mode) noexcept;
}; };
}; };

View File

@@ -123,6 +123,15 @@ namespace FancyZonesUtils
monitorInfo = std::move(sortedMonitorInfo); monitorInfo = std::move(sortedMonitorInfo);
} }
std::vector<HMONITOR> GetMonitorsOrdered()
{
auto monitors = FancyZonesUtils::GetAllMonitorRects<&MONITORINFOEX::rcWork>();
FancyZonesUtils::OrderMonitors(monitors);
std::vector<HMONITOR> output;
std::transform(std::begin(monitors), std::end(monitors), std::back_inserter(output), [](const auto& info) { return info.first; });
return output;
}
bool IsValidGuid(const std::wstring& str) bool IsValidGuid(const std::wstring& str)
{ {
GUID id; GUID id;

View File

@@ -143,13 +143,12 @@ namespace FancyZonesUtils
} }
template<RECT MONITORINFO::*member> template<RECT MONITORINFO::*member>
RECT GetAllMonitorsCombinedRect() RECT GetMonitorsCombinedRect(const std::vector<std::pair<HMONITOR, RECT>>& monitorRects)
{ {
auto allMonitors = GetAllMonitorRects<member>();
bool empty = true; bool empty = true;
RECT result{ 0, 0, 0, 0 }; RECT result{ 0, 0, 0, 0 };
for (auto& [monitor, rect] : allMonitors) for (auto& [monitor, rect] : monitorRects)
{ {
if (empty) if (empty)
{ {
@@ -168,6 +167,13 @@ namespace FancyZonesUtils
return result; return result;
} }
template<RECT MONITORINFO::*member>
RECT GetAllMonitorsCombinedRect()
{
auto allMonitors = GetAllMonitorRects<member>();
return GetMonitorsCombinedRect<member>(allMonitors);
}
constexpr RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept constexpr RECT PrepareRectForCycling(RECT windowRect, RECT workAreaRect, DWORD vkCode) noexcept
{ {
LONG deltaX = 0, deltaY = 0; LONG deltaX = 0, deltaY = 0;
@@ -196,6 +202,7 @@ namespace FancyZonesUtils
UINT GetDpiForMonitor(HMONITOR monitor) noexcept; UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo); void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
std::vector<HMONITOR> GetMonitorsOrdered();
bool IsValidGuid(const std::wstring& str); bool IsValidGuid(const std::wstring& str);
std::optional<GUID> GuidFromString(const std::wstring& str) noexcept; std::optional<GUID> GuidFromString(const std::wstring& str) noexcept;

View File

@@ -207,22 +207,22 @@ namespace FancyZonesUnitTests
TEST_METHOD (AppLastZoneInvalidWindow) TEST_METHOD (AppLastZoneInvalidWindow)
{ {
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
}; };
const auto window = Mocks::Window(); const auto window = Mocks::Window();
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId)); Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
const int expectedZoneIndex = 1; const int expectedZoneIndex = 1;
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { expectedZoneIndex })); Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { expectedZoneIndex }));
} }
TEST_METHOD (AppLastZoneNullWindow) TEST_METHOD (AppLastZoneNullWindow)
{ {
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -230,12 +230,12 @@ namespace FancyZonesUnitTests
const auto window = nullptr; const auto window = nullptr;
const int expectedZoneIndex = 1; const int expectedZoneIndex = 1;
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { expectedZoneIndex })); Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { expectedZoneIndex }));
} }
TEST_METHOD (AppLastdeviceIdTest) TEST_METHOD (AppLastdeviceIdTest)
{ {
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId1{ const FancyZonesDataTypes::WorkAreaId workAreaId1{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -247,15 +247,15 @@ namespace FancyZonesUnitTests
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
const int expectedZoneIndex = 10; const int expectedZoneIndex = 10;
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId1, zoneSetId, { expectedZoneIndex })); Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId1, layoutId, { expectedZoneIndex }));
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId1, zoneSetId)); Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId1, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId2, zoneSetId)); Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId2, layoutId));
} }
TEST_METHOD (AppLastZoneSetIdTest) TEST_METHOD (AppLastZoneSetIdTest)
{ {
const std::wstring zoneSetId1 = L"{B7A1F5A9-9DC2-4505-84AB-993253839093}"; const auto layoutId1 = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839093}").value();
const std::wstring zoneSetId2 = L"{B7A1F5A9-9DC2-4505-84AB-993253839094}"; const auto layoutId2 = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839094}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -263,56 +263,56 @@ namespace FancyZonesUnitTests
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
const int expectedZoneIndex = 10; const int expectedZoneIndex = 10;
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId1, { expectedZoneIndex })); Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId1, { expectedZoneIndex }));
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId1)); Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId1));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId2)); Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId2));
} }
TEST_METHOD (AppLastZoneRemoveWindow) TEST_METHOD (AppLastZoneRemoveWindow)
{ {
const std::wstring zoneSetId = L"{B7A1F5A9-9DC2-4505-84AB-993253839093}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839093}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
}; };
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { 1 })); Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { 1 }));
Assert::IsTrue(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetId)); Assert::IsTrue(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId)); Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
} }
TEST_METHOD (AppLastZoneRemoveUnknownWindow) TEST_METHOD (AppLastZoneRemoveUnknownWindow)
{ {
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
}; };
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetId)); Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId)); Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
} }
TEST_METHOD (AppLastZoneRemoveUnknownZoneSetId) TEST_METHOD (AppLastZoneRemoveUnknownZoneSetId)
{ {
const std::wstring zoneSetIdToInsert = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutIdToInsert = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const std::wstring zoneSetIdToRemove = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F1}"; const auto layoutIdToRemove = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F1}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
}; };
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetIdToInsert, { 1 })); Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutIdToInsert, { 1 }));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetIdToRemove)); Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutIdToRemove));
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetIdToInsert)); Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutIdToInsert));
} }
TEST_METHOD (AppLastZoneRemoveUnknownWindowId) TEST_METHOD (AppLastZoneRemoveUnknownWindowId)
{ {
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const FancyZonesDataTypes::WorkAreaId workAreaIdToInsert{ const FancyZonesDataTypes::WorkAreaId workAreaIdToInsert{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -323,20 +323,20 @@ namespace FancyZonesUnitTests
}; };
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaIdToInsert, zoneSetId, { 1 })); Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaIdToInsert, layoutId, { 1 }));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaIdToRemove, zoneSetId)); Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaIdToRemove, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaIdToInsert, zoneSetId)); Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaIdToInsert, layoutId));
} }
TEST_METHOD (AppLastZoneRemoveNullWindow) TEST_METHOD (AppLastZoneRemoveNullWindow)
{ {
const std::wstring zoneSetId = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}"; const auto layoutId = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{ const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } }, .monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value() .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
}; };
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(nullptr, workAreaId, zoneSetId)); Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(nullptr, workAreaId, layoutId));
} }
}; };
} }

View File

@@ -140,10 +140,9 @@ namespace FancyZonesUnitTests
data.zoneCount = 4; data.zoneCount = 4;
// prepare settings // prepare settings
PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey); auto settings = FancyZonesSettings::settings();
values.add_property(L"fancyzones_overlappingZonesAlgorithm", json::value(static_cast<int>(OverlappingZonesAlgorithm::Smallest))); settings.overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json()); FancyZonesSettings::instance().SetSettings(settings);
FancyZonesSettings::instance().LoadSettings();
auto layout = std::make_unique<Layout>(data); auto layout = std::make_unique<Layout>(data);
layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor());
@@ -168,10 +167,9 @@ namespace FancyZonesUnitTests
data.zoneCount = 4; data.zoneCount = 4;
// prepare settings // prepare settings
PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey); auto settings = FancyZonesSettings::settings();
values.add_property(L"fancyzones_overlappingZonesAlgorithm", json::value(static_cast<int>(OverlappingZonesAlgorithm::Smallest))); settings.overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json()); FancyZonesSettings::instance().SetSettings(settings);
FancyZonesSettings::instance().LoadSettings();
auto layout = std::make_unique<Layout>(data); auto layout = std::make_unique<Layout>(data);
layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor()); layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor());

View File

@@ -55,6 +55,7 @@
</ClCompile> </ClCompile>
<ClCompile Include="Util.Spec.cpp" /> <ClCompile Include="Util.Spec.cpp" />
<ClCompile Include="Util.cpp" /> <ClCompile Include="Util.cpp" />
<ClCompile Include="WindowKeyboardSnap.Spec.cpp" />
<ClCompile Include="WorkArea.Spec.cpp" /> <ClCompile Include="WorkArea.Spec.cpp" />
<ClCompile Include="WorkAreaIdTests.Spec.cpp" /> <ClCompile Include="WorkAreaIdTests.Spec.cpp" />
<ClCompile Include="Zone.Spec.cpp" /> <ClCompile Include="Zone.Spec.cpp" />

View File

@@ -63,6 +63,9 @@
<ClCompile Include="DefaultLayoutsTests.Spec.cpp"> <ClCompile Include="DefaultLayoutsTests.Spec.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="WindowKeyboardSnap.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="pch.h"> <ClInclude Include="pch.h">

File diff suppressed because it is too large Load Diff

View File

@@ -44,7 +44,6 @@ namespace FancyZonesUnitTests
{ {
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName()); std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
std::filesystem::remove(DefaultLayouts::DefaultLayoutsFileName()); std::filesystem::remove(DefaultLayouts::DefaultLayoutsFileName());
} }
@@ -58,7 +57,6 @@ namespace FancyZonesUnitTests
const auto& layout = workArea->GetLayout(); const auto& layout = workArea->GetLayout();
Assert::IsNotNull(layout.get()); Assert::IsNotNull(layout.get());
Assert::IsNotNull(workArea->GetLayoutWindows().get());
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type())); Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type()));
Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size())); Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size()));
} }
@@ -73,7 +71,6 @@ namespace FancyZonesUnitTests
const auto& layout = workArea->GetLayout(); const auto& layout = workArea->GetLayout();
Assert::IsNotNull(layout.get()); Assert::IsNotNull(layout.get());
Assert::IsNotNull(workArea->GetLayoutWindows().get());
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type())); Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type()));
Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size())); Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size()));
} }
@@ -102,7 +99,6 @@ namespace FancyZonesUnitTests
auto actualWorkArea = WorkArea::Create(m_hInst, m_workAreaId, parentUniqueId, m_workAreaRect); auto actualWorkArea = WorkArea::Create(m_hInst, m_workAreaId, parentUniqueId, m_workAreaRect);
Assert::IsNotNull(actualWorkArea->GetLayout().get()); Assert::IsNotNull(actualWorkArea->GetLayout().get());
Assert::IsNotNull(actualWorkArea->GetLayoutWindows().get());
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_workAreaId)); Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(m_workAreaId));
const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_workAreaId); const auto& actualLayout = AppliedLayouts::instance().GetAppliedLayoutMap().at(m_workAreaId);
@@ -179,428 +175,46 @@ namespace FancyZonesUnitTests
} }
}; };
TEST_CLASS (WorkAreaMoveWindowUnitTests) TEST_CLASS (WorkAreaSnapUnitTests)
{ {
const std::wstring m_virtualDesktopIdStr = L"{A998CA86-F08D-4BCA-AED8-77F5C8FC9925}"; HINSTANCE m_hInst{};
const FancyZonesDataTypes::WorkAreaId m_workAreaId{ const HMONITOR m_monitor = Mocks::Monitor();
const FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) };
const FancyZonesDataTypes::WorkAreaId m_parentUniqueId = {};
const FancyZonesDataTypes::WorkAreaId m_workAreaId = {
.monitorId = { .monitorId = {
.monitor = Mocks::Monitor(), .monitor = m_monitor,
.deviceId = { .deviceId = {
.id = L"DELA026", .id = L"device-id-1",
.instanceId = L"5&10a58c63&0&UID16777488", .instanceId = L"5&10a58c63&0&UID16777488",
.number = 1, .number = 1,
}, },
.serialNumber = L"serial-number" .serialNumber = L"serial-number-1" },
}, .virtualDesktopId = FancyZonesUtils::GuidFromString(L"{310F2924-B587-4D87-97C2-90031BDBE3F1}").value()
.virtualDesktopId = FancyZonesUtils::GuidFromString(m_virtualDesktopIdStr).value()
}; };
FancyZonesDataTypes::WorkAreaId m_parentUniqueId; // default empty
HINSTANCE m_hInst{};
FancyZonesUtils::Rect m_workAreaRect{ RECT(0, 0, 1920, 1080) };
void PrepareEmptyLayout()
{
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::Blank)));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(false));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(0));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(0));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(0));
json::JsonObject workAreaId{};
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceID, workAreaId);
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
layoutsArray.Append(obj);
}
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
AppliedLayouts::instance().LoadData();
}
void PrepareGridLayout()
{
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::Grid)));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ShowSpacingID, json::value(false));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SpacingID, json::value(0));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::ZoneCountID, json::value(4));
layout.SetNamedValue(NonLocalizable::AppliedLayoutsIds::SensitivityRadiusID, json::value(20));
json::JsonObject workAreaId{};
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorID, json::value(m_workAreaId.monitorId.deviceId.id));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorInstanceID, json::value(m_workAreaId.monitorId.deviceId.instanceId));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorSerialNumberID, json::value(m_workAreaId.monitorId.serialNumber));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::MonitorNumberID, json::value(m_workAreaId.monitorId.deviceId.number));
workAreaId.SetNamedValue(NonLocalizable::AppliedLayoutsIds::VirtualDesktopID, json::value(m_virtualDesktopIdStr));
json::JsonObject obj{};
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::DeviceID, workAreaId);
obj.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutID, layout);
layoutsArray.Append(obj);
}
root.SetNamedValue(NonLocalizable::AppliedLayoutsIds::AppliedLayoutsArrayID, layoutsArray);
json::to_file(AppliedLayouts::AppliedLayoutsFileName(), root);
AppliedLayouts::instance().LoadData();
}
TEST_METHOD_INITIALIZE(Init) noexcept TEST_METHOD_INITIALIZE(Init) noexcept
{ {
AppZoneHistory::instance().LoadData(); AppZoneHistory::instance().LoadData();
AppliedLayouts::instance().LoadData();
} }
TEST_METHOD_CLEANUP(CleanUp) noexcept TEST_METHOD_CLEANUP(CleanUp) noexcept
{ {
std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName()); std::filesystem::remove(AppZoneHistory::AppZoneHistoryFileName());
std::filesystem::remove(AppliedLayouts::AppliedLayoutsFileName());
}
TEST_METHOD (EmptyZonesMoveLeftByIndex)
{
// prepare
PrepareEmptyLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)0, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{} == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (EmptyZonesRightByIndex)
{
// prepare
PrepareEmptyLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)0, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{} == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveLeftNonAppliedWindowByIndex)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 3 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveRightNonAppliedWindowByIndex)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowByIndex)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
const auto& layoutWindows = workArea->GetLayoutWindows();
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true);
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
}
TEST_METHOD (MoveAppliedWindowByIndexCycle)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ static_cast<ZoneIndex>(workArea->GetLayout()->Zones().size() - 1) } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowByIndexNoCycle)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, false);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (EmptyZonesMoveByPosition)
{
// prepare
PrepareEmptyLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)0, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{} == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveLeftNonAppliedWindowByPosition)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveRightNonAppliedWindowByPosition)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_RIGHT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowHorizontallyByPosition)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_RIGHT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowVerticallyByPosition)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_DOWN, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 2 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowByPositionHorizontallyCycle)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowByPositionHorizontallyNoCycle)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_RIGHT, true); // apply to 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_LEFT, false);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowByPositionVerticallyCycle)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
const auto& layoutWindows = workArea->GetLayoutWindows();
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_UP, true);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
Assert::IsTrue(ZoneIndexSet{ 2 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (MoveAppliedWindowByPositionVerticallyNoCycle)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
// test
workArea->MoveWindowIntoZoneByDirectionAndPosition(window, VK_UP, false);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (ExtendZoneHorizontally)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
// test
workArea->ExtendWindowByDirectionAndPosition(window, VK_RIGHT);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0, 1 } == layoutWindows->GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (ExtendZoneVertically)
{
// prepare
PrepareGridLayout();
auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->MoveWindowIntoZoneByIndexSet(window, { 0 }, true); // snap to the 1st zone
// test
workArea->ExtendWindowByDirectionAndPosition(window, VK_DOWN);
const auto& actualAppZoneHistory = AppZoneHistory::instance().GetFullAppZoneHistory();
Assert::AreEqual((size_t)1, actualAppZoneHistory.size());
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(ZoneIndexSet{ 0, 2 } == layoutWindows->GetZoneIndexSetFromWindow(window));
} }
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt) TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
{ {
LayoutData layout{
.uuid = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC58}").value(),
.type = FancyZonesDataTypes::ZoneSetLayoutType::Grid,
.showSpacing = false,
.spacing = 0,
.zoneCount = 4,
.sensitivityRadius = 20,
};
AppliedLayouts::instance().ApplyLayout(m_workAreaId, layout);
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
@@ -610,7 +224,10 @@ namespace FancyZonesUnitTests
SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW); SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW);
SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX); SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX);
workArea->MoveWindowIntoZoneByDirectionAndIndex(window, VK_LEFT, true); Assert::IsTrue(workArea->Snap(window, { 1 }, true));
// wait for the window to be resized
std::this_thread::sleep_for(std::chrono::milliseconds(10));
RECT inZoneRect; RECT inZoneRect;
GetWindowRect(window, &inZoneRect); GetWindowRect(window, &inZoneRect);
@@ -619,13 +236,46 @@ namespace FancyZonesUnitTests
Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top); Assert::AreEqual(originalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top);
} }
TEST_METHOD (WhenWindowIsResizablePlacingItIntoTheZoneShouldResizeIt)
{
LayoutData layout{
.uuid = FancyZonesUtils::GuidFromString(L"{61FA9FC0-26A6-4B37-A834-491C148DFC58}").value(),
.type = FancyZonesDataTypes::ZoneSetLayoutType::Grid,
.showSpacing = false,
.spacing = 0,
.zoneCount = 4,
.sensitivityRadius = 20,
};
AppliedLayouts::instance().ApplyLayout(m_workAreaId, layout);
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
SetWindowPos(window, nullptr, 150, 150, 450, 550, SWP_SHOWWINDOW);
Assert::IsTrue(workArea->Snap(window, { 1 }, true));
// wait for the window to be resized
std::this_thread::sleep_for(std::chrono::milliseconds(10));
RECT zonedWindowRect;
GetWindowRect(window, &zonedWindowRect);
RECT zoneRect = workArea->GetLayout()->Zones().at(1).GetZoneRect();
Assert::AreEqual(zoneRect.left, zonedWindowRect.left);
Assert::AreEqual(zoneRect.right, zonedWindowRect.right);
Assert::AreEqual(zoneRect.top, zonedWindowRect.top);
Assert::AreEqual(zoneRect.bottom, zonedWindowRect.bottom);
}
TEST_METHOD (SnapWindowPropertyTest) TEST_METHOD (SnapWindowPropertyTest)
{ {
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
const ZoneIndexSet expected = { 1, 2 }; const ZoneIndexSet expected = { 1, 2 };
workArea->SnapWindow(window, expected); Assert::IsTrue(workArea->Snap(window, expected));
const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
Assert::AreEqual(expected.size(), actual.size()); Assert::AreEqual(expected.size(), actual.size());
@@ -641,7 +291,7 @@ namespace FancyZonesUnitTests
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
const ZoneIndexSet expected = { 1, 2 }; const ZoneIndexSet expected = { 1, 2 };
workArea->SnapWindow(window, expected); Assert::IsTrue(workArea->Snap(window, expected));
const auto processPath = get_process_path(window); const auto processPath = get_process_path(window);
const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId); const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId);
@@ -654,13 +304,25 @@ namespace FancyZonesUnitTests
} }
} }
TEST_METHOD (SnapLayoutAssignedWindowsTest)
{
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
const ZoneIndexSet expected = { 1, 2 };
Assert::IsTrue(workArea->Snap(window, expected));
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(expected == layoutWindows.GetZoneIndexSetFromWindow(window));
}
TEST_METHOD (UnsnapPropertyTest) TEST_METHOD (UnsnapPropertyTest)
{ {
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
workArea->SnapWindow(window, { 1, 2 }); Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
workArea->UnsnapWindow(window); Assert::IsTrue(workArea->Unsnap(window));
const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window); const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
Assert::IsTrue(actual.empty()); Assert::IsTrue(actual.empty());
@@ -671,13 +333,25 @@ namespace FancyZonesUnitTests
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect); const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst); const auto window = Mocks::WindowCreate(m_hInst);
workArea->SnapWindow(window, { 1, 2 }); Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
workArea->UnsnapWindow(window); Assert::IsTrue(workArea->Unsnap(window));
const auto processPath = get_process_path(window); const auto processPath = get_process_path(window);
const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId); const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId);
Assert::IsFalse(history.has_value()); Assert::IsFalse(history.has_value());
} }
TEST_METHOD (UnsnapLayoutAssignedWindowsTest)
{
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
Assert::IsTrue(workArea->Unsnap(window));
const auto& layoutWindows = workArea->GetLayoutWindows();
Assert::IsTrue(layoutWindows.GetZoneIndexSetFromWindow(window).empty());
}
}; };
} }