dwindle: simplify split_bias logic and set of possible values. (#11448)

This commit is contained in:
Mike Will
2025-08-19 14:32:37 -04:00
committed by GitHub
parent d0d728c6a6
commit 10cec2b7e2
5 changed files with 79 additions and 56 deletions

View File

@@ -46,16 +46,16 @@ static void expectSnapMove(const Vector2D FROM, const Vector2D* TO) {
} }
static void testWindowSnap(const bool RESPECTGAPS) { static void testWindowSnap(const bool RESPECTGAPS) {
const double BORDERSIZE = 2; const int BORDERSIZE = 2;
const double WINDOWSIZE = 100; const int WINDOWSIZE = 100;
const double OTHER = 500; const int OTHER = 500;
const double WINDOWGAP = 8; const int WINDOWGAP = 8;
const double GAPSIN = 5; const int GAPSIN = 5;
const double GAP = (RESPECTGAPS ? 2 * GAPSIN : 0) + (2 * BORDERSIZE); const int GAP = (RESPECTGAPS ? 2 * GAPSIN : 0) + (2 * BORDERSIZE);
const double END = GAP + WINDOWSIZE; const int END = GAP + WINDOWSIZE;
double x; int x;
Vector2D predict; Vector2D predict;
x = WINDOWGAP + END; x = WINDOWGAP + END;
@@ -71,16 +71,16 @@ static void testWindowSnap(const bool RESPECTGAPS) {
} }
static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) { static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) {
const double BORDERSIZE = 2; const int BORDERSIZE = 2;
const double WINDOWSIZE = 100; const int WINDOWSIZE = 100;
const double MONITORGAP = 10; const int MONITORGAP = 10;
const double GAPSOUT = 20; const int GAPSOUT = 20;
const double RESP = (RESPECTGAPS ? GAPSOUT : 0); const int RESP = (RESPECTGAPS ? GAPSOUT : 0);
const double GAP = RESP + (OVERLAP ? 0 : BORDERSIZE); const int GAP = RESP + (OVERLAP ? 0 : BORDERSIZE);
const double END = GAP + WINDOWSIZE; const int END = GAP + WINDOWSIZE;
double x; int x;
Vector2D predict; Vector2D predict;
x = MONITORGAP + GAP; x = MONITORGAP + GAP;
@@ -94,9 +94,9 @@ static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) {
expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - END, 1080 - END})); expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - END, 1080 - END}));
// test reserved area // test reserved area
const double RESERVED = 200; const int RESERVED = 200;
const double RGAP = RESERVED + RESP + BORDERSIZE; const int RGAP = RESERVED + RESP + BORDERSIZE;
const double REND = RGAP + WINDOWSIZE; const int REND = RGAP + WINDOWSIZE;
x = MONITORGAP + RGAP; x = MONITORGAP + RGAP;
expectSnapMove({x, x}, nullptr); expectSnapMove({x, x}, nullptr);

View File

@@ -1,22 +1,24 @@
#include "tests.hpp" #include <cmath>
#include "../../shared.hpp"
#include "../../hyprctlCompat.hpp"
#include <print>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <hyprutils/os/Process.hpp> #include <hyprutils/os/Process.hpp>
#include <hyprutils/memory/WeakPtr.hpp> #include <hyprutils/memory/WeakPtr.hpp>
#include <csignal>
#include <cerrno> #include "../../shared.hpp"
#include "../../hyprctlCompat.hpp"
#include "../shared.hpp" #include "../shared.hpp"
#include "tests.hpp"
static int ret = 0; static int ret = 0;
using namespace Hyprutils::OS; static bool spawnKitty(const std::string& class_) {
using namespace Hyprutils::Memory; NLog::log("{}Spawning {}", Colors::YELLOW, class_);
if (!Tests::spawnKitty(class_)) {
#define UP CUniquePointer NLog::log("{}Error: {} did not spawn", Colors::RED, class_);
#define SP CSharedPointer return false;
}
return true;
}
static bool test() { static bool test() {
NLog::log("{}Testing windows", Colors::GREEN); NLog::log("{}Testing windows", Colors::GREEN);
@@ -25,19 +27,11 @@ static bool test() {
NLog::log("{}Switching to workspace `window`", Colors::YELLOW); NLog::log("{}Switching to workspace `window`", Colors::YELLOW);
getFromSocket("/dispatch workspace name:window"); getFromSocket("/dispatch workspace name:window");
NLog::log("{}Spawning kittyProcA", Colors::YELLOW); if (!spawnKitty("kitty_A"))
auto kittyProcA = Tests::spawnKitty();
if (!kittyProcA) {
NLog::log("{}Error: kitty did not spawn", Colors::RED);
return false; return false;
}
NLog::log("{}Expecting 1 window", Colors::YELLOW);
EXPECT(Tests::windowCount(), 1);
// check kitty properties. One kitty should take the entire screen, as this is smart gaps // check kitty properties. One kitty should take the entire screen, as this is smart gaps
NLog::log("{}Expecting kitty to take up the whole screen", Colors::YELLOW); NLog::log("{}Expecting kitty_A to take up the whole screen", Colors::YELLOW);
{ {
auto str = getFromSocket("/clients"); auto str = getFromSocket("/clients");
EXPECT(str.contains("at: 0,0"), true); EXPECT(str.contains("at: 0,0"), true);
@@ -45,15 +39,44 @@ static bool test() {
EXPECT(str.contains("fullscreen: 0"), true); EXPECT(str.contains("fullscreen: 0"), true);
} }
NLog::log("{}Spawning kittyProcB", Colors::YELLOW); NLog::log("{}Testing window split ratios", Colors::YELLOW);
auto kittyProcB = Tests::spawnKitty(); {
if (!kittyProcB) { const double RATIO = 1.25;
NLog::log("{}Error: kitty did not spawn", Colors::RED); const double PERCENT = RATIO / 2.0 * 100.0;
return false; const int GAPSIN = 5;
} const int GAPSOUT = 20;
const int BORDERS = 2 * 2;
const int WTRIM = BORDERS + GAPSIN + GAPSOUT;
const int HEIGHT = 1080 - (BORDERS + (GAPSOUT * 2));
const int WIDTH1 = std::round(1920.0 / 2.0 * (2 - RATIO)) - WTRIM;
const int WIDTH2 = std::round(1920.0 / 2.0 * RATIO) - WTRIM;
NLog::log("{}Expecting 2 windows", Colors::YELLOW); OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25"));
EXPECT(Tests::windowCount(), 2);
if (!spawnKitty("kitty_B"))
return false;
NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, 100 - PERCENT);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT));
OK(getFromSocket("/dispatch killwindow activewindow"));
Tests::waitUntilWindowsN(1);
NLog::log("{}Inverting the split ratio", Colors::YELLOW);
OK(getFromSocket("/keyword dwindle:default_split_ratio 0.75"));
if (!spawnKitty("kitty_B"))
return false;
NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, PERCENT);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT));
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
NLog::log("{}Expecting kitty_A to have the same width as the previous kitty_B", Colors::YELLOW);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT));
OK(getFromSocket("/keyword dwindle:default_split_ratio 1"));
}
// open xeyes // open xeyes
NLog::log("{}Spawning xeyes", Colors::YELLOW); NLog::log("{}Spawning xeyes", Colors::YELLOW);

View File

@@ -165,6 +165,7 @@ animations {
dwindle { dwindle {
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
preserve_split = true # You probably want this preserve_split = true # You probably want this
split_bias = 1
} }
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more

View File

@@ -1827,9 +1827,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
}, },
SConfigOptionDescription{ SConfigOptionDescription{
.value = "dwindle:split_bias", .value = "dwindle:split_bias",
.description = "specifies which window will receive the larger half of a split. positional - 0, current window - 1, opening window - 2 [0/1/2]", .description = "specifies which window will receive the split ratio. 0 -> directional (the top or left window), 1 -> the current window",
.type = CONFIG_OPTION_CHOICE, .type = CONFIG_OPTION_CHOICE,
.data = SConfigOptionDescription::SChoiceData{0, "positional,current,opening"}, .data = SConfigOptionDescription::SChoiceData{0, "directional,current"},
}, },
SConfigOptionDescription{ SConfigOptionDescription{
.value = "dwindle:precise_mouse_move", .value = "dwindle:precise_mouse_move",

View File

@@ -347,6 +347,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir
static auto PFORCESPLIT = CConfigValue<Hyprlang::INT>("dwindle:force_split"); static auto PFORCESPLIT = CConfigValue<Hyprlang::INT>("dwindle:force_split");
static auto PERMANENTDIRECTIONOVERRIDE = CConfigValue<Hyprlang::INT>("dwindle:permanent_direction_override"); static auto PERMANENTDIRECTIONOVERRIDE = CConfigValue<Hyprlang::INT>("dwindle:permanent_direction_override");
static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split"); static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split");
static auto PSPLITBIAS = CConfigValue<Hyprlang::INT>("dwindle:split_bias");
bool horizontalOverride = false; bool horizontalOverride = false;
bool verticalOverride = false; bool verticalOverride = false;
@@ -427,9 +428,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir
} }
// split in favor of a specific window // split in favor of a specific window
const auto first = NEWPARENT->children[0]; if (*PSPLITBIAS && NEWPARENT->children[0] == PNODE)
static auto PSPLITBIAS = CConfigValue<Hyprlang::INT>("dwindle:split_bias");
if ((*PSPLITBIAS == 1 && first == PNODE) || (*PSPLITBIAS == 2 && first == OPENINGON))
NEWPARENT->splitRatio = 2.f - NEWPARENT->splitRatio; NEWPARENT->splitRatio = 2.f - NEWPARENT->splitRatio;
// and update the previous parent if it exists // and update the previous parent if it exists