diff --git a/src/modules/fancyzones/lib/ZoneSet.cpp b/src/modules/fancyzones/lib/ZoneSet.cpp index e70336c382..1a2c40ced6 100644 --- a/src/modules/fancyzones/lib/ZoneSet.cpp +++ b/src/modules/fancyzones/lib/ZoneSet.cpp @@ -8,6 +8,7 @@ #include "Zone.h" #include "util.h" +#include #include #include @@ -158,6 +159,11 @@ private: bool CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept; bool CalculateCustomLayout(Rect workArea, int spacing) noexcept; bool CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing); + std::vector ZoneSelectSubregion(const std::vector& capturedZones, POINT pt) const; + + // `compare` should return true if the first argument is a better choice than the second argument. + template + std::vector ZoneSelectPriority(const std::vector& capturedZones, CompareF compare) const; ZonesMap m_zones; std::map> m_windowIndexSet; @@ -211,7 +217,7 @@ ZoneSet::ZonesFromPoint(POINT pt) const noexcept } // 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; for (size_t i = 0; i < capturedZones.size(); ++i) { @@ -244,30 +250,28 @@ ZoneSet::ZonesFromPoint(POINT pt) const noexcept if (overlap) { - size_t smallestIdx = 0; - for (size_t i = 1; i < capturedZones.size(); ++i) - { - RECT rectS; - RECT rectI; - try - { - rectS = m_zones.at(capturedZones[smallestIdx])->GetZoneRect(); - rectI = m_zones.at(capturedZones[i])->GetZoneRect(); - } - catch (std::out_of_range) - { - return {}; - } - int smallestSize = (rectS.bottom - rectS.top) * (rectS.right - rectS.left); - int iSize = (rectI.bottom - rectI.top) * (rectI.right - rectI.left); + auto zoneArea = [](auto zone) { + RECT rect = zone->GetZoneRect(); + return max(rect.bottom - rect.top, 0) * max(rect.right - rect.left, 0); + }; - if (iSize <= smallestSize) + try + { + switch (m_config.SelectionAlgorithm) { - smallestIdx = i; + 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); }); } } - - capturedZones = { capturedZones[smallestIdx] }; + catch (std::out_of_range) + { + Logger::error("Exception out_of_range was thrown in ZoneSet::ZonesFromPoint"); + return { capturedZones[0] }; + } } return capturedZones; @@ -937,6 +941,67 @@ std::vector ZoneSet::GetCombinedZoneRange(const std::vector& ini return result; } +std::vector ZoneSet::ZoneSelectSubregion(const std::vector& 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 +std::vector ZoneSet::ZoneSelectPriority(const std::vector& 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 MakeZoneSet(ZoneSetConfig const& config) noexcept { return winrt::make_self(config); diff --git a/src/modules/fancyzones/lib/ZoneSet.h b/src/modules/fancyzones/lib/ZoneSet.h index d7bdf6b3fd..6d78cfca35 100644 --- a/src/modules/fancyzones/lib/ZoneSet.h +++ b/src/modules/fancyzones/lib/ZoneSet.h @@ -1,6 +1,7 @@ #pragma once #include "Zone.h" +#include "Settings.h" namespace FancyZonesDataTypes { @@ -151,6 +152,13 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet : IFACEMETHOD_(std::vector, GetCombinedZoneRange)(const std::vector& initialZones, const std::vector& finalZones) const = 0; }; +enum struct ZoneSelectionAlgorithm +{ + SMALLEST = 0, + LARGEST = 1, + SUBREGION = 2, +}; + struct ZoneSetConfig { ZoneSetConfig( @@ -169,6 +177,7 @@ struct ZoneSetConfig FancyZonesDataTypes::ZoneSetLayoutType LayoutType{}; HMONITOR Monitor{}; int SensitivityRadius; + ZoneSelectionAlgorithm SelectionAlgorithm = ZoneSelectionAlgorithm::SMALLEST; }; winrt::com_ptr MakeZoneSet(ZoneSetConfig const& config) noexcept;