diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp index ef270a62..234bfc33 100644 --- a/hyprtester/src/tests/main/dwindle.cpp +++ b/hyprtester/src/tests/main/dwindle.cpp @@ -135,6 +135,98 @@ static void testSplit() { Tests::killAllWindows(); } +static void testRotatesplit() { + OK(getFromSocket("r/keyword general:gaps_in 0")); + OK(getFromSocket("r/keyword general:gaps_out 0")); + OK(getFromSocket("r/keyword general:border_size 0")); + + for (auto const& win : {"a", "b"}) { + if (!Tests::spawnKitty(win)) { + NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); + ++TESTS_FAILED; + ret = 1; + return; + } + } + + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 960,1080"); + } + + // test 4 repeated rotations by 90 degrees + OK(getFromSocket("/dispatch layoutmsg rotatesplit")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,540"); + } + + OK(getFromSocket("/dispatch layoutmsg rotatesplit")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 960,0"); + EXPECT_CONTAINS(str, "size: 960,1080"); + } + + OK(getFromSocket("/dispatch layoutmsg rotatesplit")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,540"); + EXPECT_CONTAINS(str, "size: 1920,540"); + } + + OK(getFromSocket("/dispatch layoutmsg rotatesplit")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 960,1080"); + } + + // test different angles + OK(getFromSocket("/dispatch layoutmsg rotatesplit 180")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 960,0"); + EXPECT_CONTAINS(str, "size: 960,1080"); + } + + OK(getFromSocket("/dispatch layoutmsg rotatesplit 270")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,540"); + EXPECT_CONTAINS(str, "size: 1920,540"); + } + + OK(getFromSocket("/dispatch layoutmsg rotatesplit 360")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 1920,540"); + } + + // test negative angles + OK(getFromSocket("/dispatch layoutmsg rotatesplit -90")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 0,0"); + EXPECT_CONTAINS(str, "size: 960,1080"); + } + + OK(getFromSocket("/dispatch layoutmsg rotatesplit -180")); + { + auto str = getFromSocket("/clients"); + EXPECT_CONTAINS(str, "at: 960,0"); + EXPECT_CONTAINS(str, "size: 960,1080"); + } + + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + + OK(getFromSocket("/reload")); +} + static bool test() { NLog::log("{}Testing Dwindle layout", Colors::GREEN); @@ -148,6 +240,9 @@ static bool test() { NLog::log("{}Testing splits", Colors::GREEN); testSplit(); + NLog::log("{}Testing rotatesplit", Colors::GREEN); + testRotatesplit(); + // clean up NLog::log("Cleaning up", Colors::YELLOW); getFromSocket("/dispatch workspace 1"); diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp index 716097ba..7ef36753 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp @@ -666,6 +666,19 @@ std::expected CDwindleAlgorithm::layoutMsg(const std::string_ if (!swapSplit(CURRENT_NODE)) return std::unexpected("can't swapsplit in the current workspace"); } + } else if (ARGS[0] == "rotatesplit") { + if (CURRENT_NODE) { + int angle = 90; + if (!ARGS[1].empty()) { + try { + angle = std::stoi(std::string{ARGS[1]}); + } catch (const std::exception& e) { + Log::logger->log(Log::WARN, "Invalid angle argument for rotatesplit: {}", ARGS[1]); + return std::unexpected("Invalid angle argument"); + } + } + rotateSplit(CURRENT_NODE, angle); + } } else if (ARGS[0] == "movetoroot") { auto node = CURRENT_NODE; if (!ARGS[1].empty()) { @@ -760,6 +773,43 @@ bool CDwindleAlgorithm::swapSplit(SP x) { return true; } +void CDwindleAlgorithm::rotateSplit(SP x, int angle) { + if (!x || !x->pParent) + return; + + if (x->pTarget->fullscreenMode() != FSMODE_NONE) + return; + + // normalize the angle to multiples of 90 degrees + int normalizedAngle = ((sc(angle / 90) % 4) + 4) % 4; // ensures positive modulo + + auto pParent = x->pParent; + + bool shouldSwap = false; + + switch (normalizedAngle) { + case 0: // 0 degrees - no change + break; + case 1: + if (pParent->splitTop) + shouldSwap = true; + pParent->splitTop = !pParent->splitTop; + break; + case 2: shouldSwap = true; break; + case 3: + if (!pParent->splitTop) + shouldSwap = true; + pParent->splitTop = !pParent->splitTop; + break; + default: break; // should never happen + } + + if (shouldSwap) + std::swap(pParent->children[0], pParent->children[1]); + + pParent->recalcSizePosRecursive(); +} + bool CDwindleAlgorithm::moveToRoot(SP x, bool stable) { if (!x || !x->pParent) return false; diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index 97ea2908..41cbf8bb 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp @@ -50,6 +50,7 @@ namespace Layout::Tiled { bool toggleSplit(SP); bool swapSplit(SP); + void rotateSplit(SP, int angle = 90); bool moveToRoot(SP, bool stable = true); Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT;