mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
Basic support for snapping to multiple zones (#1955)
* Refactor a method which resizes windows * Completed initial work for MultiZones Without changing any test, they all pass! * Implemented a basic version of Multizones, updated some tests * Reduced the sensitivity radius * Added a few must-have unit tests for Multizones * Some fixups * Took care of the conflict between this and #1938 * Improved how zones are detected, reverted a change in one unit test * Resolved another merge conflict * Fixed bugs related to stamping
This commit is contained in:
@@ -150,6 +150,7 @@ public:
|
||||
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
|
||||
|
||||
void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept;
|
||||
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept;
|
||||
|
||||
protected:
|
||||
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
||||
@@ -695,6 +696,12 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
||||
}
|
||||
|
||||
void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
MoveWindowIntoZoneByIndexSet(window, monitor, { index });
|
||||
}
|
||||
|
||||
void FancyZones::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
if (window != m_windowMoveSize)
|
||||
@@ -706,7 +713,7 @@ void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int in
|
||||
if (zoneWindow != m_zoneWindowMap.end())
|
||||
{
|
||||
const auto& zoneWindowPtr = zoneWindow->second;
|
||||
zoneWindowPtr->MoveWindowIntoZoneByIndex(window, index);
|
||||
zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <common/monitors.h>
|
||||
#include "Zone.h"
|
||||
#include "Settings.h"
|
||||
#include "util.h"
|
||||
|
||||
struct Zone : winrt::implements<Zone, IZone>
|
||||
{
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
|
||||
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
|
||||
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
|
||||
IFACEMETHODIMP_(RECT) ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept;
|
||||
|
||||
private:
|
||||
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
|
||||
@@ -61,12 +63,11 @@ IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize)
|
||||
|
||||
void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
{
|
||||
// Skip invisible windows
|
||||
if (!IsWindowVisible(window))
|
||||
{
|
||||
return;
|
||||
}
|
||||
SizeWindowToRect(window, ComputeActualZoneRect(window, zoneWindow));
|
||||
}
|
||||
|
||||
RECT Zone::ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept
|
||||
{
|
||||
// Take care of 1px border
|
||||
RECT newWindowRect = m_zoneRect;
|
||||
|
||||
@@ -111,29 +112,7 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top);
|
||||
}
|
||||
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
|
||||
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
|
||||
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
::GetWindowPlacement(window, &placement);
|
||||
}
|
||||
|
||||
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
|
||||
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
|
||||
{
|
||||
placement.showCmd = SW_RESTORE | SW_SHOWNA;
|
||||
}
|
||||
|
||||
placement.rcNormalPosition = newWindowRect;
|
||||
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
||||
|
||||
::SetWindowPlacement(window, &placement);
|
||||
// Do it again, allowing Windows to resize the window and set correct scaling
|
||||
// This fixes Issue #365
|
||||
::SetWindowPlacement(window, &placement);
|
||||
return newWindowRect;
|
||||
}
|
||||
|
||||
void Zone::StampZone(HWND window, bool stamp) noexcept
|
||||
|
||||
@@ -42,9 +42,20 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
|
||||
*/
|
||||
IFACEMETHOD_(void, SetId)(size_t id) = 0;
|
||||
/**
|
||||
* @retirns Zone identifier.
|
||||
* @returns Zone identifier.
|
||||
*/
|
||||
IFACEMETHOD_(size_t, Id)() = 0;
|
||||
|
||||
/**
|
||||
* Compute the coordinates of the rectangle to which a window should be resized.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @returns a RECT structure, describing global coordinates to which a window should be resized
|
||||
*/
|
||||
IFACEMETHOD_(RECT, ComputeActualZoneRect)(HWND window, HWND zoneWindow) = 0;
|
||||
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept;
|
||||
|
||||
@@ -122,14 +122,16 @@ public:
|
||||
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
|
||||
LayoutType() noexcept { return m_config.LayoutType; }
|
||||
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
|
||||
ZoneFromPoint(POINT pt) noexcept;
|
||||
IFACEMETHODIMP_(std::vector<int>)
|
||||
ZonesFromPoint(POINT pt) noexcept;
|
||||
IFACEMETHODIMP_(int)
|
||||
GetZoneIndexFromWindow(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>)
|
||||
GetZones() noexcept { return m_zones; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
|
||||
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index, bool stampZone) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
@@ -162,41 +164,79 @@ IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
|
||||
ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
||||
IFACEMETHODIMP_(std::vector<int>)
|
||||
ZoneSet::ZonesFromPoint(POINT pt) noexcept
|
||||
{
|
||||
winrt::com_ptr<IZone> smallestKnownZone = nullptr;
|
||||
// To reduce redundant calculations, we will store the last known zones area.
|
||||
int smallestKnownZoneArea = INT32_MAX;
|
||||
for (auto iter = m_zones.rbegin(); iter != m_zones.rend(); iter++)
|
||||
const int SENSITIVITY_RADIUS = 20;
|
||||
std::vector<int> capturedZones;
|
||||
std::vector<int> strictlyCapturedZones;
|
||||
for (size_t i = 0; i < m_zones.size(); i++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
auto zone = m_zones[i];
|
||||
RECT newZoneRect = zone->GetZoneRect();
|
||||
if (newZoneRect.left < newZoneRect.right && newZoneRect.top < newZoneRect.bottom) // proper zone
|
||||
{
|
||||
RECT* newZoneRect = &zone->GetZoneRect();
|
||||
if (PtInRect(newZoneRect, pt))
|
||||
if (newZoneRect.left - SENSITIVITY_RADIUS <= pt.x && pt.x <= newZoneRect.right + SENSITIVITY_RADIUS &&
|
||||
newZoneRect.top - SENSITIVITY_RADIUS <= pt.y && pt.y <= newZoneRect.bottom + SENSITIVITY_RADIUS)
|
||||
{
|
||||
if (smallestKnownZone == nullptr)
|
||||
{
|
||||
smallestKnownZone = zone;
|
||||
|
||||
RECT* r = &smallestKnownZone->GetZoneRect();
|
||||
smallestKnownZoneArea = (r->right - r->left) * (r->bottom - r->top);
|
||||
}
|
||||
else
|
||||
{
|
||||
int newZoneArea = (newZoneRect->right - newZoneRect->left) * (newZoneRect->bottom - newZoneRect->top);
|
||||
|
||||
if (newZoneArea < smallestKnownZoneArea)
|
||||
{
|
||||
smallestKnownZone = zone;
|
||||
smallestKnownZoneArea = newZoneArea;
|
||||
}
|
||||
}
|
||||
capturedZones.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
|
||||
if (newZoneRect.left <= pt.x && pt.x < newZoneRect.right &&
|
||||
newZoneRect.top <= pt.y && pt.y < newZoneRect.bottom)
|
||||
{
|
||||
strictlyCapturedZones.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return smallestKnownZone;
|
||||
// If only one zone is captured, but it's not strictly captured
|
||||
// don't consider it as captured
|
||||
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// If captured zones do not overlap, return all of them
|
||||
// Otherwise, return the smallest one
|
||||
|
||||
bool overlap = false;
|
||||
for (size_t i = 0; i < capturedZones.size(); ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < capturedZones.size(); ++j)
|
||||
{
|
||||
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
|
||||
auto rectJ = m_zones[capturedZones[j]]->GetZoneRect();
|
||||
if (max(rectI.top, rectJ.top) < min(rectI.bottom, rectJ.bottom) &&
|
||||
max(rectI.left, rectJ.left) < min(rectI.right, rectJ.right))
|
||||
{
|
||||
overlap = true;
|
||||
i = capturedZones.size() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
size_t smallestIdx = 0;
|
||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||
{
|
||||
auto rectS = m_zones[capturedZones[smallestIdx]]->GetZoneRect();
|
||||
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
|
||||
int smallestSize = (rectS.bottom - rectS.top) * (rectS.right - rectS.left);
|
||||
int iSize = (rectI.bottom - rectI.top) * (rectI.right - rectI.left);
|
||||
|
||||
if (iSize <= smallestSize)
|
||||
{
|
||||
smallestIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
capturedZones = { capturedZones[smallestIdx] };
|
||||
}
|
||||
|
||||
return capturedZones;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(int)
|
||||
@@ -217,7 +257,7 @@ ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
|
||||
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index, bool stampZone) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
@@ -236,7 +276,55 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
|
||||
|
||||
if (auto zone = m_zones.at(index))
|
||||
{
|
||||
zone->AddWindowToZone(window, windowZone, false);
|
||||
zone->AddWindowToZone(window, windowZone, stampZone);
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (auto zoneDrop = ZoneFromWindow(window))
|
||||
{
|
||||
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||
}
|
||||
|
||||
if (indexSet.size() == 1)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, windowZone, indexSet[0], stampZone);
|
||||
return;
|
||||
}
|
||||
|
||||
RECT size;
|
||||
bool sizeEmpty = true;
|
||||
|
||||
for (int index : indexSet)
|
||||
{
|
||||
if (index < static_cast<int>(m_zones.size()))
|
||||
{
|
||||
RECT newSize = m_zones.at(index)->ComputeActualZoneRect(window, windowZone);
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
size.left = min(size.left, newSize.left);
|
||||
size.top = min(size.top, newSize.top);
|
||||
size.right = max(size.right, newSize.right);
|
||||
size.bottom = max(size.bottom, newSize.bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = newSize;
|
||||
sizeEmpty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
SizeWindowToRect(window, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,10 +394,8 @@ ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient)
|
||||
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||
}
|
||||
|
||||
if (auto zone = ZoneFromPoint(ptClient))
|
||||
{
|
||||
zone->AddWindowToZone(window, zoneWindow, true);
|
||||
}
|
||||
auto zones = ZonesFromPoint(ptClient);
|
||||
MoveWindowIntoZoneByIndexSet(window, zoneWindow, zones, true);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
|
||||
@@ -24,12 +24,12 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
*/
|
||||
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
|
||||
/**
|
||||
* Get zone from cursor coordinates.
|
||||
* Get zones from cursor coordinates.
|
||||
*
|
||||
* @param pt Cursor coordinates.
|
||||
* @returns Zone object (defining coordinates of the zone).
|
||||
* @returns Vector of indices, corresponding to the current set of zones - the zones considered active.
|
||||
*/
|
||||
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
|
||||
IFACEMETHOD_(std::vector<int>, ZonesFromPoint)(POINT pt) = 0;
|
||||
/**
|
||||
* Get index of the zone inside zone layout by window assigned to it.
|
||||
*
|
||||
@@ -48,8 +48,20 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param index Zone index within zone layout.
|
||||
* @param stampZone Whether the window being added to the zone should be stamped.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index, bool stampZone) = 0;
|
||||
/**
|
||||
* Assign window to the zones based on the set of zone indices inside zone layout.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param indexSet The set of zone indices within zone layout.
|
||||
* @param stampZone Whether the window being added to the zone should be stamped,
|
||||
in case a single window is to be added.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND zoneWindow, const std::vector<int>& indexSet, bool stampZone) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
|
||||
*
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace ZoneWindowDrawUtils
|
||||
COLORREF highlightColor,
|
||||
int zoneOpacity,
|
||||
const std::vector<winrt::com_ptr<IZone>>& zones,
|
||||
const winrt::com_ptr<IZone>& highlightZone,
|
||||
const std::vector<int>& highlightZones,
|
||||
bool flashMode,
|
||||
bool drawHints) noexcept
|
||||
{
|
||||
@@ -158,15 +158,22 @@ namespace ZoneWindowDrawUtils
|
||||
ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 };
|
||||
ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
|
||||
|
||||
std::vector<bool> isHighlighted(zones.size(), false);
|
||||
for (int x : highlightZones)
|
||||
{
|
||||
isHighlighted[x] = true;
|
||||
}
|
||||
|
||||
for (auto iter = zones.begin(); iter != zones.end(); iter++)
|
||||
{
|
||||
int zoneId = static_cast<int>(iter - zones.begin());
|
||||
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
|
||||
if (!zone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zone != highlightZone)
|
||||
if (!isHighlighted[zoneId])
|
||||
{
|
||||
if (flashMode)
|
||||
{
|
||||
@@ -182,13 +189,12 @@ namespace ZoneWindowDrawUtils
|
||||
DrawZone(hdc, colorViewer, zone, zones, flashMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightZone)
|
||||
{
|
||||
colorHighlight.fill = highlightColor;
|
||||
colorHighlight.border = zoneBorderColor;
|
||||
DrawZone(hdc, colorHighlight, highlightZone, zones, flashMode);
|
||||
else
|
||||
{
|
||||
colorHighlight.fill = highlightColor;
|
||||
colorHighlight.border = zoneBorderColor;
|
||||
DrawZone(hdc, colorHighlight, zone, zones, flashMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +216,8 @@ public:
|
||||
IsDragEnabled() noexcept { return m_dragEnabled; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
@@ -238,7 +246,7 @@ private:
|
||||
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
void OnPaint(wil::unique_hdc& hdc) noexcept;
|
||||
void OnKeyUp(WPARAM wparam) noexcept;
|
||||
winrt::com_ptr<IZone> ZoneFromPoint(POINT pt) noexcept;
|
||||
std::vector<int> ZonesFromPoint(POINT pt) noexcept;
|
||||
void CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept;
|
||||
void FlashZones() noexcept;
|
||||
|
||||
@@ -253,7 +261,7 @@ private:
|
||||
bool m_dragEnabled{};
|
||||
winrt::com_ptr<IZoneSet> m_activeZoneSet;
|
||||
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
|
||||
winrt::com_ptr<IZone> m_highlightZone;
|
||||
std::vector<int> m_highlightZone;
|
||||
WPARAM m_keyLast{};
|
||||
size_t m_keyCycle{};
|
||||
static const UINT m_showAnimationDuration = 200; // ms
|
||||
@@ -363,7 +371,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
|
||||
m_dragEnabled = dragEnabled;
|
||||
m_windowMoveSize = window;
|
||||
m_drawHints = true;
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
ShowZoneWindow();
|
||||
return S_OK;
|
||||
}
|
||||
@@ -378,13 +386,13 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
|
||||
|
||||
if (dragEnabled)
|
||||
{
|
||||
auto highlightZone = ZoneFromPoint(ptClient);
|
||||
auto highlightZone = ZonesFromPoint(ptClient);
|
||||
redraw = (highlightZone != m_highlightZone);
|
||||
m_highlightZone = std::move(highlightZone);
|
||||
}
|
||||
else if (m_highlightZone)
|
||||
else if (m_highlightZone.size())
|
||||
{
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
@@ -432,10 +440,16 @@ ZoneWindow::RestoreOrginalTransparency() noexcept
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
|
||||
{
|
||||
MoveWindowIntoZoneByIndexSet(window, { index });
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept
|
||||
{
|
||||
if (m_activeZoneSet)
|
||||
{
|
||||
m_activeZoneSet->MoveWindowIntoZoneByIndex(window, m_window.get(), index);
|
||||
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +532,7 @@ ZoneWindow::HideZoneWindow() noexcept
|
||||
m_keyLast = 0;
|
||||
m_windowMoveSize = nullptr;
|
||||
m_drawHints = false;
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,13 +699,13 @@ void ZoneWindow::OnKeyUp(WPARAM wparam) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZone> ZoneWindow::ZoneFromPoint(POINT pt) noexcept
|
||||
std::vector<int> ZoneWindow::ZonesFromPoint(POINT pt) noexcept
|
||||
{
|
||||
if (m_activeZoneSet)
|
||||
{
|
||||
return m_activeZoneSet->ZoneFromPoint(pt);
|
||||
return m_activeZoneSet->ZonesFromPoint(pt);
|
||||
}
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept
|
||||
@@ -739,7 +753,7 @@ void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::Inp
|
||||
{
|
||||
m_host->MoveWindowsOnActiveZoneSetChange();
|
||||
}
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
}
|
||||
|
||||
void ZoneWindow::FlashZones() noexcept
|
||||
|
||||
@@ -53,6 +53,13 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
||||
* @param index Zone index within zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0;
|
||||
/**
|
||||
* Assign window to the zones based on the set of zone indices inside zone layout.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param indexSet The set of zone indices within zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const std::vector<int>& indexSet) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
|
||||
*
|
||||
|
||||
@@ -108,3 +108,30 @@ void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
|
||||
monitorInfo = std::move(sortedMonitorInfo);
|
||||
}
|
||||
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept
|
||||
{
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
|
||||
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
|
||||
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
::GetWindowPlacement(window, &placement);
|
||||
}
|
||||
|
||||
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
|
||||
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
|
||||
{
|
||||
placement.showCmd = SW_RESTORE | SW_SHOWNA;
|
||||
}
|
||||
|
||||
placement.rcNormalPosition = rect;
|
||||
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
||||
|
||||
::SetWindowPlacement(window, &placement);
|
||||
// Do it again, allowing Windows to resize the window and set correct scaling
|
||||
// This fixes Issue #365
|
||||
::SetWindowPlacement(window, &placement);
|
||||
}
|
||||
|
||||
@@ -118,3 +118,4 @@ inline BYTE OpacitySettingToAlpha(int opacity)
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (ZoneFromPointEmpty)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(nullptr == actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointInner)
|
||||
@@ -146,9 +146,9 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
for (int j = top + 1; j < bottom; j++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ i, j });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(expected, actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ i, j });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(expected, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,29 +161,29 @@ namespace FancyZonesUnitTests
|
||||
|
||||
for (int i = left; i < right; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ i, top });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(expected, actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ i, top });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(expected, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
|
||||
for (int i = top; i < bottom; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ left, i });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(expected, actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ left, i });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(expected, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
|
||||
//bottom and right borders considered to be outside
|
||||
for (int i = left; i < right; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ i, bottom });
|
||||
Assert::IsTrue(nullptr == actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ i, bottom });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
for (int i = top; i < bottom; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ right, i });
|
||||
Assert::IsTrue(nullptr == actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ right, i });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,8 +193,8 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone = MakeZone({ left, top, right, bottom });
|
||||
m_set->AddZone(zone);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 101, 101 });
|
||||
Assert::IsTrue(actual == nullptr);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 200, 200 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointOverlapping)
|
||||
@@ -208,9 +208,65 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 10, 10, 50, 50 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(zone2, actual);
|
||||
// zone4 is expected because it's the smallest one, and it's considered to be inside
|
||||
// since Multizones support
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(zone4, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointMultizoneHorizontal)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
|
||||
m_set->AddZone(zone1);
|
||||
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
|
||||
m_set->AddZone(zone2);
|
||||
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
|
||||
m_set->AddZone(zone3);
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 50, 100 });
|
||||
Assert::IsTrue(actual.size() == 2);
|
||||
compareZones(zone1, m_set->GetZones()[actual[0]]);
|
||||
compareZones(zone3, m_set->GetZones()[actual[1]]);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointMultizoneVertical)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
|
||||
m_set->AddZone(zone1);
|
||||
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
|
||||
m_set->AddZone(zone2);
|
||||
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
|
||||
m_set->AddZone(zone3);
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 100, 50 });
|
||||
Assert::IsTrue(actual.size() == 2);
|
||||
compareZones(zone1, m_set->GetZones()[actual[0]]);
|
||||
compareZones(zone2, m_set->GetZones()[actual[1]]);
|
||||
}
|
||||
|
||||
TEST_METHOD(ZoneFromPointMultizoneQuad)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
|
||||
m_set->AddZone(zone1);
|
||||
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
|
||||
m_set->AddZone(zone2);
|
||||
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
|
||||
m_set->AddZone(zone3);
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 100, 100 });
|
||||
Assert::IsTrue(actual.size() == 4);
|
||||
compareZones(zone1, m_set->GetZones()[actual[0]]);
|
||||
compareZones(zone2, m_set->GetZones()[actual[1]]);
|
||||
compareZones(zone3, m_set->GetZones()[actual[2]]);
|
||||
compareZones(zone4, m_set->GetZones()[actual[3]]);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointWithNotNormalizedRect)
|
||||
@@ -218,8 +274,8 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone = MakeZone({ 100, 100, 0, 0 });
|
||||
m_set->AddZone(zone);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual == nullptr);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointWithZeroRect)
|
||||
@@ -227,8 +283,8 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone = MakeZone({ 0, 0, 0, 0 });
|
||||
m_set->AddZone(zone);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(actual == nullptr);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneIndexFromWindow)
|
||||
@@ -316,7 +372,7 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsTrue(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
@@ -325,7 +381,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (MoveWindowIntoZoneByIndexWithNoZones)
|
||||
{
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveWindowIntoZoneByIndexWithInvalidIndex)
|
||||
@@ -338,7 +394,7 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
@@ -355,17 +411,17 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
Assert::IsTrue(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsTrue(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsTrue(zone3->ContainsWindow(window));
|
||||
@@ -382,9 +438,9 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
Assert::IsTrue(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
@@ -401,7 +457,7 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone1);
|
||||
|
||||
auto window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 101, 101 });
|
||||
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 200, 200 });
|
||||
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
}
|
||||
|
||||
@@ -445,7 +445,7 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual(expected, actual);
|
||||
|
||||
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||
Assert::AreNotEqual(-1, actualZoneIndex);
|
||||
}
|
||||
@@ -458,7 +458,7 @@ namespace FancyZonesUnitTests
|
||||
zoneWindow->MoveSizeEnter(window, true);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
|
||||
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -100, -100 });
|
||||
Assert::AreEqual(expected, actual);
|
||||
|
||||
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||
@@ -501,7 +501,7 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual(expected, actual);
|
||||
|
||||
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user