mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
[FancyZones] Open window on active monitor (#4361)
* Initial design for opening window on active monitor * Perform entire handling in DPI unaware thread * Codestyle improvement * Improve resizing mechanism and optimise code a bit * Remove unneeded code, make simple helper functions inline * Make this feature configurable * Code optimization, improve positioning for some applications * Retry positioning for certain applications * Improve readability * Address PR comments: Minor code style improvements * Remove retries in custom positioning * Position new toggle in settings menu
This commit is contained in:
@@ -28,6 +28,11 @@ enum class DisplayChangeType
|
||||
Initialization
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int CUSTOM_POSITIONING_LEFT_TOP_PADDING = 16;
|
||||
}
|
||||
|
||||
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
|
||||
{
|
||||
public:
|
||||
@@ -413,12 +418,81 @@ void FancyZones::MoveWindowIntoZone(HWND window, winrt::com_ptr<IZoneWindow> zon
|
||||
}
|
||||
}
|
||||
|
||||
inline int RectWidth(const RECT& rect)
|
||||
{
|
||||
return rect.right - rect.left;
|
||||
}
|
||||
|
||||
inline int RectHeight(const RECT& rect)
|
||||
{
|
||||
return rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
RECT FitOnScreen(const RECT& windowRect, const RECT& originMonitorRect, const RECT& destMonitorRect)
|
||||
{
|
||||
// New window position on active monitor. If window fits the screen, this will be final position.
|
||||
int left = destMonitorRect.left + (windowRect.left - originMonitorRect.left);
|
||||
int top = destMonitorRect.top + (windowRect.top - originMonitorRect.top);
|
||||
int W = RectWidth(windowRect);
|
||||
int H = RectHeight(windowRect);
|
||||
|
||||
if ((left < destMonitorRect.left) || (left + W > destMonitorRect.right))
|
||||
{
|
||||
// Set left window border to left border of screen (add padding). Resize window width if needed.
|
||||
left = destMonitorRect.left + CUSTOM_POSITIONING_LEFT_TOP_PADDING;
|
||||
W = min(W, RectWidth(destMonitorRect) - CUSTOM_POSITIONING_LEFT_TOP_PADDING);
|
||||
}
|
||||
if ((top < destMonitorRect.top) || (top + H > destMonitorRect.bottom))
|
||||
{
|
||||
// Set top window border to top border of screen (add padding). Resize window height if needed.
|
||||
top = destMonitorRect.top + CUSTOM_POSITIONING_LEFT_TOP_PADDING;
|
||||
H = min(H, RectHeight(destMonitorRect) - CUSTOM_POSITIONING_LEFT_TOP_PADDING);
|
||||
}
|
||||
|
||||
return { .left = left,
|
||||
.top = top,
|
||||
.right = left + W,
|
||||
.bottom = top + H };
|
||||
}
|
||||
|
||||
void OpenWindowOnActiveMonitor(HWND window, HMONITOR monitor) noexcept
|
||||
{
|
||||
// By default Windows opens new window on primary monitor.
|
||||
// Try to preserve window width and height, adjust top-left corner if needed.
|
||||
HMONITOR origin = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
|
||||
if (origin == monitor)
|
||||
{
|
||||
// Certain applications by design open in last known position, regardless of FancyZones.
|
||||
// If that position is on currently active monitor, skip custom positioning.
|
||||
return;
|
||||
}
|
||||
|
||||
WINDOWPLACEMENT placement{};
|
||||
if (GetWindowPlacement(window, &placement))
|
||||
{
|
||||
MONITORINFOEX originMi;
|
||||
originMi.cbSize = sizeof(originMi);
|
||||
if (GetMonitorInfo(origin, &originMi))
|
||||
{
|
||||
MONITORINFOEX destMi;
|
||||
destMi.cbSize = sizeof(destMi);
|
||||
if (GetMonitorInfo(monitor, &destMi))
|
||||
{
|
||||
RECT newPosition = FitOnScreen(placement.rcNormalPosition, originMi.rcWork, destMi.rcWork);
|
||||
SizeWindowToRect(window, newPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IFancyZonesCallback
|
||||
IFACEMETHODIMP_(void)
|
||||
FancyZones::WindowCreated(HWND window) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
if (m_settings->GetSettings()->appLastZone_moveWindows && ShouldProcessNewWindow(window))
|
||||
const bool moveToAppLastZone = m_settings->GetSettings()->appLastZone_moveWindows;
|
||||
const bool openOnActiveMonitor = m_settings->GetSettings()->openWindowOnActiveMonitor;
|
||||
if ((moveToAppLastZone || openOnActiveMonitor) && ShouldProcessNewWindow(window))
|
||||
{
|
||||
HMONITOR primary = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
|
||||
HMONITOR active = primary;
|
||||
@@ -429,11 +503,20 @@ FancyZones::WindowCreated(HWND window) noexcept
|
||||
active = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
const bool primaryActive = (primary == active);
|
||||
std::pair<winrt::com_ptr<IZoneWindow>, std::vector<int>> appZoneHistoryInfo = GetAppZoneHistoryInfo(window, active, primaryActive);
|
||||
if (!appZoneHistoryInfo.second.empty())
|
||||
bool windowZoned{ false };
|
||||
if (moveToAppLastZone)
|
||||
{
|
||||
MoveWindowIntoZone(window, appZoneHistoryInfo.first, appZoneHistoryInfo.second);
|
||||
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);
|
||||
windowZoned = true;
|
||||
}
|
||||
}
|
||||
if (!windowZoned && openOnActiveMonitor)
|
||||
{
|
||||
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] { OpenWindowOnActiveMonitor(window, active); } }).wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user