[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/SettingsObserver.h>
#include <FancyZonesLib/trace.h>
#include <FancyZonesLib/WindowDrag.h>
#include <FancyZonesLib/WindowKeyboardSnap.h>
#include <FancyZonesLib/WindowMouseSnap.h>
#include <FancyZonesLib/WorkArea.h>
enum class DisplayChangeType
@@ -142,10 +143,6 @@ protected:
private:
void UpdateWorkAreas(bool updateWindowPositions) 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;
@@ -153,13 +150,11 @@ private:
bool MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept;
void UpdateActiveLayouts() noexcept;
void RefreshLayouts() noexcept;
bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept;
void ApplyQuickLayout(int key) noexcept;
void FlashZones() noexcept;
std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
HMONITOR WorkAreaKeyFromWindow(HWND window) noexcept;
virtual void SettingsUpdate(SettingId type) override;
@@ -167,7 +162,8 @@ private:
const HINSTANCE m_hinstance{};
HWND m_window{};
std::unique_ptr<WindowDrag> m_windowDrag{};
std::unique_ptr<WindowMouseSnap> m_windowMouseSnapper{};
WindowKeyboardSnap m_windowKeyboardSnapper{};
MonitorWorkAreaMap m_workAreaHandler;
DraggingState m_draggingState;
@@ -288,8 +284,8 @@ FancyZones::VirtualDesktopChanged() noexcept
void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
{
m_windowDrag = WindowDrag::Create(window, m_workAreaHandler.GetAllWorkAreas());
if (m_windowDrag)
m_windowMouseSnapper = WindowMouseSnap::Create(window, m_workAreaHandler.GetAllWorkAreas());
if (m_windowMouseSnapper)
{
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
{
@@ -298,13 +294,13 @@ void FancyZones::MoveSizeStart(HWND window, HMONITOR monitor)
m_draggingState.Enable();
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)
{
if (m_windowDrag)
if (m_windowMouseSnapper)
{
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
{
@@ -312,17 +308,17 @@ void FancyZones::MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen)
}
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()
{
if (m_windowDrag)
if (m_windowMouseSnapper)
{
m_windowDrag->MoveSizeEnd();
m_windowMouseSnapper->MoveSizeEnd();
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();
if (workArea)
{
indexes = workArea->GetWindowZoneIndexes(window);
indexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), workArea->GetLayoutId());
}
}
else
@@ -353,7 +349,7 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
{
if (secondaryWorkArea)
{
indexes = secondaryWorkArea->GetWindowZoneIndexes(window);
indexes = AppZoneHistory::instance().GetAppLastZoneIndexSet(window, secondaryWorkArea->UniqueId(), secondaryWorkArea->GetLayoutId());
workArea = secondaryWorkArea.get();
if (!indexes.empty())
{
@@ -365,8 +361,8 @@ bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
if (!indexes.empty() && workArea)
{
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
workArea->MoveWindowIntoZoneByIndexSet(window, indexes);
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows());
workArea->Snap(window, indexes);
return true;
}
@@ -604,7 +600,40 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
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)
{
@@ -661,7 +690,7 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
else if (message == WM_PRIV_APPLIED_LAYOUTS_FILE_UPDATE)
{
AppliedLayouts::instance().LoadData();
UpdateActiveLayouts();
RefreshLayouts();
}
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)
{
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 monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
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);
}
else
@@ -829,10 +858,10 @@ void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
{
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)
{
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
{
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())
{
if (workArea)
{
workArea->UpdateActiveZoneSet();
workArea->InitLayout();
if (FancyZonesSettings::settings().zoneSetChange_moveWindows)
{
@@ -1265,7 +1043,7 @@ void FancyZones::ApplyQuickLayout(int key) noexcept
{
AppliedLayouts::instance().ApplyLayout(workArea->UniqueId(), layout.value());
AppliedLayouts::instance().SaveData();
UpdateActiveLayouts();
RefreshLayouts();
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
{
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)

View File

@@ -103,13 +103,14 @@ namespace JsonUtils
}
data.workAreaId = deviceIdOpt.value();
data.zoneSetUuid = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID);
if (!FancyZonesUtils::IsValidGuid(data.zoneSetUuid))
std::wstring layoutIdStr = json.GetNamedString(NonLocalizable::AppZoneHistoryIds::LayoutIdID).c_str();
auto layoutIdOpt = FancyZonesUtils::GuidFromString(layoutIdStr);
if (!layoutIdOpt.has_value())
{
return std::nullopt;
}
data.layoutId = layoutIdOpt.value();
return data;
}
@@ -187,7 +188,11 @@ namespace JsonUtils
desktopData.SetNamedValue(NonLocalizable::AppZoneHistoryIds::LayoutIndexesID, jsonIndexSet);
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);
}
@@ -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))
{
@@ -347,8 +352,12 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
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;
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
data.processIdToHandleMap[processId] = window;
data.zoneSetUuid = zoneSetId;
data.layoutId = layoutId;
data.zoneIndexSet = zoneIndexSet;
SaveData();
return true;
@@ -373,7 +382,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
std::unordered_map<DWORD, HWND> processIdToHandleMap{};
processIdToHandleMap[processId] = window;
FancyZonesDataTypes::AppZoneHistoryData data{ .processIdToHandleMap = processIdToHandleMap,
.zoneSetUuid = zoneSetId,
.layoutId = layoutId,
.workAreaId = workAreaId,
.zoneIndexSet = zoneIndexSet };
@@ -392,56 +401,66 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor
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);
if (!processPath.empty())
if (processPath.empty())
{
auto history = m_history.find(processPath);
if (history != std::end(m_history))
{
auto& perDesktopData = history->second;
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
{
if (data->workAreaId == workAreaId && data->zoneSetUuid == zoneSetId)
{
if (!IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
data->processIdToHandleMap.erase(processId);
}
// if there is another instance of same application placed in the same zone don't erase history
auto windowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
for (auto placedWindow : data->processIdToHandleMap)
{
auto placedWindowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(placedWindow.second);
if (IsWindow(placedWindow.second) && (windowZoneStamps == placedWindowZoneStamps))
{
return false;
}
}
data = perDesktopData.erase(data);
if (perDesktopData.empty())
{
m_history.erase(processPath);
}
SaveData();
return true;
}
else
{
++data;
}
}
}
return false;
}
auto history = m_history.find(processPath);
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;
for (auto data = std::begin(perDesktopData); data != std::end(perDesktopData);)
{
if (data->workAreaId == workAreaId && data->layoutId == layoutId)
{
if (!IsAnotherWindowOfApplicationInstanceZoned(window, workAreaId))
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
data->processIdToHandleMap.erase(processId);
}
// if there is another instance of same application placed in the same zone don't erase history
auto windowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
for (auto placedWindow : data->processIdToHandleMap)
{
auto placedWindowZoneStamps = FancyZonesWindowProperties::RetrieveZoneIndexProperty(placedWindow.second);
if (IsWindow(placedWindow.second) && (windowZoneStamps == placedWindowZoneStamps))
{
return false;
}
}
data = perDesktopData.erase(data);
if (perDesktopData.empty())
{
m_history.erase(processPath);
}
SaveData();
return true;
}
else
{
++data;
}
}
return false;
}
@@ -532,7 +551,7 @@ bool AppZoneHistory::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
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);
if (processPath.empty())
@@ -559,7 +578,7 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
const auto& perDesktopData = history->second;
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)
{

View File

@@ -45,8 +45,8 @@ public:
void SaveData();
void AdjustWorkAreaIds(const std::vector<FancyZonesDataTypes::MonitorId>& ids);
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId, const ZoneIndexSet& zoneIndexSet);
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId);
bool SetAppLastZones(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId, const ZoneIndexSet& zoneIndexSet);
bool RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const GUID& layoutId);
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;
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 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::wstring zoneSetUuid;
WorkAreaId workAreaId;
ZoneIndexSet zoneIndexSet;
GUID layoutId = {};
WorkAreaId workAreaId = {};
ZoneIndexSet zoneIndexSet = {};
};
struct DeviceInfoData

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ namespace ZonedWindowProperties
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);
ZoneIndexSetBitmask bitmask = ZoneIndexSetBitmask::FromIndexSet(zoneSet);
@@ -33,6 +33,7 @@ void FancyZonesWindowProperties::StampZoneIndexProperty(HWND window, const ZoneI
if (!SetProp(window, ZonedWindowProperties::PropertyMultipleZone64ID, rawData))
{
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))
{
Logger::error(L"Failed to stamp window {}", get_last_error_or_default(GetLastError()));
return false;
}
}
return true;
}
void FancyZonesWindowProperties::RemoveZoneIndexProperty(HWND window)

View File

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

View File

@@ -277,13 +277,16 @@ namespace
.monitorId = { .deviceId = MonitorUtils::Display::ConvertObsoleteDeviceId(deviceId->deviceName) },
.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;
}
data.layoutId = layoutIdOpt.value();
return data;
}

View File

@@ -6,37 +6,10 @@
#include <FancyZonesLib/VirtualDesktop.h>
#include <FancyZonesLib/WindowUtils.h>
LayoutAssignedWindows::LayoutAssignedWindows()
{
m_extendData = std::make_unique<ExtendWindowModeData>();
}
void LayoutAssignedWindows::Assign(HWND window, const ZoneIndexSet& zones)
{
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)
{
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)
{
if (tabSortKeyWithinZone.has_value())

View File

@@ -4,19 +4,11 @@
class LayoutAssignedWindows
{
public:
struct ExtendWindowModeData
{
std::map<HWND, ZoneIndexSet> windowInitialIndexSet;
std::map<HWND, ZoneIndex> windowFinalIndex;
};
public :
LayoutAssignedWindows();
LayoutAssignedWindows() = default;
~LayoutAssignedWindows() = default;
void Assign(HWND window, const ZoneIndexSet& zones);
void Extend(HWND window, const ZoneIndexSet& zones);
void Dismiss(HWND window);
std::map<HWND, ZoneIndexSet> SnappedWindows() const noexcept;
@@ -25,12 +17,9 @@ public :
void CycleWindows(HWND window, bool reverse);
const std::unique_ptr<ExtendWindowModeData>& ExtendWindowData();
private:
std::map<HWND, ZoneIndexSet> m_windowIndexSet{};
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);
HWND GetNextZoneWindow(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;

View File

@@ -80,6 +80,13 @@ public:
#endif
}
#if defined(UNIT_TESTS)
inline void SetSettings(const Settings& settings)
{
m_settings = settings;
}
#endif
void AddObserver(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 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:
static std::unique_ptr<WindowDrag> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
~WindowDrag();
static std::unique_ptr<WindowMouseSnap> Create(HWND window, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& activeWorkAreas);
~WindowMouseSnap();
bool MoveSizeStart(HMONITOR monitor, bool isSnapping);
void MoveSizeUpdate(HMONITOR monitor, POINT const& ptScreen, bool isSnapping, bool isSelectManyZonesState);

View File

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

View File

@@ -1,26 +1,16 @@
#include "pch.h"
#include "WorkArea.h"
#include <common/logger/call_tracer.h>
#include <common/logger/logger.h>
#include <common/utils/winapi_error.h>
#include "FancyZonesData/AppliedLayouts.h"
#include "FancyZonesData/AppZoneHistory.h"
#include "FancyZonesDataTypes.h"
#include "SettingsObserver.h"
#include "ZonesOverlay.h"
#include "trace.h"
#include "on_thread_executor.h"
#include "Settings.h"
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/VirtualDesktop.h>
#include <FancyZonesLib/WindowUtils.h>
#include <ShellScalingApi.h>
#include <mutex>
#include <fileapi.h>
// disabling warning 4458 - declaration of 'identifier' hides class member
// to avoid warnings from GDI files - can't add winRT directory to external code
// in the Cpp.Build.props
@@ -127,284 +117,41 @@ WorkArea::~WorkArea()
windowPool.FreeZonesOverlayWindow(m_window);
}
void WorkArea::MoveWindowIntoZoneByIndex(HWND window, ZoneIndex index)
bool WorkArea::Snap(HWND window, const ZoneIndexSet& zones, bool updatePosition)
{
MoveWindowIntoZoneByIndexSet(window, { index });
}
void WorkArea::MoveWindowIntoZoneByIndexSet(HWND window, const ZoneIndexSet& indexSet, bool updatePosition /* = true*/)
{
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty() || indexSet.empty())
if (!m_layout || zones.empty())
{
return;
return false;
}
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
m_layoutWindows.Assign(window, zones);
AppZoneHistory::instance().SetAppLastZones(window, m_uniqueId, m_layout->Id(), zones);
if (updatePosition)
{
const auto rect = m_layout->GetCombinedZonesRect(indexSet);
if (rect.bottom - rect.top > 0 && rect.right - rect.left > 0)
{
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
}
const auto rect = m_layout->GetCombinedZonesRect(zones);
const auto adjustedRect = FancyZonesWindowUtils::AdjustRectForSizeWindowToRect(window, rect, m_window);
FancyZonesWindowUtils::SaveWindowSizeAndOrigin(window);
FancyZonesWindowUtils::SizeWindowToRect(window, adjustedRect);
}
SnapWindow(window, indexSet);
return FancyZonesWindowProperties::StampZoneIndexProperty(window, zones);
}
bool WorkArea::MoveWindowIntoZoneByDirectionAndIndex(HWND window, DWORD vkCode, bool cycle)
bool WorkArea::Unsnap(HWND window)
{
if (!m_layout || !m_layoutWindows || m_layout->Zones().empty())
if (!m_layout)
{
return false;
}
auto zoneIndexes = m_layoutWindows->GetZoneIndexSetFromWindow(window);
const auto numZones = m_layout->Zones().size();
// 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);
}
}
}
m_layoutWindows.Dismiss(window);
AppZoneHistory::instance().RemoveAppLastZone(window, m_uniqueId, m_layout->Id());
FancyZonesWindowProperties::RemoveZoneIndexProperty(window);
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);
}
const GUID WorkArea::GetLayoutId() const noexcept
{
if (m_layout)
@@ -415,29 +162,7 @@ const GUID WorkArea::GetLayoutId() const noexcept
return GUID{};
}
ZoneIndexSet WorkArea::GetWindowZoneIndexes(HWND window) const
{
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*/)
void WorkArea::ShowZones(const ZoneIndexSet& highlight, HWND draggedWindow/* = nullptr*/)
{
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)
{
@@ -465,15 +190,10 @@ void WorkArea::FlashZones()
}
}
void WorkArea::UpdateActiveZoneSet()
void WorkArea::InitLayout()
{
const bool isLayoutAlreadyApplied = AppliedLayouts::instance().IsLayoutApplied(m_uniqueId);
if (!isLayoutAlreadyApplied)
{
AppliedLayouts::instance().ApplyDefaultLayout(m_uniqueId);
}
InitLayout({});
CalculateZoneSet();
if (m_window && m_layout)
{
m_zonesOverlay->DrawActiveZoneSet(m_layout->Zones(), {}, Colors::GetZoneColors(), FancyZonesSettings::settings().showZoneNumber);
@@ -482,24 +202,16 @@ void WorkArea::UpdateActiveZoneSet()
void WorkArea::UpdateWindowPositions()
{
if (!m_layoutWindows)
{
return;
}
const auto& snappedWindows = m_layoutWindows->SnappedWindows();
const auto& snappedWindows = m_layoutWindows.SnappedWindows();
for (const auto& [window, zones] : snappedWindows)
{
MoveWindowIntoZoneByIndexSet(window, zones, true);
Snap(window, zones, true);
}
}
void WorkArea::CycleWindows(HWND window, bool reverse)
{
if (m_layoutWindows)
{
m_layoutWindows->CycleWindows(window, reverse);
}
m_layoutWindows.CycleWindows(window, reverse);
}
#pragma region private
@@ -537,6 +249,46 @@ void WorkArea::InitLayout(const FancyZonesDataTypes::WorkAreaId& parentUniqueId)
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()
{
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->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

View File

@@ -1,6 +1,5 @@
#pragma once
#include <FancyZonesLib/FancyZonesDataTypes.h>
#include <FancyZonesLib/Layout.h>
#include <FancyZonesLib/LayoutAssignedWindows.h>
@@ -39,26 +38,20 @@ public:
FancyZonesDataTypes::WorkAreaId UniqueId() const noexcept { return { m_uniqueId }; }
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 GUID GetLayoutId() const noexcept;
const FancyZonesUtils::Rect& GetWorkAreaRect() const noexcept { return m_workAreaRect; }
ZoneIndexSet GetWindowZoneIndexes(HWND window) const;
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 InitLayout();
void InitSnappedWindows();
void UpdateWindowPositions();
void ShowZonesOverlay(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr);
void HideZonesOverlay();
bool Snap(HWND window, const ZoneIndexSet& zones, bool updatePosition = true);
bool Unsnap(HWND window);
void ShowZones(const ZoneIndexSet& highlight, HWND draggedWindow = nullptr);
void HideZones();
void FlashZones();
void CycleWindows(HWND window, bool reverse);
@@ -79,6 +72,6 @@ private:
const FancyZonesDataTypes::WorkAreaId m_uniqueId;
HWND m_window{}; // Hidden tool window used to represent current monitor desktop work area.
std::unique_ptr<Layout> m_layout;
std::unique_ptr<LayoutAssignedWindows> m_layoutWindows;
LayoutAssignedWindows m_layoutWindows{};
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;
if (layout && layoutWindows)
if (layout)
{
auto zones = layout->Zones();
info.NumberOfZones = zones.size();
info.NumberOfWindows = 0;
for (int i = 0; i < static_cast<int>(zones.size()); i++)
{
if (!layoutWindows->IsZoneEmpty(i))
if (!layoutWindows.IsZoneEmpty(i))
{
info.NumberOfWindows++;
}
@@ -258,7 +258,7 @@ void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept
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);
TraceLoggingWrite(
@@ -271,7 +271,7 @@ void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, LayoutAssign
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);
TraceLoggingWrite(
@@ -356,7 +356,7 @@ void Trace::WorkArea::KeyUp(WPARAM wParam) noexcept
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);
TraceLoggingWrite(
@@ -369,7 +369,7 @@ void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, _In_opt
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);
TraceLoggingWrite(
@@ -382,7 +382,7 @@ void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, _In_opt_ La
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);
TraceLoggingWrite(

View File

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

View File

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

View File

@@ -207,22 +207,22 @@ namespace FancyZonesUnitTests
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
};
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;
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { expectedZoneIndex }));
Assert::IsFalse(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { expectedZoneIndex }));
}
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -230,12 +230,12 @@ namespace FancyZonesUnitTests
const auto window = nullptr;
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)
{
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -247,15 +247,15 @@ namespace FancyZonesUnitTests
const auto window = Mocks::WindowCreate(m_hInst);
const int expectedZoneIndex = 10;
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId1, zoneSetId, { expectedZoneIndex }));
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId1, zoneSetId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId2, zoneSetId));
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId1, layoutId, { expectedZoneIndex }));
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId1, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId2, layoutId));
}
TEST_METHOD (AppLastZoneSetIdTest)
{
const std::wstring zoneSetId1 = L"{B7A1F5A9-9DC2-4505-84AB-993253839093}";
const std::wstring zoneSetId2 = L"{B7A1F5A9-9DC2-4505-84AB-993253839094}";
const auto layoutId1 = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839093}").value();
const auto layoutId2 = FancyZonesUtils::GuidFromString(L"{B7A1F5A9-9DC2-4505-84AB-993253839094}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -263,56 +263,56 @@ namespace FancyZonesUnitTests
const auto window = Mocks::WindowCreate(m_hInst);
const int expectedZoneIndex = 10;
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId1, { expectedZoneIndex }));
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId1));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId2));
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId1, { expectedZoneIndex }));
Assert::IsTrue(std::vector<ZoneIndex>{ expectedZoneIndex } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId1));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId2));
}
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
};
const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetId, { 1 }));
Assert::IsTrue(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId));
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutId, { 1 }));
Assert::IsTrue(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
}
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
};
const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetId));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{} == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutId));
}
TEST_METHOD (AppLastZoneRemoveUnknownZoneSetId)
{
const std::wstring zoneSetIdToInsert = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}";
const std::wstring zoneSetIdToRemove = L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F1}";
const auto layoutIdToInsert = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F2}").value();
const auto layoutIdToRemove = FancyZonesUtils::GuidFromString(L"{2FEC41DA-3A0B-4E31-9CE1-9473C65D99F1}").value();
const FancyZonesDataTypes::WorkAreaId workAreaId{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
};
const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, zoneSetIdToInsert, { 1 }));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, zoneSetIdToRemove));
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, zoneSetIdToInsert));
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaId, layoutIdToInsert, { 1 }));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaId, layoutIdToRemove));
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaId, layoutIdToInsert));
}
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{39B25DD2-130D-4B5D-8851-4791D66B1539}").value()
@@ -323,20 +323,20 @@ namespace FancyZonesUnitTests
};
const auto window = Mocks::WindowCreate(m_hInst);
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaIdToInsert, zoneSetId, { 1 }));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaIdToRemove, zoneSetId));
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaIdToInsert, zoneSetId));
Assert::IsTrue(AppZoneHistory::instance().SetAppLastZones(window, workAreaIdToInsert, layoutId, { 1 }));
Assert::IsFalse(AppZoneHistory::instance().RemoveAppLastZone(window, workAreaIdToRemove, layoutId));
Assert::IsTrue(std::vector<ZoneIndex>{ 1 } == AppZoneHistory::instance().GetAppLastZoneIndexSet(window, workAreaIdToInsert, layoutId));
}
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{
.monitorId = { .deviceId = { .id = L"DELA026", .instanceId = L"5&10a58c63&0&UID16777488" } },
.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;
// prepare settings
PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey);
values.add_property(L"fancyzones_overlappingZonesAlgorithm", json::value(static_cast<int>(OverlappingZonesAlgorithm::Smallest)));
json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json());
FancyZonesSettings::instance().LoadSettings();
auto settings = FancyZonesSettings::settings();
settings.overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
FancyZonesSettings::instance().SetSettings(settings);
auto layout = std::make_unique<Layout>(data);
layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor());
@@ -168,10 +167,9 @@ namespace FancyZonesUnitTests
data.zoneCount = 4;
// prepare settings
PowerToysSettings::PowerToyValues values(NonLocalizable::ModuleKey, NonLocalizable::ModuleKey);
values.add_property(L"fancyzones_overlappingZonesAlgorithm", json::value(static_cast<int>(OverlappingZonesAlgorithm::Smallest)));
json::to_file(FancyZonesSettings::GetSettingsFileName(), values.get_raw_json());
FancyZonesSettings::instance().LoadSettings();
auto settings = FancyZonesSettings::settings();
settings.overlappingZonesAlgorithm = OverlappingZonesAlgorithm::Smallest;
FancyZonesSettings::instance().SetSettings(settings);
auto layout = std::make_unique<Layout>(data);
layout->Init(RECT{ 0, 0, 1920, 1080 }, Mocks::Monitor());

View File

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

View File

@@ -63,6 +63,9 @@
<ClCompile Include="DefaultLayoutsTests.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WindowKeyboardSnap.Spec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<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(AppZoneHistory::AppZoneHistoryFileName());
std::filesystem::remove(DefaultLayouts::DefaultLayoutsFileName());
}
@@ -58,7 +57,6 @@ namespace FancyZonesUnitTests
const auto& layout = workArea->GetLayout();
Assert::IsNotNull(layout.get());
Assert::IsNotNull(workArea->GetLayoutWindows().get());
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type()));
Assert::AreEqual(defaultLayout.zoneCount, static_cast<int>(layout->Zones().size()));
}
@@ -73,7 +71,6 @@ namespace FancyZonesUnitTests
const auto& layout = workArea->GetLayout();
Assert::IsNotNull(layout.get());
Assert::IsNotNull(workArea->GetLayoutWindows().get());
Assert::AreEqual(static_cast<int>(defaultLayout.type), static_cast<int>(layout->Type()));
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);
Assert::IsNotNull(actualWorkArea->GetLayout().get());
Assert::IsNotNull(actualWorkArea->GetLayoutWindows().get());
Assert::IsTrue(AppliedLayouts::instance().GetAppliedLayoutMap().contains(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}";
const FancyZonesDataTypes::WorkAreaId m_workAreaId{
HINSTANCE m_hInst{};
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 = {
.monitor = Mocks::Monitor(),
.deviceId = {
.id = L"DELA026",
.monitor = m_monitor,
.deviceId = {
.id = L"device-id-1",
.instanceId = L"5&10a58c63&0&UID16777488",
.number = 1,
},
.serialNumber = L"serial-number"
},
.virtualDesktopId = FancyZonesUtils::GuidFromString(m_virtualDesktopIdStr).value()
.serialNumber = L"serial-number-1" },
.virtualDesktopId = FancyZonesUtils::GuidFromString(L"{310F2924-B587-4D87-97C2-90031BDBE3F1}").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
{
AppZoneHistory::instance().LoadData();
AppliedLayouts::instance().LoadData();
}
TEST_METHOD_CLEANUP(CleanUp) noexcept
{
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)
{
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);
@@ -610,7 +224,10 @@ namespace FancyZonesUnitTests
SetWindowPos(window, nullptr, 150, 150, originalWidth, originalHeight, SWP_SHOWWINDOW);
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;
GetWindowRect(window, &inZoneRect);
@@ -619,13 +236,46 @@ namespace FancyZonesUnitTests
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)
{
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 };
workArea->SnapWindow(window, expected);
Assert::IsTrue(workArea->Snap(window, expected));
const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
Assert::AreEqual(expected.size(), actual.size());
@@ -641,7 +291,7 @@ namespace FancyZonesUnitTests
const auto window = Mocks::WindowCreate(m_hInst);
const ZoneIndexSet expected = { 1, 2 };
workArea->SnapWindow(window, expected);
Assert::IsTrue(workArea->Snap(window, expected));
const auto processPath = get_process_path(window);
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)
{
const auto workArea = WorkArea::Create(m_hInst, m_workAreaId, m_parentUniqueId, m_workAreaRect);
const auto window = Mocks::WindowCreate(m_hInst);
workArea->SnapWindow(window, { 1, 2 });
workArea->UnsnapWindow(window);
Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
Assert::IsTrue(workArea->Unsnap(window));
const auto actual = FancyZonesWindowProperties::RetrieveZoneIndexProperty(window);
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 window = Mocks::WindowCreate(m_hInst);
workArea->SnapWindow(window, { 1, 2 });
workArea->UnsnapWindow(window);
Assert::IsTrue(workArea->Snap(window, { 1, 2 }));
Assert::IsTrue(workArea->Unsnap(window));
const auto processPath = get_process_path(window);
const auto history = AppZoneHistory::instance().GetZoneHistory(processPath, m_workAreaId);
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());
}
};
}