Skip to content

Commit

Permalink
[FancyZones] Move windows to the app last zone fix (#24290)
Browse files Browse the repository at this point in the history
  • Loading branch information
SeraphimaZykova authored Feb 27, 2023
1 parent 3e651b8 commit f85fc98
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 146 deletions.
193 changes: 105 additions & 88 deletions src/modules/fancyzones/FancyZonesLib/FancyZones.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ enum class DisplayChangeType
Initialization
};

constexpr wchar_t* DisplayChangeTypeName (const DisplayChangeType type){
switch (type)
{
case DisplayChangeType::WorkArea:
return L"WorkArea";
case DisplayChangeType::DisplayChange:
return L"DisplayChange";
case DisplayChangeType::VirtualDesktop:
return L"VirtualDesktop";
case DisplayChangeType::Initialization:
return L"Initialization";
default:
return L"";
}
}

// Non-localizable strings
namespace NonLocalizable
{
Expand Down Expand Up @@ -131,13 +147,11 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
bool OnSnapHotkey(DWORD vkCode) noexcept;
bool ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle, WorkArea* const workArea) noexcept;

void RegisterVirtualDesktopUpdates() noexcept;
void SyncVirtualDesktops() noexcept;

void UpdateHotkey(int hotkeyId, const PowerToysSettings::HotkeyObject& hotkeyObject, bool enable) noexcept;

std::pair<WorkArea*, ZoneIndexSet> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& workAreas) noexcept;
void MoveWindowIntoZone(HWND window, WorkArea* const workArea, const ZoneIndexSet& zoneIndexSet) noexcept;
bool MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept;
bool MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept;

void UpdateActiveLayouts() noexcept;
bool ShouldProcessSnapHotkey(DWORD vkCode) noexcept;
Expand Down Expand Up @@ -252,7 +266,14 @@ FancyZones::Run() noexcept
}
});

PostMessage(m_window, WM_PRIV_VD_INIT, 0, 0);
SyncVirtualDesktops();

// id format of applied-layouts and app-zone-history was changed in 0.60
auto monitors = MonitorUtils::IdentifyMonitors();
AppliedLayouts::instance().AdjustWorkAreaIds(monitors);
AppZoneHistory::instance().AdjustWorkAreaIds(monitors);

PostMessage(m_window, WM_PRIV_INIT, 0, 0);
}

// IFancyZones
Expand Down Expand Up @@ -319,65 +340,50 @@ void FancyZones::MoveSizeEnd()
}
}

std::pair<WorkArea*, ZoneIndexSet> FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, const std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>>& workAreas) noexcept
bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR monitor) noexcept
{
for (const auto& [workAreaMonitor, workArea] : workAreas)
{
if (workAreaMonitor == monitor && workArea)
const auto& workAreas = m_workAreaHandler.GetAllWorkAreas();
WorkArea* workArea{ nullptr };
ZoneIndexSet indexes{};

if (monitor)
{
if (workAreas.contains(monitor))
{
return std::pair<WorkArea*, ZoneIndexSet>{ workArea.get(), workArea->GetWindowZoneIndexes(window) };
workArea = workAreas.at(monitor).get();
if (workArea)
{
indexes = workArea->GetWindowZoneIndexes(window);
}
}
else
{
Logger::error(L"Unable to find work area for requested monitor on the active virtual desktop");
}
}

Logger::error(L"No work area for the currently active monitor.");
return std::pair<WorkArea*, ZoneIndexSet>{ nullptr, {} };
}

void FancyZones::MoveWindowIntoZone(HWND window, WorkArea* const workArea, const ZoneIndexSet& zoneIndexSet) noexcept
{
if (workArea)
{
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
workArea->MoveWindowIntoZoneByIndexSet(window, zoneIndexSet);
AppZoneHistory::instance().UpdateProcessIdToHandleMap(window, workArea->UniqueId());
}
}

bool FancyZones::MoveToAppLastZone(HWND window, HMONITOR active, HMONITOR primary) noexcept
{
const auto& workAreas = m_workAreaHandler.GetAllWorkAreas();
if (workAreas.empty())
{
Logger::trace(L"No work area for the current desktop.");
return false;
}

// Search application history on currently active monitor.
auto appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, workAreas);

// No application history on currently active monitor
if (appZoneHistoryInfo.second.empty())
else
{
// Search application history on primary monitor.
appZoneHistoryInfo = GetAppZoneHistoryInfo(window, primary, workAreas);
for (const auto& [_, secondaryWorkArea] : workAreas)
{
if (secondaryWorkArea)
{
indexes = secondaryWorkArea->GetWindowZoneIndexes(window);
workArea = secondaryWorkArea.get();
if (!indexes.empty())
{
break;
}
}
}
}

// No application history on currently active and primary monitors
if (appZoneHistoryInfo.second.empty())

if (!indexes.empty() && workArea)
{
// Search application history on remaining monitors.
appZoneHistoryInfo = GetAppZoneHistoryInfo(window, nullptr, workAreas);
}
Trace::FancyZones::SnapNewWindowIntoZone(workArea->GetLayout().get(), workArea->GetLayoutWindows().get());
workArea->MoveWindowIntoZoneByIndexSet(window, indexes);

if (!appZoneHistoryInfo.second.empty())
{
MoveWindowIntoZone(window, appZoneHistoryInfo.first, appZoneHistoryInfo.second);
return true;
}
else
{
Logger::trace(L"App zone history is empty for the processing window on a current virtual desktop");
}

return false;
}
Expand Down Expand Up @@ -419,24 +425,35 @@ void FancyZones::WindowCreated(HWND window) noexcept
active = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY);
}

bool movedToAppLastZone = false;
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
bool windowMovedToZone = false;
if (moveToAppLastZone)
{
if (moveToAppLastZone)
if (FancyZonesSettings::settings().spanZonesAcrossMonitors)
{
movedToAppLastZone = MoveToAppLastZone(window, nullptr, nullptr);
windowMovedToZone = MoveToAppLastZone(window, nullptr);
}
}
else
{
if (moveToAppLastZone)
else
{
movedToAppLastZone = MoveToAppLastZone(window, active, primary);
// Search application history on currently active monitor.
windowMovedToZone = MoveToAppLastZone(window, active);

if (!windowMovedToZone && primary != active)
{
// Search application history on primary monitor.
windowMovedToZone = MoveToAppLastZone(window, primary);
}

if (!windowMovedToZone)
{
// Search application history on remaining monitors.
windowMovedToZone = MoveToAppLastZone(window, nullptr);
}
}
}


// Open on active monitor if window wasn't zoned
if (openOnActiveMonitor && !movedToAppLastZone)
if (openOnActiveMonitor && !windowMovedToZone)
{
// window is recreated after switching virtual desktop
// avoid moving already opened windows after switching vd
Expand Down Expand Up @@ -603,18 +620,16 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa
{
OnSnapHotkey(static_cast<DWORD>(lparam));
}
else if (message == WM_PRIV_VD_INIT)
else if (message == WM_PRIV_INIT)
{
VirtualDesktop::instance().UpdateVirtualDesktopId();
OnDisplayChange(DisplayChangeType::Initialization);
}
else if (message == WM_PRIV_VD_SWITCH)
{
VirtualDesktop::instance().UpdateVirtualDesktopId();
OnDisplayChange(DisplayChangeType::VirtualDesktop);
}
else if (message == WM_PRIV_VD_UPDATE)
{
OnDisplayChange(DisplayChangeType::Initialization);
}
else if (message == WM_PRIV_EDITOR)
{
// Clean up the event either way
Expand Down Expand Up @@ -685,27 +700,26 @@ LRESULT FancyZones::WndProc(HWND window, UINT message, WPARAM wparam, LPARAM lpa

void FancyZones::OnDisplayChange(DisplayChangeType changeType) noexcept
{
Logger::info(L"Display changed, type: {}", changeType);

if (changeType == DisplayChangeType::VirtualDesktop ||
changeType == DisplayChangeType::Initialization)
{
VirtualDesktop::instance().UpdateVirtualDesktopId();
Logger::info(L"Display changed, type: {}", DisplayChangeTypeName(changeType));

if (changeType == DisplayChangeType::Initialization)
{
RegisterVirtualDesktopUpdates();
bool updateWindowsPositions = false;

// id format of applied-layouts and app-zone-history was changed in 0.60
auto monitors = MonitorUtils::IdentifyMonitors();
AppliedLayouts::instance().AdjustWorkAreaIds(monitors);
AppZoneHistory::instance().AdjustWorkAreaIds(monitors);
}
switch (changeType)
{
case DisplayChangeType::WorkArea: // WorkArea size changed
case DisplayChangeType::DisplayChange: // Resolution changed or display added
updateWindowsPositions = FancyZonesSettings::settings().displayChange_moveWindows;
break;
case DisplayChangeType::VirtualDesktop: // Switched virtual desktop
break;
case DisplayChangeType::Initialization: // Initialization
updateWindowsPositions = FancyZonesSettings::settings().zoneSetChange_moveWindows;
break;
default:
break;
}

bool updateWindowsPositionsOnResolutionChange = FancyZonesSettings::settings().displayChange_moveWindows && changeType == DisplayChangeType::DisplayChange;
bool updateWindowsPositionsOnStart = FancyZonesSettings::settings().zoneSetChange_moveWindows && changeType == DisplayChangeType::Initialization;
UpdateWorkAreas(updateWindowsPositionsOnResolutionChange || updateWindowsPositionsOnStart);
UpdateWorkAreas(updateWindowsPositions);
}

void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAreaId& id, bool updateWindowsPositions) noexcept
Expand All @@ -729,6 +743,7 @@ void FancyZones::AddWorkArea(HMONITOR monitor, const FancyZonesDataTypes::WorkAr
auto workArea = WorkArea::Create(m_hinstance, id, m_workAreaHandler.GetParent(monitor), rect);
if (workArea)
{
workArea->InitSnappedWindows();
if (updateWindowsPositions)
{
workArea->UpdateWindowPositions();
Expand All @@ -754,6 +769,8 @@ LRESULT CALLBACK FancyZones::s_WndProc(HWND window, UINT message, WPARAM wparam,

void FancyZones::UpdateWorkAreas(bool updateWindowPositions) noexcept
{
Logger::debug(L"Update work areas, update windows positions: {}", updateWindowPositions);

m_workAreaHandler.SaveParentIds();
m_workAreaHandler.Clear();

Expand Down Expand Up @@ -1042,7 +1059,7 @@ bool FancyZones::ProcessDirectedSnapHotkey(HWND window, DWORD vkCode, bool cycle
}
}

void FancyZones::RegisterVirtualDesktopUpdates() noexcept
void FancyZones::SyncVirtualDesktops() noexcept
{
auto guids = VirtualDesktop::instance().GetVirtualDesktopIdsFromRegistry();
if (guids.has_value())
Expand Down Expand Up @@ -1101,7 +1118,7 @@ void FancyZones::SettingsUpdate(SettingId id)
case SettingId::SpanZonesAcrossMonitors:
{
m_workAreaHandler.Clear();
PostMessageW(m_window, WM_PRIV_VD_INIT, NULL, NULL);
PostMessageW(m_window, WM_PRIV_INIT, NULL, NULL);
}
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ bool AppZoneHistory::SetAppLastZones(HWND window, const FancyZonesDataTypes::Wor

bool AppZoneHistory::RemoveAppLastZone(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring_view& zoneSetId)
{
Logger::info(L"Add app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId);
Logger::info(L"Remove app zone history, device: {}, layout: {}", workAreaId.toString(), zoneSetId);

auto processPath = get_process_path_waiting_uwp(window);
if (!processPath.empty())
Expand Down Expand Up @@ -532,29 +532,6 @@ bool AppZoneHistory::IsAnotherWindowOfApplicationInstanceZoned(HWND window, cons
return false;
}

void AppZoneHistory::UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId)
{
auto processPath = get_process_path_waiting_uwp(window);
if (!processPath.empty())
{
auto history = m_history.find(processPath);
if (history != std::end(m_history))
{
auto& perDesktopData = history->second;
for (auto& data : perDesktopData)
{
if (data.workAreaId == workAreaId)
{
DWORD processId = 0;
GetWindowThreadProcessId(window, &processId);
data.processIdToHandleMap[processId] = window;
break;
}
}
}
}
}

ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const
{
auto processPath = get_process_path_waiting_uwp(window);
Expand All @@ -564,18 +541,14 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
return {};
}

auto srcVirtualDesktopIDStr = FancyZonesUtils::GuidToString(workAreaId.virtualDesktopId);
auto app = processPath;
auto pos = processPath.find_last_of('\\');
if (pos != std::string::npos && pos + 1 < processPath.length())
{
app = processPath.substr(pos + 1);
}

if (srcVirtualDesktopIDStr)
{
Logger::debug(L"Get {} zone history on monitor: {}, virtual desktop: {}", app, workAreaId.toString(), srcVirtualDesktopIDStr.value());
}
Logger::info(L"Get {} zone history on work area: {}", app, workAreaId.toString());

auto history = m_history.find(processPath);
if (history == std::end(m_history))
Expand All @@ -588,14 +561,9 @@ ZoneIndexSet AppZoneHistory::GetAppLastZoneIndexSet(HWND window, const FancyZone
{
if (data.zoneSetUuid == zoneSetId && data.workAreaId == workAreaId)
{
auto vdStr = FancyZonesUtils::GuidToString(data.workAreaId.virtualDesktopId);
if (vdStr)
{
Logger::debug(L"App zone history found on the device {} with virtual desktop {}", data.workAreaId.toString(), vdStr.value());
}

if (data.workAreaId.virtualDesktopId == workAreaId.virtualDesktopId || data.workAreaId.virtualDesktopId == GUID_NULL)
{
Logger::info(L"App zone history found on the work area {}", data.workAreaId.toString());
return data.zoneIndexSet;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class AppZoneHistory
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;
void UpdateProcessIdToHandleMap(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId);
ZoneIndexSet GetAppLastZoneIndexSet(HWND window, const FancyZonesDataTypes::WorkAreaId& workAreaId, const std::wstring& zoneSetId) const;

void SyncVirtualDesktops();
Expand Down
Loading

0 comments on commit f85fc98

Please sign in to comment.