mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 19:57:57 +01:00
[FancyZones] Improve zone selection (#9447)
* Improve zone selection algorithm
* Thanks, spell checker
* Fix failing test case
* Add error logging
* Added support for different zone selection algorithms
* Revert "Fix failing test case"
This reverts commit 9f31a8a7e6.
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include "Zone.h"
|
#include "Zone.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <common/logger/logger.h>
|
||||||
#include <common/display/dpi_aware.h>
|
#include <common/display/dpi_aware.h>
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -158,6 +159,11 @@ private:
|
|||||||
bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept;
|
bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept;
|
||||||
bool CalculateCustomLayout(Rect workArea, int spacing) noexcept;
|
bool CalculateCustomLayout(Rect workArea, int spacing) noexcept;
|
||||||
bool CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing);
|
bool CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing);
|
||||||
|
std::vector<size_t> ZoneSelectSubregion(const std::vector<size_t>& capturedZones, POINT pt) const;
|
||||||
|
|
||||||
|
// `compare` should return true if the first argument is a better choice than the second argument.
|
||||||
|
template<class CompareF>
|
||||||
|
std::vector<size_t> ZoneSelectPriority(const std::vector<size_t>& capturedZones, CompareF compare) const;
|
||||||
|
|
||||||
ZonesMap m_zones;
|
ZonesMap m_zones;
|
||||||
std::map<HWND, std::vector<size_t>> m_windowIndexSet;
|
std::map<HWND, std::vector<size_t>> m_windowIndexSet;
|
||||||
@@ -211,7 +217,7 @@ ZoneSet::ZonesFromPoint(POINT pt) const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If captured zones do not overlap, return all of them
|
// If captured zones do not overlap, return all of them
|
||||||
// Otherwise, return the smallest one
|
// Otherwise, return one of them based on the chosen selection algorithm.
|
||||||
bool overlap = false;
|
bool overlap = false;
|
||||||
for (size_t i = 0; i < capturedZones.size(); ++i)
|
for (size_t i = 0; i < capturedZones.size(); ++i)
|
||||||
{
|
{
|
||||||
@@ -244,30 +250,28 @@ ZoneSet::ZonesFromPoint(POINT pt) const noexcept
|
|||||||
|
|
||||||
if (overlap)
|
if (overlap)
|
||||||
{
|
{
|
||||||
size_t smallestIdx = 0;
|
auto zoneArea = [](auto zone) {
|
||||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
RECT rect = zone->GetZoneRect();
|
||||||
{
|
return max(rect.bottom - rect.top, 0) * max(rect.right - rect.left, 0);
|
||||||
RECT rectS;
|
};
|
||||||
RECT rectI;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rectS = m_zones.at(capturedZones[smallestIdx])->GetZoneRect();
|
switch (m_config.SelectionAlgorithm)
|
||||||
rectI = m_zones.at(capturedZones[i])->GetZoneRect();
|
{
|
||||||
|
case ZoneSelectionAlgorithm::SUBREGION:
|
||||||
|
return ZoneSelectSubregion(capturedZones, pt);
|
||||||
|
case ZoneSelectionAlgorithm::SMALLEST:
|
||||||
|
return ZoneSelectPriority(capturedZones, [&](auto zone1, auto zone2) { return zoneArea(zone1) < zoneArea(zone2); });
|
||||||
|
case ZoneSelectionAlgorithm::LARGEST:
|
||||||
|
return ZoneSelectPriority(capturedZones, [&](auto zone1, auto zone2) { return zoneArea(zone1) > zoneArea(zone2); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (std::out_of_range)
|
catch (std::out_of_range)
|
||||||
{
|
{
|
||||||
return {};
|
Logger::error("Exception out_of_range was thrown in ZoneSet::ZonesFromPoint");
|
||||||
|
return { capturedZones[0] };
|
||||||
}
|
}
|
||||||
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;
|
return capturedZones;
|
||||||
@@ -937,6 +941,67 @@ std::vector<size_t> ZoneSet::GetCombinedZoneRange(const std::vector<size_t>& ini
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> ZoneSet::ZoneSelectSubregion(const std::vector<size_t>& capturedZones, POINT pt) const
|
||||||
|
{
|
||||||
|
auto expand = [&](RECT& rect) {
|
||||||
|
rect.top -= m_config.SensitivityRadius / 2;
|
||||||
|
rect.bottom += m_config.SensitivityRadius / 2;
|
||||||
|
rect.left -= m_config.SensitivityRadius / 2;
|
||||||
|
rect.right += m_config.SensitivityRadius / 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute the overlapped rectangle.
|
||||||
|
RECT overlap = m_zones.at(capturedZones[0])->GetZoneRect();
|
||||||
|
expand(overlap);
|
||||||
|
|
||||||
|
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||||
|
{
|
||||||
|
RECT current = m_zones.at(capturedZones[i])->GetZoneRect();
|
||||||
|
expand(current);
|
||||||
|
|
||||||
|
overlap.top = max(overlap.top, current.top);
|
||||||
|
overlap.left = max(overlap.left, current.left);
|
||||||
|
overlap.bottom = min(overlap.bottom, current.bottom);
|
||||||
|
overlap.right = min(overlap.right, current.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid division by zero
|
||||||
|
int width = max(overlap.right - overlap.left, 1);
|
||||||
|
int height = max(overlap.bottom - overlap.top, 1);
|
||||||
|
|
||||||
|
bool verticalSplit = height > width;
|
||||||
|
size_t zoneIndex;
|
||||||
|
|
||||||
|
if (verticalSplit)
|
||||||
|
{
|
||||||
|
zoneIndex = (pt.y - overlap.top) * capturedZones.size() / height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zoneIndex = (pt.x - overlap.left) * capturedZones.size() / width;
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneIndex = std::clamp(zoneIndex, size_t(0), capturedZones.size() - 1);
|
||||||
|
|
||||||
|
return { capturedZones[zoneIndex] };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CompareF>
|
||||||
|
std::vector<size_t> ZoneSet::ZoneSelectPriority(const std::vector<size_t>& capturedZones, CompareF compare) const
|
||||||
|
{
|
||||||
|
size_t chosen = 0;
|
||||||
|
|
||||||
|
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||||
|
{
|
||||||
|
if (compare(m_zones.at(capturedZones[i]), m_zones.at(capturedZones[chosen])))
|
||||||
|
{
|
||||||
|
chosen = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { capturedZones[chosen] };
|
||||||
|
}
|
||||||
|
|
||||||
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept
|
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept
|
||||||
{
|
{
|
||||||
return winrt::make_self<ZoneSet>(config);
|
return winrt::make_self<ZoneSet>(config);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Zone.h"
|
#include "Zone.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
namespace FancyZonesDataTypes
|
namespace FancyZonesDataTypes
|
||||||
{
|
{
|
||||||
@@ -151,6 +152,13 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
|||||||
IFACEMETHOD_(std::vector<size_t>, GetCombinedZoneRange)(const std::vector<size_t>& initialZones, const std::vector<size_t>& finalZones) const = 0;
|
IFACEMETHOD_(std::vector<size_t>, GetCombinedZoneRange)(const std::vector<size_t>& initialZones, const std::vector<size_t>& finalZones) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum struct ZoneSelectionAlgorithm
|
||||||
|
{
|
||||||
|
SMALLEST = 0,
|
||||||
|
LARGEST = 1,
|
||||||
|
SUBREGION = 2,
|
||||||
|
};
|
||||||
|
|
||||||
struct ZoneSetConfig
|
struct ZoneSetConfig
|
||||||
{
|
{
|
||||||
ZoneSetConfig(
|
ZoneSetConfig(
|
||||||
@@ -169,6 +177,7 @@ struct ZoneSetConfig
|
|||||||
FancyZonesDataTypes::ZoneSetLayoutType LayoutType{};
|
FancyZonesDataTypes::ZoneSetLayoutType LayoutType{};
|
||||||
HMONITOR Monitor{};
|
HMONITOR Monitor{};
|
||||||
int SensitivityRadius;
|
int SensitivityRadius;
|
||||||
|
ZoneSelectionAlgorithm SelectionAlgorithm = ZoneSelectionAlgorithm::SMALLEST;
|
||||||
};
|
};
|
||||||
|
|
||||||
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept;
|
winrt::com_ptr<IZoneSet> MakeZoneSet(ZoneSetConfig const& config) noexcept;
|
||||||
|
|||||||
Reference in New Issue
Block a user