compositor: fix focus edge detection (#13425)
fixes edge detection, making it more relaxed and intuitive
This commit is contained in:
parent
e333a330c0
commit
b90c61c04f
4 changed files with 123 additions and 10 deletions
79
hyprtester/src/tests/main/scroll.cpp
Normal file
79
hyprtester/src/tests/main/scroll.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include "../shared.hpp"
|
||||||
|
#include "../../shared.hpp"
|
||||||
|
#include "../../hyprctlCompat.hpp"
|
||||||
|
#include "tests.hpp"
|
||||||
|
|
||||||
|
static int ret = 0;
|
||||||
|
|
||||||
|
static void testFocusCycling() {
|
||||||
|
for (auto const& win : {"a", "b", "c", "d"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: d");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: d");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus u"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test() {
|
||||||
|
NLog::log("{}Testing Scroll layout", Colors::GREEN);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
OK(getFromSocket("/dispatch workspace name:scroll"));
|
||||||
|
OK(getFromSocket("/keyword general:layout scrolling"));
|
||||||
|
|
||||||
|
// test
|
||||||
|
NLog::log("{}Testing focus cycling", Colors::GREEN);
|
||||||
|
testFocusCycling();
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TEST_FN(test);
|
||||||
|
|
@ -93,9 +93,9 @@ static void testSwapWindow() {
|
||||||
{
|
{
|
||||||
getFromSocket("/dispatch focuswindow class:kitty_A");
|
getFromSocket("/dispatch focuswindow class:kitty_A");
|
||||||
auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:");
|
auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:");
|
||||||
NLog::log("{}Testing kitty_A {}, swapwindow with direction 'l'", Colors::YELLOW, pos);
|
NLog::log("{}Testing kitty_A {}, swapwindow with direction 'r'", Colors::YELLOW, pos);
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch swapwindow l"));
|
OK(getFromSocket("/dispatch swapwindow r"));
|
||||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos));
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos));
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,15 @@ master {
|
||||||
new_status = master
|
new_status = master
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrolling {
|
||||||
|
fullscreen_on_one_column = true
|
||||||
|
column_width = 0.5
|
||||||
|
focus_fit_method = 1
|
||||||
|
follow_focus = true
|
||||||
|
follow_min_visible = 1
|
||||||
|
explicit_column_widths = 0.25, 0.333, 0.5, 0.667, 0.75, 1.0
|
||||||
|
}
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
||||||
misc {
|
misc {
|
||||||
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
||||||
|
|
|
||||||
|
|
@ -1404,6 +1404,35 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
||||||
PHLWINDOW leaderWindow = nullptr;
|
PHLWINDOW leaderWindow = nullptr;
|
||||||
|
|
||||||
if (!useVectorAngles) {
|
if (!useVectorAngles) {
|
||||||
|
// helper to check if two rectangles are adjacent along an axis, considering slight overlaps.
|
||||||
|
// returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension.
|
||||||
|
static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool {
|
||||||
|
constexpr double STICK_THRESHOLD = 2.0;
|
||||||
|
constexpr double MAX_OVERLAP_RATIO = 0.5;
|
||||||
|
|
||||||
|
const double aEdge = aMin;
|
||||||
|
const double bEdge = bMax;
|
||||||
|
const double delta = aEdge - bEdge;
|
||||||
|
|
||||||
|
// old STICKS check for 2px
|
||||||
|
if (std::abs(delta) < STICK_THRESHOLD)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (delta >= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const double overlap = -delta;
|
||||||
|
const double sizeA = aMax - aMin;
|
||||||
|
const double sizeB = bMax - bMin;
|
||||||
|
|
||||||
|
// reject if one rectangle fully contains the other
|
||||||
|
if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// accept if overlap is at most 50% of the smaller dimension
|
||||||
|
return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO;
|
||||||
|
};
|
||||||
|
|
||||||
for (auto const& w : m_windows) {
|
for (auto const& w : m_windows) {
|
||||||
if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
|
if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1426,24 +1455,20 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
||||||
|
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case Math::DIRECTION_LEFT:
|
case Math::DIRECTION_LEFT:
|
||||||
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
|
if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.x, POSB.x + SIZEB.x))
|
||||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Math::DIRECTION_RIGHT:
|
case Math::DIRECTION_RIGHT:
|
||||||
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
|
if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x))
|
||||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Math::DIRECTION_UP:
|
case Math::DIRECTION_UP:
|
||||||
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
|
if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.y, POSB.y + SIZEB.y))
|
||||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Math::DIRECTION_DOWN:
|
case Math::DIRECTION_DOWN:
|
||||||
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
|
if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y))
|
||||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue