[FancyZones] Fix canvas layout with scaling reset issue (#17186)

* canvas scaling

* predef layout check

* return false on custom layout error

* clean up

* update unit tests

* spelling

* floating point convert

* fix build
This commit is contained in:
Seraphima Zykova
2022-03-25 19:09:59 +03:00
committed by GitHub
parent 403969587e
commit 4aadaf9bf1
16 changed files with 651 additions and 723 deletions

View File

@@ -58,6 +58,7 @@
<ClInclude Include="JsonHelpers.h" />
<ClInclude Include="KeyState.h" />
<ClInclude Include="FancyZonesData\LayoutHotkeys.h" />
<ClInclude Include="LayoutConfigurator.h" />
<ClInclude Include="ModuleConstants.h" />
<ClInclude Include="MonitorUtils.h" />
<ClInclude Include="MonitorWorkAreaHandler.h" />
@@ -109,6 +110,7 @@
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="LayoutConfigurator.cpp" />
<ClCompile Include="MonitorUtils.cpp" />
<ClCompile Include="MonitorWorkAreaHandler.cpp" />
<ClCompile Include="OnThreadExecutor.cpp" />

View File

@@ -126,6 +126,9 @@
<ClInclude Include="WindowUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LayoutConfigurator.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -209,6 +212,9 @@
<ClCompile Include="WindowUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LayoutConfigurator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -0,0 +1,433 @@
#include "pch.h"
#include "LayoutConfigurator.h"
#include <common/display/dpi_aware.h>
#include <common/logger/logger.h>
#include <FancyZonesLib/FancyZonesDataTypes.h>
namespace
{
constexpr int C_MULTIPLIER = 10000;
// PriorityGrid layout is unique for zoneCount <= 11. For zoneCount > 11 PriorityGrid is same as Grid
FancyZonesDataTypes::GridLayoutInfo predefinedPriorityGridLayouts[11] = {
/* 1 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 1,
.rowsPercents = { 10000 },
.columnsPercents = { 10000 },
.cellChildMap = { { 0 } } }),
/* 2 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 2,
.rowsPercents = { 10000 },
.columnsPercents = { 6667, 3333 },
.cellChildMap = { { 0, 1 } } }),
/* 3 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 3,
.rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }),
/* 4 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2,
.columns = 3,
.rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 } } }),
/* 5 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2,
.columns = 3,
.rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 } } }),
/* 6 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 3,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } } }),
/* 7 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 3,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } } }),
/* 8 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } } }),
/* 9 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } } }),
/* 10 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } } }),
/* 11 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 8, 9, 10 } } }),
};
}
bool AddZone(winrt::com_ptr<IZone> zone, ZonesMap& zones) noexcept
{
auto zoneId = zone->Id();
if (zones.contains(zoneId))
{
return false;
}
zones[zoneId] = zone;
return true;
}
ZonesMap CalculateGridZones(FancyZonesUtils::Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing)
{
ZonesMap zones;
long totalWidth = workArea.width();
long totalHeight = workArea.height();
struct Info
{
long Extent;
long Start;
long End;
};
std::vector<Info> rowInfo(gridLayoutInfo.rows());
std::vector<Info> columnInfo(gridLayoutInfo.columns());
// Note: The expressions below are carefully written to
// make the sum of all zones' sizes exactly total{Width|Height}
int totalPercents = 0;
for (int row = 0; row < gridLayoutInfo.rows(); row++)
{
rowInfo[row].Start = totalPercents * totalHeight / C_MULTIPLIER;
totalPercents += gridLayoutInfo.rowsPercents()[row];
rowInfo[row].End = totalPercents * totalHeight / C_MULTIPLIER;
rowInfo[row].Extent = rowInfo[row].End - rowInfo[row].Start;
}
totalPercents = 0;
for (int col = 0; col < gridLayoutInfo.columns(); col++)
{
columnInfo[col].Start = totalPercents * totalWidth / C_MULTIPLIER;
totalPercents += gridLayoutInfo.columnsPercents()[col];
columnInfo[col].End = totalPercents * totalWidth / C_MULTIPLIER;
columnInfo[col].Extent = columnInfo[col].End - columnInfo[col].Start;
}
for (int row = 0; row < gridLayoutInfo.rows(); row++)
{
for (int col = 0; col < gridLayoutInfo.columns(); col++)
{
int i = gridLayoutInfo.cellChildMap()[row][col];
if (((row == 0) || (gridLayoutInfo.cellChildMap()[row - 1][col] != i)) &&
((col == 0) || (gridLayoutInfo.cellChildMap()[row][col - 1] != i)))
{
long left = columnInfo[col].Start;
long top = rowInfo[row].Start;
int maxRow = row;
while (((maxRow + 1) < gridLayoutInfo.rows()) && (gridLayoutInfo.cellChildMap()[maxRow + 1][col] == i))
{
maxRow++;
}
int maxCol = col;
while (((maxCol + 1) < gridLayoutInfo.columns()) && (gridLayoutInfo.cellChildMap()[row][maxCol + 1] == i))
{
maxCol++;
}
long right = columnInfo[maxCol].End;
long bottom = rowInfo[maxRow].End;
top += row == 0 ? spacing : spacing / 2;
bottom -= maxRow == gridLayoutInfo.rows() - 1 ? spacing : spacing / 2;
left += col == 0 ? spacing : spacing / 2;
right -= maxCol == gridLayoutInfo.columns() - 1 ? spacing : spacing / 2;
auto zone = MakeZone(RECT{ left, top, right, bottom }, i);
if (zone)
{
if (!AddZone(zone, zones))
{
Logger::error(L"Failed to create grid layout. Invalid zone id");
return {};
}
}
else
{
// All zones within zone set should be valid in order to use its functionality.
Logger::error(L"Failed to create grid layout. Invalid zone");
return {};
}
}
}
}
return zones;
}
ZonesMap LayoutConfigurator::Focus(FancyZonesUtils::Rect workArea, int zoneCount) noexcept
{
ZonesMap zones;
long left{ 100 };
long top{ 100 };
long right{ left + long(workArea.width() * 0.4) };
long bottom{ top + long(workArea.height() * 0.4) };
RECT focusZoneRect{ left, top, right, bottom };
long focusRectXIncrement = (zoneCount <= 1) ? 0 : 50;
long focusRectYIncrement = (zoneCount <= 1) ? 0 : 50;
for (int i = 0; i < zoneCount; i++)
{
auto zone = MakeZone(focusZoneRect, zones.size());
if (zone)
{
if (!AddZone(zone, zones))
{
Logger::error(L"Failed to create Focus layout. Invalid zone id");
return {};
}
}
else
{
// All zones within zone set should be valid in order to use its functionality.
Logger::error(L"Failed to create Focus layout. Invalid zone");
return {};
}
focusZoneRect.left += focusRectXIncrement;
focusZoneRect.right += focusRectXIncrement;
focusZoneRect.bottom += focusRectYIncrement;
focusZoneRect.top += focusRectYIncrement;
}
return zones;
}
ZonesMap LayoutConfigurator::Rows(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept
{
ZonesMap zones;
long totalWidth = workArea.width() - (spacing * 2);
long totalHeight = workArea.height() - (spacing * (zoneCount + 1));
long top = spacing;
long left = spacing;
long bottom;
long right;
// Note: The expressions below are NOT equal to total{Width|Height} / zoneCount and are done
// like this to make the sum of all zones' sizes exactly total{Width|Height}.
for (int zoneIndex = 0; zoneIndex < zoneCount; ++zoneIndex)
{
right = totalWidth + spacing;
bottom = top + (zoneIndex + 1) * totalHeight / zoneCount - zoneIndex * totalHeight / zoneCount;
auto zone = MakeZone(RECT{ left, top, right, bottom }, zones.size());
if (zone)
{
if (!AddZone(zone, zones))
{
Logger::error(L"Failed to create Rows layout. Invalid zone id");
return {};
}
}
else
{
// All zones within zone set should be valid in order to use its functionality.
Logger::error(L"Failed to create Rows layout. Invalid zone");
return {};
}
top = bottom + spacing;
}
return zones;
}
ZonesMap LayoutConfigurator::Columns(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept
{
ZonesMap zones;
long totalWidth = workArea.width() - (spacing * (zoneCount + 1));
long totalHeight = workArea.height() - (spacing * 2);
long top = spacing;
long left = spacing;
long bottom;
long right;
// Note: The expressions below are NOT equal to total{Width|Height} / zoneCount and are done
// like this to make the sum of all zones' sizes exactly total{Width|Height}.
for (int zoneIndex = 0; zoneIndex < zoneCount; ++zoneIndex)
{
right = left + (zoneIndex + 1) * totalWidth / zoneCount - zoneIndex * totalWidth / zoneCount;
bottom = totalHeight + spacing;
auto zone = MakeZone(RECT{ left, top, right, bottom }, zones.size());
if (zone)
{
if (!AddZone(zone, zones))
{
Logger::error(L"Failed to create Columns layout. Invalid zone id");
return {};
}
}
else
{
// All zones within zone set should be valid in order to use its functionality.
Logger::error(L"Failed to create Columns layout. Invalid zone");
return {};
}
left = right + spacing;
}
return zones;
}
ZonesMap LayoutConfigurator::Grid(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept
{
int rows = 1, columns = 1;
while (zoneCount / rows >= rows)
{
rows++;
}
rows--;
columns = zoneCount / rows;
if (zoneCount % rows == 0)
{
// even grid
}
else
{
columns++;
}
FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Minimal{ .rows = rows, .columns = columns });
// Note: The expressions below are NOT equal to C_MULTIPLIER / {rows|columns} and are done
// like this to make the sum of all percents exactly C_MULTIPLIER
for (int row = 0; row < rows; row++)
{
gridLayoutInfo.rowsPercents()[row] = C_MULTIPLIER * (row + 1) / rows - C_MULTIPLIER * row / rows;
}
for (int col = 0; col < columns; col++)
{
gridLayoutInfo.columnsPercents()[col] = C_MULTIPLIER * (col + 1) / columns - C_MULTIPLIER * col / columns;
}
for (int i = 0; i < rows; ++i)
{
gridLayoutInfo.cellChildMap()[i] = std::vector<int>(columns);
}
int index = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < columns; col++)
{
gridLayoutInfo.cellChildMap()[row][col] = index++;
if (index == zoneCount)
{
index--;
}
}
}
return CalculateGridZones(workArea, gridLayoutInfo, spacing);
}
ZonesMap LayoutConfigurator::PriorityGrid(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept
{
if (zoneCount <= 0)
{
return {};
}
constexpr int predefinedLayoutsCount = sizeof(predefinedPriorityGridLayouts) / sizeof(FancyZonesDataTypes::GridLayoutInfo);
if (zoneCount < predefinedLayoutsCount)
{
return CalculateGridZones(workArea, predefinedPriorityGridLayouts[zoneCount - 1], spacing);
}
return Grid(workArea, zoneCount, spacing);
}
ZonesMap LayoutConfigurator::Custom(FancyZonesUtils::Rect workArea, HMONITOR monitor, const FancyZonesDataTypes::CustomLayoutData& zoneSet, int spacing) noexcept
{
if (zoneSet.type == FancyZonesDataTypes::CustomLayoutType::Canvas && std::holds_alternative<FancyZonesDataTypes::CanvasLayoutInfo>(zoneSet.info))
{
ZonesMap zones;
const auto& zoneSetInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(zoneSet.info);
float width = static_cast<float>(workArea.width());
float height = static_cast<float>(workArea.height());
DPIAware::InverseConvert(monitor, width, height);
for (const auto& zone : zoneSetInfo.zones)
{
float x = static_cast<float>(zone.x) * width / zoneSetInfo.lastWorkAreaWidth;
float y = static_cast<float>(zone.y) * height / zoneSetInfo.lastWorkAreaHeight;
float zoneWidth = static_cast<float>(zone.width) * width / zoneSetInfo.lastWorkAreaWidth;
float zoneHeight = static_cast<float>(zone.height) * height / zoneSetInfo.lastWorkAreaHeight;
DPIAware::Convert(monitor, x, y);
DPIAware::Convert(monitor, zoneWidth, zoneHeight);
auto zone = MakeZone(RECT{ static_cast<long>(x), static_cast<long>(y), static_cast<long>(x + zoneWidth), static_cast<long>(y + zoneHeight) }, zones.size());
if (zone)
{
if (!AddZone(zone, zones))
{
Logger::error(L"Failed to create Custom layout. Invalid zone id");
return {};
}
}
else
{
// All zones within zone set should be valid in order to use its functionality.
Logger::error(L"Failed to create Custom layout. Invalid zone");
return {};
}
}
return zones;
}
else if (zoneSet.type == FancyZonesDataTypes::CustomLayoutType::Grid && std::holds_alternative<FancyZonesDataTypes::GridLayoutInfo>(zoneSet.info))
{
const auto& info = std::get<FancyZonesDataTypes::GridLayoutInfo>(zoneSet.info);
return CalculateGridZones(workArea, info, spacing);
}
return {};
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <FancyZonesLib/Zone.h>
#include <FancyZonesLib/util.h>
// Mapping zone id to zone
using ZonesMap = std::map<ZoneIndex, winrt::com_ptr<IZone>>;
namespace FancyZonesDataTypes
{
struct CustomLayoutData;
}
class LayoutConfigurator
{
public:
static ZonesMap Focus(FancyZonesUtils::Rect workArea, int zoneCount) noexcept;
static ZonesMap Rows(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept;
static ZonesMap Columns(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept;
static ZonesMap Grid(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept;
static ZonesMap PriorityGrid(FancyZonesUtils::Rect workArea, int zoneCount, int spacing) noexcept;
static ZonesMap Custom(FancyZonesUtils::Rect workArea, HMONITOR monitor, const FancyZonesDataTypes::CustomLayoutData& data, int spacing) noexcept;
};

View File

@@ -373,16 +373,16 @@ void FancyZonesWindowUtils::SaveWindowSizeAndOrigin(HWND window) noexcept
RECT rect;
if (GetWindowRect(window, &rect))
{
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
int originX = rect.left;
int originY = rect.top;
float width = static_cast<float>(rect.right - rect.left);
float height = static_cast<float>(rect.bottom - rect.top);
float originX = static_cast<float>(rect.left);
float originY = static_cast<float>(rect.top);
DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), width, height);
DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), originX, originY);
std::array<int, 2> windowSizeData = { width, height };
std::array<int, 2> windowOriginData = { originX, originY };
std::array<int, 2> windowSizeData = { static_cast<int>(width), static_cast<int>(height) };
std::array<int, 2> windowOriginData = { static_cast<int>(originX), static_cast<int>(originY) };
HANDLE rawData;
memcpy(&rawData, windowSizeData.data(), sizeof rawData);
SetPropW(window, ZonedWindowProperties::PropertyRestoreSizeID, rawData);
@@ -399,8 +399,10 @@ void FancyZonesWindowUtils::RestoreWindowSize(HWND window) noexcept
std::array<int, 2> windowSize;
memcpy(windowSize.data(), &windowSizeData, sizeof windowSize);
float windowWidth = static_cast<float>(windowSize[0]), windowHeight = static_cast<float>(windowSize[1]);
// {width, height}
DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowSize[0], windowSize[1]);
DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowWidth, windowHeight);
RECT rect;
if (GetWindowRect(window, &rect))
@@ -428,8 +430,10 @@ void FancyZonesWindowUtils::RestoreWindowOrigin(HWND window) noexcept
std::array<int, 2> windowOrigin;
memcpy(windowOrigin.data(), &windowOriginData, sizeof windowOrigin);
float windowWidth = static_cast<float>(windowOrigin[0]), windowHeight = static_cast<float>(windowOrigin[1]);
// {width, height}
DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowOrigin[0], windowOrigin[1]);
DPIAware::Convert(MonitorFromWindow(window, MONITOR_DEFAULTTONULL), windowWidth, windowHeight);
RECT rect;
if (GetWindowRect(window, &rect))

View File

@@ -3,108 +3,17 @@
#include "ZoneSet.h"
#include <FancyZonesLib/FancyZonesData/CustomLayouts.h>
#include "FancyZonesDataTypes.h"
#include "FancyZonesWindowProperties.h"
#include "Settings.h"
#include "Zone.h"
#include <FancyZonesLib/util.h>
#include <FancyZonesLib/FancyZonesWindowProperties.h>
#include <FancyZonesLib/WindowUtils.h>
#include <common/logger/logger.h>
#include <common/display/dpi_aware.h>
#include <common/utils/winapi_error.h>
#include <limits>
#include <map>
#include <utility>
using namespace FancyZonesUtils;
namespace
{
constexpr int C_MULTIPLIER = 10000;
constexpr int OVERLAPPING_CENTERS_SENSITIVITY = 75;
// PriorityGrid layout is unique for zoneCount <= 11. For zoneCount > 11 PriorityGrid is same as Grid
FancyZonesDataTypes::GridLayoutInfo predefinedPriorityGridLayouts[11] = {
/* 1 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 1,
.rowsPercents = { 10000 },
.columnsPercents = { 10000 },
.cellChildMap = { { 0 } } }),
/* 2 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 2,
.rowsPercents = { 10000 },
.columnsPercents = { 6667, 3333 },
.cellChildMap = { { 0, 1 } } }),
/* 3 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 1,
.columns = 3,
.rowsPercents = { 10000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 } } }),
/* 4 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2,
.columns = 3,
.rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 } } }),
/* 5 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 2,
.columns = 3,
.rowsPercents = { 5000, 5000 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 } } }),
/* 6 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 3,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 0, 1, 3 }, { 4, 1, 5 } } }),
/* 7 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 3,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 5000, 2500 },
.cellChildMap = { { 0, 1, 2 }, { 3, 1, 4 }, { 5, 1, 6 } } }),
/* 8 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 2, 7 } } }),
/* 9 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 2, 5 }, { 6, 1, 7, 8 } } }),
/* 10 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 1, 8, 9 } } }),
/* 11 */
FancyZonesDataTypes::GridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Full{
.rows = 3,
.columns = 4,
.rowsPercents = { 3333, 3334, 3333 },
.columnsPercents = { 2500, 2500, 2500, 2500 },
.cellChildMap = { { 0, 1, 2, 3 }, { 4, 1, 5, 6 }, { 7, 8, 9, 10 } } }),
};
}
struct ZoneSet : winrt::implements<ZoneSet, IZoneSet>
@@ -125,7 +34,6 @@ public:
Id() const noexcept { return m_config.Id; }
IFACEMETHODIMP_(FancyZonesDataTypes::ZoneSetLayoutType)
LayoutType() const noexcept { return m_config.LayoutType; }
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
IFACEMETHODIMP_(ZoneIndexSet) ZonesFromPoint(POINT pt) const noexcept;
IFACEMETHODIMP_(ZoneIndexSet) GetZoneIndexSetFromWindow(HWND window) const noexcept;
IFACEMETHODIMP_(ZonesMap) GetZones()const noexcept override { return m_zones; }
@@ -151,12 +59,6 @@ public:
IFACEMETHODIMP_(ZoneIndexSet) GetCombinedZoneRange(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const noexcept;
private:
bool CalculateFocusLayout(Rect workArea, int zoneCount) noexcept;
bool CalculateColumnsAndRowsLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
bool CalculateGridLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept;
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);
HWND GetNextTab(ZoneIndexSet indexSet, HWND current, bool reverse) noexcept;
void InsertTabIntoZone(HWND window, std::optional<size_t> tabSortKeyWithinZone, const ZoneIndexSet& indexSet);
ZoneIndexSet ZoneSelectSubregion(const ZoneIndexSet& capturedZones, POINT pt) const;
@@ -178,18 +80,6 @@ private:
ZoneSetConfig m_config;
};
IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
{
auto zoneId = zone->Id();
if (m_zones.contains(zoneId))
{
return S_FALSE;
}
m_zones[zoneId] = zone;
return S_OK;
}
IFACEMETHODIMP_(ZoneIndexSet)
ZoneSet::ZonesFromPoint(POINT pt) const noexcept
{
@@ -691,26 +581,40 @@ ZoneSet::CalculateZones(RECT workAreaRect, int zoneCount, int spacing) noexcept
return false;
}
bool success = true;
switch (m_config.LayoutType)
{
case FancyZonesDataTypes::ZoneSetLayoutType::Focus:
success = CalculateFocusLayout(workArea, zoneCount);
m_zones = LayoutConfigurator::Focus(workArea, zoneCount);
break;
case FancyZonesDataTypes::ZoneSetLayoutType::Columns:
m_zones = LayoutConfigurator::Columns(workArea, zoneCount, spacing);
break;
case FancyZonesDataTypes::ZoneSetLayoutType::Rows:
success = CalculateColumnsAndRowsLayout(workArea, m_config.LayoutType, zoneCount, spacing);
m_zones = LayoutConfigurator::Rows(workArea, zoneCount, spacing);
break;
case FancyZonesDataTypes::ZoneSetLayoutType::Grid:
m_zones = LayoutConfigurator::Grid(workArea, zoneCount, spacing);
break;
case FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid:
success = CalculateGridLayout(workArea, m_config.LayoutType, zoneCount, spacing);
m_zones = LayoutConfigurator::PriorityGrid(workArea, zoneCount, spacing);
break;
case FancyZonesDataTypes::ZoneSetLayoutType::Custom:
success = CalculateCustomLayout(workArea, spacing);
break;
{
const auto zoneSetSearchResult = CustomLayouts::instance().GetCustomLayoutData(m_config.Id);
if (zoneSetSearchResult.has_value())
{
m_zones = LayoutConfigurator::Custom(workArea, m_config.Monitor, zoneSetSearchResult.value(), spacing);
}
else
{
Logger::error(L"Custom layout not found");
return false;
}
}
break;
}
return success;
return m_zones.size() == zoneCount;
}
bool ZoneSet::IsZoneEmpty(ZoneIndex zoneIndex) const noexcept
@@ -726,296 +630,6 @@ bool ZoneSet::IsZoneEmpty(ZoneIndex zoneIndex) const noexcept
return true;
}
bool ZoneSet::CalculateFocusLayout(Rect workArea, int zoneCount) noexcept
{
long left{ 100 };
long top{ 100 };
long right{ left + long(workArea.width() * 0.4) };
long bottom{ top + long(workArea.height() * 0.4) };
RECT focusZoneRect{ left, top, right, bottom };
long focusRectXIncrement = (zoneCount <= 1) ? 0 : 50;
long focusRectYIncrement = (zoneCount <= 1) ? 0 : 50;
for (int i = 0; i < zoneCount; i++)
{
auto zone = MakeZone(focusZoneRect, m_zones.size());
if (zone)
{
AddZone(zone);
}
else
{
// All zones within zone set should be valid in order to use its functionality.
m_zones.clear();
return false;
}
focusZoneRect.left += focusRectXIncrement;
focusZoneRect.right += focusRectXIncrement;
focusZoneRect.bottom += focusRectYIncrement;
focusZoneRect.top += focusRectYIncrement;
}
return true;
}
bool ZoneSet::CalculateColumnsAndRowsLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept
{
long totalWidth;
long totalHeight;
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{
totalWidth = workArea.width() - (spacing * (zoneCount + 1));
totalHeight = workArea.height() - (spacing * 2);
}
else
{ //Rows
totalWidth = workArea.width() - (spacing * 2);
totalHeight = workArea.height() - (spacing * (zoneCount + 1));
}
long top = spacing;
long left = spacing;
long bottom;
long right;
// Note: The expressions below are NOT equal to total{Width|Height} / zoneCount and are done
// like this to make the sum of all zones' sizes exactly total{Width|Height}.
for (int zoneIndex = 0; zoneIndex < zoneCount; ++zoneIndex)
{
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{
right = left + (zoneIndex + 1) * totalWidth / zoneCount - zoneIndex * totalWidth / zoneCount;
bottom = totalHeight + spacing;
}
else
{ //Rows
right = totalWidth + spacing;
bottom = top + (zoneIndex + 1) * totalHeight / zoneCount - zoneIndex * totalHeight / zoneCount;
}
auto zone = MakeZone(RECT{ left, top, right, bottom }, m_zones.size());
if (zone)
{
AddZone(zone);
}
else
{
// All zones within zone set should be valid in order to use its functionality.
m_zones.clear();
return false;
}
if (type == FancyZonesDataTypes::ZoneSetLayoutType::Columns)
{
left = right + spacing;
}
else
{ //Rows
top = bottom + spacing;
}
}
return true;
}
bool ZoneSet::CalculateGridLayout(Rect workArea, FancyZonesDataTypes::ZoneSetLayoutType type, int zoneCount, int spacing) noexcept
{
const auto count = sizeof(predefinedPriorityGridLayouts) / sizeof(FancyZonesDataTypes::GridLayoutInfo);
if (type == FancyZonesDataTypes::ZoneSetLayoutType::PriorityGrid && zoneCount < count)
{
return CalculateUniquePriorityGridLayout(workArea, zoneCount, spacing);
}
int rows = 1, columns = 1;
while (zoneCount / rows >= rows)
{
rows++;
}
rows--;
columns = zoneCount / rows;
if (zoneCount % rows == 0)
{
// even grid
}
else
{
columns++;
}
FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo(FancyZonesDataTypes::GridLayoutInfo::Minimal{ .rows = rows, .columns = columns });
// Note: The expressions below are NOT equal to C_MULTIPLIER / {rows|columns} and are done
// like this to make the sum of all percents exactly C_MULTIPLIER
for (int row = 0; row < rows; row++)
{
gridLayoutInfo.rowsPercents()[row] = C_MULTIPLIER * (row + 1) / rows - C_MULTIPLIER * row / rows;
}
for (int col = 0; col < columns; col++)
{
gridLayoutInfo.columnsPercents()[col] = C_MULTIPLIER * (col + 1) / columns - C_MULTIPLIER * col / columns;
}
for (int i = 0; i < rows; ++i)
{
gridLayoutInfo.cellChildMap()[i] = std::vector<int>(columns);
}
int index = 0;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < columns; col++)
{
gridLayoutInfo.cellChildMap()[row][col] = index++;
if (index == zoneCount)
{
index--;
}
}
}
return CalculateGridZones(workArea, gridLayoutInfo, spacing);
}
bool ZoneSet::CalculateUniquePriorityGridLayout(Rect workArea, int zoneCount, int spacing) noexcept
{
if (zoneCount <= 0 || zoneCount >= sizeof(predefinedPriorityGridLayouts))
{
return false;
}
return CalculateGridZones(workArea, predefinedPriorityGridLayouts[zoneCount - 1], spacing);
}
bool ZoneSet::CalculateCustomLayout(Rect workArea, int spacing) noexcept
{
const auto zoneSetSearchResult = CustomLayouts::instance().GetCustomLayoutData(m_config.Id);
if (!zoneSetSearchResult.has_value())
{
return false;
}
const auto& zoneSet = *zoneSetSearchResult;
if (zoneSet.type == FancyZonesDataTypes::CustomLayoutType::Canvas && std::holds_alternative<FancyZonesDataTypes::CanvasLayoutInfo>(zoneSet.info))
{
const auto& zoneSetInfo = std::get<FancyZonesDataTypes::CanvasLayoutInfo>(zoneSet.info);
for (const auto& zone : zoneSetInfo.zones)
{
int x = zone.x;
int y = zone.y;
int width = zone.width;
int height = zone.height;
DPIAware::Convert(m_config.Monitor, x, y);
DPIAware::Convert(m_config.Monitor, width, height);
auto zone = MakeZone(RECT{ x, y, x + width, y + height }, m_zones.size());
if (zone)
{
AddZone(zone);
}
else
{
// All zones within zone set should be valid in order to use its functionality.
m_zones.clear();
return false;
}
}
return true;
}
else if (zoneSet.type == FancyZonesDataTypes::CustomLayoutType::Grid && std::holds_alternative<FancyZonesDataTypes::GridLayoutInfo>(zoneSet.info))
{
const auto& info = std::get<FancyZonesDataTypes::GridLayoutInfo>(zoneSet.info);
return CalculateGridZones(workArea, info, spacing);
}
return false;
}
bool ZoneSet::CalculateGridZones(Rect workArea, FancyZonesDataTypes::GridLayoutInfo gridLayoutInfo, int spacing)
{
long totalWidth = workArea.width();
long totalHeight = workArea.height();
struct Info
{
long Extent;
long Start;
long End;
};
std::vector<Info> rowInfo(gridLayoutInfo.rows());
std::vector<Info> columnInfo(gridLayoutInfo.columns());
// Note: The expressions below are carefully written to
// make the sum of all zones' sizes exactly total{Width|Height}
int totalPercents = 0;
for (int row = 0; row < gridLayoutInfo.rows(); row++)
{
rowInfo[row].Start = totalPercents * totalHeight / C_MULTIPLIER;
totalPercents += gridLayoutInfo.rowsPercents()[row];
rowInfo[row].End = totalPercents * totalHeight / C_MULTIPLIER;
rowInfo[row].Extent = rowInfo[row].End - rowInfo[row].Start;
}
totalPercents = 0;
for (int col = 0; col < gridLayoutInfo.columns(); col++)
{
columnInfo[col].Start = totalPercents * totalWidth / C_MULTIPLIER;
totalPercents += gridLayoutInfo.columnsPercents()[col];
columnInfo[col].End = totalPercents * totalWidth / C_MULTIPLIER;
columnInfo[col].Extent = columnInfo[col].End - columnInfo[col].Start;
}
for (int row = 0; row < gridLayoutInfo.rows(); row++)
{
for (int col = 0; col < gridLayoutInfo.columns(); col++)
{
int i = gridLayoutInfo.cellChildMap()[row][col];
if (((row == 0) || (gridLayoutInfo.cellChildMap()[row - 1][col] != i)) &&
((col == 0) || (gridLayoutInfo.cellChildMap()[row][col - 1] != i)))
{
long left = columnInfo[col].Start;
long top = rowInfo[row].Start;
int maxRow = row;
while (((maxRow + 1) < gridLayoutInfo.rows()) && (gridLayoutInfo.cellChildMap()[maxRow + 1][col] == i))
{
maxRow++;
}
int maxCol = col;
while (((maxCol + 1) < gridLayoutInfo.columns()) && (gridLayoutInfo.cellChildMap()[row][maxCol + 1] == i))
{
maxCol++;
}
long right = columnInfo[maxCol].End;
long bottom = rowInfo[maxRow].End;
top += row == 0 ? spacing : spacing / 2;
bottom -= maxRow == gridLayoutInfo.rows() - 1 ? spacing : spacing / 2;
left += col == 0 ? spacing : spacing / 2;
right -= maxCol == gridLayoutInfo.columns() - 1 ? spacing : spacing / 2;
auto zone = MakeZone(RECT{ left, top, right, bottom }, i);
if (zone)
{
AddZone(zone);
}
else
{
// All zones within zone set should be valid in order to use its functionality.
m_zones.clear();
return false;
}
}
}
}
return true;
}
ZoneIndexSet ZoneSet::GetCombinedZoneRange(const ZoneIndexSet& initialZones, const ZoneIndexSet& finalZones) const noexcept
{
ZoneIndexSet combinedZones, result;

View File

@@ -1,6 +1,6 @@
#pragma once
#include "Zone.h"
#include <FancyZonesLib/LayoutConfigurator.h>
#include "Settings.h"
namespace FancyZonesDataTypes
@@ -13,9 +13,6 @@ namespace FancyZonesDataTypes
*/
interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet : public IUnknown
{
// Mapping zone id to zone
using ZonesMap = std::map<ZoneIndex, winrt::com_ptr<IZone>>;
/**
* @returns Unique identifier of zone layout.
*/
@@ -24,12 +21,6 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
* @returns Type of the zone layout. Layout type can be focus, columns, rows, grid, priority grid or custom.
*/
IFACEMETHOD_(FancyZonesDataTypes::ZoneSetLayoutType, LayoutType)() const = 0;
/**
* Add zone to the zone layout.
*
* @param zone Zone object (defining coordinates of the zone).
*/
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
/**
* Get zones from cursor coordinates.
*

View File

@@ -277,7 +277,7 @@ void ZonesOverlay::Flash()
m_cv.notify_all();
}
void ZonesOverlay::DrawActiveZoneSet(const IZoneSet::ZonesMap& zones,
void ZonesOverlay::DrawActiveZoneSet(const ZonesMap& zones,
const ZoneIndexSet& highlightZones,
const Colors::ZoneColors& colors,
const bool showZoneText)

View File

@@ -66,7 +66,7 @@ public:
void Hide();
void Show();
void Flash();
void DrawActiveZoneSet(const IZoneSet::ZonesMap& zones,
void DrawActiveZoneSet(const ZonesMap& zones,
const ZoneIndexSet& highlightZones,
const Colors::ZoneColors& colors,
const bool showZoneText);