[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:
Ivan Stošić
2021-02-08 10:48:11 +01:00
committed by GitHub
parent 1899d017dc
commit 47e288da4c
2 changed files with 95 additions and 21 deletions

View File

@@ -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);

View File

@@ -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;