Skip to content

Commit

Permalink
[FancyZones] Move window into last known position on active work area…
Browse files Browse the repository at this point in the history
… (if possible) (#4218)

* Move window into last known position on active work area (if possible)

* Refactor code to avoid double checks

* Address PR comments

* Perform all HWND checks at one place

* Improve handling of active/primary work area in app zone history

* Address PR comments: naming, arguments checks

* Rename some functions to increase readability

* Implement special handling in 2+ monitor scenario

* Minor naming change

* Simplify

* Improve readability

* Remove blank line

* Don't move away from secondary monitor if there is no app zone history

* Update comment

* FancyZonesEditor should not be zoned

* Preserve width and height (if possible) when opening on active monitor

* Maintain w/h whenever possible

* Remove scaling, add window coordinates on active monitor top-left corner

* If there is no app zone history on secondary screen, fallback to default windows behavior.
  • Loading branch information
vldmr11080 committed Jun 17, 2020
1 parent 7f25e3b commit 698e5ec
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 25 deletions.
121 changes: 97 additions & 24 deletions src/modules/fancyzones/lib/FancyZones.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,14 @@ struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZone
void RegisterVirtualDesktopUpdates(std::vector<GUID>& ids) noexcept;

bool IsSplashScreen(HWND window);
bool ShouldProcessNewWindow(HWND window) noexcept;
std::vector<int> GetZoneIndexSetFromWorkAreaHistory(HWND window, winrt::com_ptr<IZoneWindow> workArea) noexcept;
std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, std::unordered_map<HMONITOR, winrt::com_ptr<IZoneWindow>>& workAreaMap) noexcept;
std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, bool isPrimaryMonitor) noexcept;
void MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zoneWindow, const std::vector<int>& zoneIndexSet) noexcept;

void OnEditorExitEvent() noexcept;
bool ProcessSnapHotkey() noexcept;
bool ShouldProcessSnapHotkey() noexcept;

std::vector<std::pair<HMONITOR, RECT>> GetRawMonitorData() noexcept;
std::vector<HMONITOR> GetMonitorsSorted() noexcept;
Expand Down Expand Up @@ -323,35 +328,103 @@ FancyZones::VirtualDesktopInitialize() noexcept
PostMessage(m_window, WM_PRIV_VD_INIT, 0, 0);
}

bool FancyZones::ShouldProcessNewWindow(HWND window) noexcept
{
// Avoid processing splash screens, already stamped (zoned) windows, or those windows
// that belong to excluded applications list.
if (IsSplashScreen(window) ||
(reinterpret_cast<size_t>(::GetProp(window, MULTI_ZONE_STAMP)) != 0) ||
!IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
{
return false;
}
return true;
}

std::vector<int> FancyZones::GetZoneIndexSetFromWorkAreaHistory(
HWND window, winrt::com_ptr<IZoneWindow> workArea) noexcept
{
const auto activeZoneSet = workArea->ActiveZoneSet();
if (activeZoneSet)
{
wil::unique_cotaskmem_string zoneSetId;
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &zoneSetId)))
{
return JSONHelpers::FancyZonesDataInstance().GetAppLastZoneIndexSet(window, workArea->UniqueId(), zoneSetId.get());
}
}
return {};
}

std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> FancyZones::GetAppZoneHistoryInfo(
HWND window,
HMONITOR monitor,
std::unordered_map<HMONITOR, winrt::com_ptr<IZoneWindow>>& workAreaMap) noexcept
{
if (workAreaMap.contains(monitor))
{
auto workArea = workAreaMap[monitor];
workAreaMap.erase(monitor); // monitor processed, remove entry from the map
return { workArea, GetZoneIndexSetFromWorkAreaHistory(window, workArea) };
}
return { nullptr, {} };
}

std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> FancyZones::GetAppZoneHistoryInfo(HWND window, HMONITOR monitor, bool isPrimaryMonitor) noexcept
{
std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> appZoneHistoryInfo{ nullptr, {} };
auto workAreaMap = m_workAreaHandler.GetWorkAreasByDesktopId(m_currentDesktopId);

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

if (isPrimaryMonitor && appZoneHistoryInfo.second.empty())
{
// No application history on primary monitor, search on remaining monitors.
for (const auto& [monitor, workArea] : workAreaMap)
{
auto zoneIndexSet = GetZoneIndexSetFromWorkAreaHistory(window, workArea);
if (!zoneIndexSet.empty())
{
return { workArea, zoneIndexSet };
}
}
}

return appZoneHistoryInfo;
}

void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zoneWindow, const std::vector<int>& zoneIndexSet) noexcept
{
auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
if (!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId()))
{
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
fancyZonesData.UpdateProcessIdToHandleMap(window, zoneWindow->UniqueId());
}
}

// IFancyZonesCallback
IFACEMETHODIMP_(void)
FancyZones::WindowCreated(HWND window) noexcept
{
std::shared_lock readLock(m_lock);

if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window, m_settings->GetSettings()->excludedAppsArray))
if (m_settings->GetSettings()->appLastZone_moveWindows && ShouldProcessNewWindow(window))
{
auto zoneWindow = m_workAreaHandler.GetWorkArea(window);
if (zoneWindow)
HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
HMONITOR active = primary;

POINT cursorPosition{};
if (GetCursorPos(&cursorPosition))
{
const auto activeZoneSet = zoneWindow->ActiveZoneSet();
if (activeZoneSet)
{
auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
active = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY);
}

wil::unique_cotaskmem_string guidString;
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &guidString)))
{
std::vector<int> zoneIndexSet = fancyZonesData.GetAppLastZoneIndexSet(window, zoneWindow->UniqueId(), guidString.get());
if (zoneIndexSet.size() &&
!IsSplashScreen(window) &&
!fancyZonesData.IsAnotherWindowOfApplicationInstanceZoned(window, zoneWindow->UniqueId()))
{
m_windowMoveHandler.MoveWindowIntoZoneByIndexSet(window, zoneIndexSet, zoneWindow);
fancyZonesData.UpdateProcessIdToHandleMap(window, zoneWindow->UniqueId());
}
}
}
const bool primaryActive = (primary == active);
std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, primaryActive);
if (!appZoneHistoryInfo.second.empty())
{
MoveWindowIntoZone(window, appZoneHistoryInfo.first, appZoneHistoryInfo.second);
}
}
}
Expand Down Expand Up @@ -379,7 +452,7 @@ FancyZones::OnKeyDown(PKBDLLHOOKSTRUCT info) noexcept
}
else if ((info->vkCode == VK_RIGHT) || (info->vkCode == VK_LEFT))
{
if (ProcessSnapHotkey())
if (ShouldProcessSnapHotkey())
{
Trace::FancyZones::OnKeyDown(info->vkCode, win, ctrl, false /*inMoveSize*/);
// Win+Left, Win+Right will cycle through Zones in the active ZoneSet when WM_PRIV_LOWLEVELKB's handled
Expand Down Expand Up @@ -904,7 +977,7 @@ void FancyZones::OnEditorExitEvent() noexcept
}
}

bool FancyZones::ProcessSnapHotkey() noexcept
bool FancyZones::ShouldProcessSnapHotkey() noexcept
{
if (m_settings->GetSettings()->overrideSnapHotkeys)
{
Expand Down
12 changes: 11 additions & 1 deletion src/modules/fancyzones/lib/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
#include <common/common.h>
#include <common/dpi_aware.h>

namespace
{
const wchar_t POWER_TOYS_APP_POWER_LAUCHER[] = L"POWERLAUNCHER.EXE";
const wchar_t POWER_TOYS_APP_FANCY_ZONES_EDITOR[] = L"FANCYZONESEDITOR.EXE";
}

typedef BOOL(WINAPI* GetDpiForMonitorInternalFunc)(HMONITOR, UINT, UINT*, UINT*);
UINT GetDpiForMonitor(HMONITOR monitor) noexcept
{
Expand Down Expand Up @@ -157,7 +163,11 @@ bool IsInterestingWindow(HWND window, const std::vector<std::wstring>& excludedA
{
return false;
}
if (find_app_name_in_path(filtered.process_path, { L"POWERLAUNCHER.EXE" }))
if (find_app_name_in_path(filtered.process_path, { POWER_TOYS_APP_POWER_LAUCHER }))
{
return false;
}
if (find_app_name_in_path(filtered.process_path, { POWER_TOYS_APP_FANCY_ZONES_EDITOR }))
{
return false;
}
Expand Down

0 comments on commit 698e5ec

Please sign in to comment.