summaryrefslogtreecommitdiff
path: root/src/layout/DwindleLayout.cpp
diff options
context:
space:
mode:
authorcrossatko <3613973+crossatko@users.noreply.github.com>2025-10-24 20:01:05 +0200
committerGitHub <noreply@github.com>2025-10-24 19:01:05 +0100
commit151b5f69783b111831e8c5dc062904a221799d7b (patch)
tree5fae6edc3f84427673c366b502e3246e7a102feb /src/layout/DwindleLayout.cpp
parentaa5a239ac92a6bd6947cce2ca3911606df392cb6 (diff)
dwindle: rework split logic to be fully gap-aware (#12047)
Diffstat (limited to 'src/layout/DwindleLayout.cpp')
-rw-r--r--src/layout/DwindleLayout.cpp105
1 files changed, 79 insertions, 26 deletions
diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp
index f5f545b1..4856c355 100644
--- a/src/layout/DwindleLayout.cpp
+++ b/src/layout/DwindleLayout.cpp
@@ -8,14 +8,51 @@
#include "../managers/LayoutManager.hpp"
#include "../managers/EventManager.hpp"
+SWorkspaceGaps CHyprDwindleLayout::getWorkspaceGaps(const PHLWORKSPACE& pWorkspace) {
+ const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(pWorkspace);
+ static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
+ static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
+ auto* const PGAPSIN = sc<CCssGapData*>((PGAPSINDATA.ptr())->getData());
+ auto* const PGAPSOUT = sc<CCssGapData*>((PGAPSOUTDATA.ptr())->getData());
+
+ SWorkspaceGaps gaps;
+ gaps.in = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
+ gaps.out = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
+ return gaps;
+}
+
+SNodeDisplayEdgeFlags CHyprDwindleLayout::getNodeDisplayEdgeFlags(const CBox& box, const PHLMONITOR& monitor) {
+ return {
+ .top = STICKS(box.y, monitor->m_position.y + monitor->m_reservedTopLeft.y),
+ .bottom = STICKS(box.y + box.h, monitor->m_position.y + monitor->m_size.y - monitor->m_reservedBottomRight.y),
+ .left = STICKS(box.x, monitor->m_position.x + monitor->m_reservedTopLeft.x),
+ .right = STICKS(box.x + box.w, monitor->m_position.x + monitor->m_size.x - monitor->m_reservedBottomRight.x),
+ };
+}
+
void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverride, bool verticalOverride) {
if (children[0]) {
static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split");
static auto PPRESERVESPLIT = CConfigValue<Hyprlang::INT>("dwindle:preserve_split");
static auto PFLMULT = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
+ const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceID);
+ if (!PWORKSPACE)
+ return;
+
+ const auto PMONITOR = PWORKSPACE->m_monitor.lock();
+ if (!PMONITOR)
+ return;
+
+ const auto edges = layout->getNodeDisplayEdgeFlags(box, PMONITOR);
+ auto [gapsIn, gapsOut] = layout->getWorkspaceGaps(PWORKSPACE);
+
+ const Vector2D availableSize = box.size() -
+ Vector2D{(edges.left ? gapsOut.m_left : gapsIn.m_left / 2.f) + (edges.right ? gapsOut.m_right : gapsIn.m_right / 2.f),
+ (edges.top ? gapsOut.m_top : gapsIn.m_top / 2.f) + (edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom / 2.f)};
+
if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0)
- splitTop = box.h * *PFLMULT > box.w;
+ splitTop = availableSize.y * *PFLMULT > availableSize.x;
if (verticalOverride)
splitTop = true;
@@ -26,14 +63,28 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverrid
if (SPLITSIDE) {
// split left/right
- const float FIRSTSIZE = box.w / 2.0 * splitRatio;
- children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize();
- children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize();
+ const float gapsAppliedToChild1 = (edges.left ? gapsOut.m_left : gapsIn.m_left / 2.f) + gapsIn.m_right / 2.f;
+ const float gapsAppliedToChild2 = gapsIn.m_left / 2.f + (edges.right ? gapsOut.m_right : gapsIn.m_right / 2.f);
+ const float totalGaps = gapsAppliedToChild1 + gapsAppliedToChild2;
+ const float totalAvailable = box.w - totalGaps;
+
+ const float child1Available = totalAvailable * (splitRatio / 2.f);
+ const float FIRSTSIZE = child1Available + gapsAppliedToChild1;
+
+ children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize();
+ children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize();
} else {
// split top/bottom
- const float FIRSTSIZE = box.h / 2.0 * splitRatio;
- children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize();
- children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize();
+ const float gapsAppliedToChild1 = (edges.top ? gapsOut.m_top : gapsIn.m_top / 2.f) + gapsIn.m_bottom / 2.f;
+ const float gapsAppliedToChild2 = gapsIn.m_top / 2.f + (edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom / 2.f);
+ const float totalGaps = gapsAppliedToChild1 + gapsAppliedToChild2;
+ const float totalAvailable = box.h - totalGaps;
+
+ const float child1Available = totalAvailable * (splitRatio / 2.f);
+ const float FIRSTSIZE = child1Available + gapsAppliedToChild1;
+
+ children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize();
+ children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize();
}
children[0]->recalcSizePosRecursive(force);
@@ -115,10 +166,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
}
// for gaps outer
- const bool DISPLAYLEFT = STICKS(pNode->box.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
- const bool DISPLAYRIGHT = STICKS(pNode->box.x + pNode->box.w, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
- const bool DISPLAYTOP = STICKS(pNode->box.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
- const bool DISPLAYBOTTOM = STICKS(pNode->box.y + pNode->box.h, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
+ const auto edges = getNodeDisplayEdgeFlags(pNode->box, PMONITOR);
const auto PWINDOW = pNode->pWindow.lock();
// get specific gaps and rules for this workspace,
@@ -179,9 +227,9 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
}
}
- const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(DISPLAYLEFT ? gapsOut.m_left : gapsIn.m_left), sc<double>(DISPLAYTOP ? gapsOut.m_top : gapsIn.m_top));
+ const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(edges.left ? gapsOut.m_left : gapsIn.m_left), sc<double>(edges.top ? gapsOut.m_top : gapsIn.m_top));
- const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(DISPLAYRIGHT ? gapsOut.m_right : gapsIn.m_right), sc<double>(DISPLAYBOTTOM ? gapsOut.m_bottom : gapsIn.m_bottom));
+ const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(edges.right ? gapsOut.m_right : gapsIn.m_right), sc<double>(edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom));
calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2;
calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding;
@@ -349,7 +397,6 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir
}
// get the node under our cursor
-
m_dwindleNodesData.emplace_back();
const auto NEWPARENT = &m_dwindleNodesData.back();
@@ -362,8 +409,17 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir
static auto PWIDTHMULTIPLIER = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
+ const auto edges = getNodeDisplayEdgeFlags(NEWPARENT->box, PMONITOR);
+
+ const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PNODE->workspaceID);
+ auto [gapsIn, gapsOut] = getWorkspaceGaps(WORKSPACE);
+
// if cursor over first child, make it first, etc
- const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * *PWIDTHMULTIPLIER;
+ const Vector2D availableSize = NEWPARENT->box.size() -
+ Vector2D{(edges.left ? gapsOut.m_left : gapsIn.m_left / 2.f) + (edges.right ? gapsOut.m_right : gapsIn.m_right / 2.f),
+ (edges.top ? gapsOut.m_top : gapsIn.m_top / 2.f) + (edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom / 2.f)};
+
+ const auto SIDEBYSIDE = availableSize.x > availableSize.y * *PWIDTHMULTIPLIER;
NEWPARENT->splitTop = !SIDEBYSIDE;
static auto PFORCESPLIT = CConfigValue<Hyprlang::INT>("dwindle:force_split");
@@ -611,11 +667,8 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
static auto PSMARTRESIZING = CConfigValue<Hyprlang::INT>("dwindle:smart_resizing");
// get some data about our window
- const auto PMONITOR = PWINDOW->m_monitor.lock();
- const bool DISPLAYLEFT = STICKS(PWINDOW->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
- const bool DISPLAYRIGHT = STICKS(PWINDOW->m_position.x + PWINDOW->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
- const bool DISPLAYTOP = STICKS(PWINDOW->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
- const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_position.y + PWINDOW->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
+ const auto PMONITOR = PWINDOW->m_monitor.lock();
+ const auto edges = getNodeDisplayEdgeFlags(CBox{PWINDOW->m_position, PWINDOW->m_size}, PMONITOR);
if (PWINDOW->m_isPseudotiled) {
if (!m_pseudoDragFlags.started) {
@@ -663,10 +716,10 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
// construct allowed movement
Vector2D allowedMovement = pixResize;
- if (DISPLAYLEFT && DISPLAYRIGHT)
+ if (edges.left && edges.right)
allowedMovement.x = 0;
- if (DISPLAYBOTTOM && DISPLAYTOP)
+ if (edges.bottom && edges.top)
allowedMovement.y = 0;
if (*PSMARTRESIZING == 1) {
@@ -676,10 +729,10 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
SDwindleNodeData* PHOUTER = nullptr;
SDwindleNodeData* PHINNER = nullptr;
- const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT;
- const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM;
- const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT;
- const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP;
+ const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || edges.right;
+ const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || edges.bottom;
+ const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || edges.left;
+ const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || edges.top;
const auto NONE = corner == CORNER_NONE;
for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent) {