From 2631b66e65969d9d9abac580b17963d85b5056e6 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:32:24 +0200 Subject: [PATCH 01/40] fix scrolling view move when a grouped window is closed - prevent redundant space assignment for a grouped window that's about to be unmapped, add a `FOCUS_REASON` for unmapping grouped windows --- src/desktop/state/FocusState.hpp | 1 + src/desktop/view/Group.cpp | 6 +++++- src/desktop/view/Group.hpp | 8 +++++++- src/desktop/view/Window.cpp | 4 ++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index 71330a3eb..d9f2ede7f 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -13,6 +13,7 @@ namespace Desktop { FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, + FOCUS_REASON_UNMAP_GROUPED_WINDOW, FOCUS_REASON_NEW_WINDOW, FOCUS_REASON_GHOSTS, }; diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index f2b2a5642..dcfba62d9 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -122,7 +122,7 @@ void CGroup::add(PHLWINDOW w) { m_target->recalc(); } -void CGroup::remove(PHLWINDOW w, Math::eDirection dir) { +void CGroup::remove(PHLWINDOW w, Math::eDirection dir, std::optional reason) { std::optional idx; for (size_t i = 0; i < m_windows.size(); ++i) { if (m_windows.at(i) == w) { @@ -171,6 +171,10 @@ void CGroup::remove(PHLWINDOW w, Math::eDirection dir) { default: break; } } + + // We don't need to assign a window to a new space if we intend to unmap it + if (reason == REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW) + return; w->m_target->assignToSpace(m_target->space(), focalPoint); } } diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 048c5023b..46203fab2 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -3,6 +3,7 @@ #include "../DesktopTypes.hpp" #include "../../helpers/math/Direction.hpp" +#include #include namespace Layout { @@ -15,10 +16,15 @@ namespace Desktop::View { static SP create(std::vector&& windows); ~CGroup(); + + enum eRemoveFromGroupReason :uint8_t { + REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW, + }; + bool has(PHLWINDOW w) const; void add(PHLWINDOW w); - void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT); + void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT, std::optional reason = std::nullopt); void moveCurrent(bool next); void setCurrent(size_t idx); void setCurrent(PHLWINDOW w); diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index b2f4821b8..d3d16b945 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -2361,7 +2361,7 @@ void CWindow::unmapWindow() { PWORKSPACE->m_hasFullscreenWindow = false; if (m_group) - m_group->remove(m_self.lock()); + m_group->remove(m_self.lock(), Math::DIRECTION_DEFAULT, CGroup::REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW); g_layoutManager->removeTarget(m_target); @@ -2390,7 +2390,7 @@ void CWindow::unmapWindow() { if (candidate != Desktop::focusState()->window() && candidate) { if (candidate == nextInGroup) - Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE); + Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_UNMAP_GROUPED_WINDOW); else Desktop::focusState()->fullWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE); From 7c1592297cf68a9345d8a5f2dea6c81f8b4aea08 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:43:08 +0200 Subject: [PATCH 02/40] fix focus_fallback after unmapping a window floating moving view --- src/desktop/state/FocusState.cpp | 2 +- src/desktop/state/FocusState.hpp | 2 ++ src/desktop/view/Window.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/desktop/state/FocusState.cpp b/src/desktop/state/FocusState.cpp index 5db4bac5c..f8699e664 100644 --- a/src/desktop/state/FocusState.cpp +++ b/src/desktop/state/FocusState.cpp @@ -301,5 +301,5 @@ void CFocusState::resetWindowFocus() { } bool Desktop::isHardInputFocusReason(eFocusReason r) { - return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE; + return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE || r == FOCUS_REASON_UNMAP_WINDOW_TILING; } diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index d9f2ede7f..f2e187fcf 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -13,6 +13,8 @@ namespace Desktop { FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, + FOCUS_REASON_UNMAP_WINDOW_TILING, + FOCUS_REASON_UNMAP_WINDOW_FLOATING, FOCUS_REASON_UNMAP_GROUPED_WINDOW, FOCUS_REASON_NEW_WINDOW, FOCUS_REASON_GHOSTS, diff --git a/src/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index d3d16b945..03229190a 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -2392,7 +2392,7 @@ void CWindow::unmapWindow() { if (candidate == nextInGroup) Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_UNMAP_GROUPED_WINDOW); else - Desktop::focusState()->fullWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE); + Desktop::focusState()->fullWindowFocus(candidate, m_self->m_isFloating ? FOCUS_REASON_UNMAP_WINDOW_FLOATING : FOCUS_REASON_UNMAP_WINDOW_TILING); if ((*PEXITRETAINSFS || candidate == nextInGroup) && CURRENTWINDOWFSSTATE) g_pCompositor->setWindowFullscreenInternal(candidate, CURRENTFSMODE); From 8ccf0f53d407972f7a19b9bb6bf98172a511bd20 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 04:09:20 +0200 Subject: [PATCH 03/40] fix view change when changing workspaces (special and normal). add an optional `RECALCULATE_REASON` parameter to `recalculate()` calls of all algorithms. add an optional `RECALCULATE_MONITOR_REASON` parameter to `recalculateMonitor()` calls. add 2 new `FOCUS_REASON`s, for workspace change and special workspace toggle --- src/desktop/state/FocusState.hpp | 2 ++ src/helpers/Monitor.cpp | 14 ++++++------- src/layout/LayoutManager.cpp | 21 ++++++++++++++----- src/layout/LayoutManager.hpp | 9 ++++++-- src/layout/algorithm/Algorithm.cpp | 6 +++--- src/layout/algorithm/Algorithm.hpp | 5 +++-- src/layout/algorithm/FloatingAlgorithm.cpp | 2 +- src/layout/algorithm/FloatingAlgorithm.hpp | 2 +- src/layout/algorithm/ModeAlgorithm.hpp | 3 ++- .../tiled/dwindle/DwindleAlgorithm.cpp | 2 +- .../tiled/dwindle/DwindleAlgorithm.hpp | 2 +- .../tiled/master/MasterAlgorithm.cpp | 2 +- .../tiled/master/MasterAlgorithm.hpp | 2 +- .../tiled/monocle/MonocleAlgorithm.cpp | 2 +- .../tiled/monocle/MonocleAlgorithm.hpp | 4 ++-- .../tiled/scrolling/ScrollingAlgorithm.cpp | 9 +++++--- .../tiled/scrolling/ScrollingAlgorithm.hpp | 4 ++-- src/layout/space/Space.cpp | 4 ++-- src/layout/space/Space.hpp | 9 +++++++- 19 files changed, 67 insertions(+), 37 deletions(-) diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index f2e187fcf..b294c375b 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -13,6 +13,8 @@ namespace Desktop { FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, + FOCUS_REASON_WORKSPACE_CHANGE, + FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE, FOCUS_REASON_UNMAP_WINDOW_TILING, FOCUS_REASON_UNMAP_WINDOW_FLOATING, FOCUS_REASON_UNMAP_GROUPED_WINDOW, diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index a5195a94f..fe75040ed 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1409,13 +1409,13 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo pWindow = pWorkspace->getFirstWindow(); } - Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); + Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_WORKSPACE_CHANGE); } if (!noMouseMove) g_pInputManager->simulateMouseMovement(); - g_layoutManager->recalculateMonitor(m_self.lock()); + g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE); g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_name}); g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", pWorkspace->m_id, pWorkspace->m_name)}); @@ -1478,11 +1478,11 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { if (POLDSPECIAL) POLDSPECIAL->m_events.activeChanged.emit(); - g_layoutManager->recalculateMonitor(m_self.lock()); + g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE); if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) { if (const auto PLAST = m_activeWorkspace->getLastFocusedWindow(); PLAST) - Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND); + Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE); else g_pInputManager->refocus(); } @@ -1508,7 +1508,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { if (PMONITOR && PMONITOR->m_activeSpecialWorkspace == pWorkspace) { PMONITOR->m_activeSpecialWorkspace.reset(); - g_layoutManager->recalculateMonitor(PMONITOR); + g_layoutManager->recalculateMonitor(PMONITOR, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE); g_pHyprRenderer->damageMonitor(PMONITOR); g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMONITOR->m_name}); g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMONITOR->m_name}); @@ -1573,11 +1573,11 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) { } } - g_layoutManager->recalculateMonitor(m_self.lock()); + g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE); if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) { if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST) - Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND); + Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE); else g_pInputManager->refocus(); } diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index f90cedf63..f9a2821e9 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -332,11 +332,22 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SPm_activeSpecialWorkspace) - m->m_activeSpecialWorkspace->m_space->recalculate(); - if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); +void CLayoutManager::recalculateMonitor(PHLMONITOR m, std::optional reason) { + if (m->m_activeSpecialWorkspace) { + if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE) + m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE); + else + m->m_activeSpecialWorkspace->m_space->recalculate(); + return; + } + + if (m->m_activeWorkspace) { + if (reason == RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE) + m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_WORKSPACE_CHANGE); + else + m->m_activeWorkspace->m_space->recalculate(); + return; + } } void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) { diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index 2660cf289..19f18f47b 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -45,6 +45,11 @@ namespace Layout { CLayoutManager(); ~CLayoutManager() = default; + enum eRecalculateMonitorReason : uint8_t { + RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE, + RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE + }; + void newTarget(SP target, SP space); void removeTarget(SP target); @@ -75,8 +80,8 @@ namespace Layout { void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP target, eMouseBindMode mode, int corner, const Vector2D& beginSize); - void invalidateMonitorGeometries(PHLMONITOR); - void recalculateMonitor(PHLMONITOR); + void invalidateMonitorGeometries(PHLMONITOR); + void recalculateMonitor(PHLMONITOR, std::optional reason = std::nullopt); const UP& dragController(); diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp index b22eb9bfc..3867d352f 100644 --- a/src/layout/algorithm/Algorithm.cpp +++ b/src/layout/algorithm/Algorithm.cpp @@ -101,9 +101,9 @@ size_t CAlgorithm::floatingTargets() const { return m_floatingTargets.size(); } -void CAlgorithm::recalculate() { - m_tiled->recalculate(); - m_floating->recalculate(); +void CAlgorithm::recalculate(std::optional reason) { + m_tiled->recalculate(reason); + m_floating->recalculate(reason); const auto PWORKSPACE = m_space->workspace(); if (!PWORKSPACE) diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp index 8b9e471d2..24701d0ec 100644 --- a/src/layout/algorithm/Algorithm.hpp +++ b/src/layout/algorithm/Algorithm.hpp @@ -5,6 +5,7 @@ #include "../../helpers/memory/Memory.hpp" #include "../LayoutManager.hpp" +#include "layout/space/Space.hpp" #include #include @@ -34,8 +35,8 @@ namespace Layout { Config::ErrorResult layoutMsg(const std::string_view& sv); std::optional predictSizeForNewTiledTarget(); - void recalculate(); - void recenter(SP t); + void recalculate(std::optional reason = std::nullopt); + void recenter(SP t); void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); void moveTarget(const Vector2D& Δ, SP target); diff --git a/src/layout/algorithm/FloatingAlgorithm.cpp b/src/layout/algorithm/FloatingAlgorithm.cpp index 058887bf0..f3c286277 100644 --- a/src/layout/algorithm/FloatingAlgorithm.cpp +++ b/src/layout/algorithm/FloatingAlgorithm.cpp @@ -4,7 +4,7 @@ using namespace Layout; -void IFloatingAlgorithm::recalculate() { +void IFloatingAlgorithm::recalculate(std::optional reason) { ; } diff --git a/src/layout/algorithm/FloatingAlgorithm.hpp b/src/layout/algorithm/FloatingAlgorithm.hpp index 40e530343..936f00b59 100644 --- a/src/layout/algorithm/FloatingAlgorithm.hpp +++ b/src/layout/algorithm/FloatingAlgorithm.hpp @@ -22,7 +22,7 @@ namespace Layout { virtual void recenter(SP t); - virtual void recalculate(); + virtual void recalculate(std::optional reason = std::nullopt); protected: IFloatingAlgorithm() = default; diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp index e53d4391e..08de09eb4 100644 --- a/src/layout/algorithm/ModeAlgorithm.hpp +++ b/src/layout/algorithm/ModeAlgorithm.hpp @@ -5,6 +5,7 @@ #include "../../helpers/memory/Memory.hpp" #include "../LayoutManager.hpp" +#include "layout/space/Space.hpp" #include @@ -30,7 +31,7 @@ namespace Layout { virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE) = 0; // recalculate layout - virtual void recalculate() = 0; + virtual void recalculate(std::optional reason = std::nullopt) = 0; // swap targets virtual void swapTargets(SP a, SP b) = 0; diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp index 100856be3..c8509da5d 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp @@ -488,7 +488,7 @@ void CDwindleAlgorithm::swapTargets(SP a, SP b) { nodeB->pTarget = a; } -void CDwindleAlgorithm::recalculate() { +void CDwindleAlgorithm::recalculate(std::optional reason) { calculateWorkspace(); } diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index 300daa639..85eea2fcf 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp @@ -35,7 +35,7 @@ namespace Layout::Tiled { virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); + virtual void recalculate(std::optional reason = std::nullopt); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp index a0ea13c34..abaf05d75 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp @@ -424,7 +424,7 @@ void CMasterAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir } } -void CMasterAlgorithm::recalculate() { +void CMasterAlgorithm::recalculate(std::optional reason) { calculateWorkspace(); } diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp index 00e827841..cd8fa278a 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp @@ -53,7 +53,7 @@ namespace Layout::Tiled { virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); + virtual void recalculate(std::optional reason = std::nullopt); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp index 84b4f9e2c..c3e80053a 100644 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp @@ -125,7 +125,7 @@ void CMonocleAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRe // monocle layout doesn't support manual resizing, all windows are fullscreen } -void CMonocleAlgorithm::recalculate() { +void CMonocleAlgorithm::recalculate(std::optional reason) { if (m_targetDatas.empty()) return; diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp index 3f07748b0..60e52bd3e 100644 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp @@ -25,8 +25,8 @@ namespace Layout::Tiled { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(std::optional reason = std::nullopt); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 28941109d..7bca0f9e1 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -796,7 +796,7 @@ void CScrollingAlgorithm::resizeTarget(const Vector2D& delta, SP target m_scrollingData->recalculate(true); } -void CScrollingAlgorithm::recalculate() { +void CScrollingAlgorithm::recalculate(std::optional reason) { // guard against recalculation during transitional monitor states // (e.g. monitor reconnecting after suspend where workspace/monitor may not be ready) if (!m_parent || !m_parent->space() || !m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor) @@ -807,8 +807,11 @@ void CScrollingAlgorithm::recalculate() { const auto TARGETDATA = dataFor(TARGET); - if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) - focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_KB); + if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) { + // guard against scrolling tape move when switching workspaces when target is scrolling (special or not) + if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE) + focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_KB); + } } m_scrollingData->recalculate(); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp index 288d15a62..7c430a2a6 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp @@ -98,8 +98,8 @@ namespace Layout::Tiled { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(std::optional reason = std::nullopt); virtual SP getNextCandidate(SP old); diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index 26c02aaec..7d95e1b3f 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -146,11 +146,11 @@ SP CSpace::algorithm() const { return m_algorithm; } -void CSpace::recalculate() { +void CSpace::recalculate(std::optional reason) { recheckWorkArea(); if (m_algorithm) - m_algorithm->recalculate(); + m_algorithm->recalculate(reason); } void CSpace::setFullscreen(SP t, eFullscreenMode mode) { diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 5d7f9d014..c116c6d6e 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -12,6 +12,13 @@ #include namespace Layout { + + enum eRecalculateReason : uint8_t { + RECALCULATE_REASON_WORKSPACE_CHANGE, + RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, + }; + + class ITarget; class CAlgorithm; @@ -34,7 +41,7 @@ namespace Layout { void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - void recalculate(); + void recalculate(std::optional reason = std::nullopt); void toggleTargetFloating(SP t); From a2c2a93f7900fecfc2007717b621d3d71946aa95 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:19:38 +0200 Subject: [PATCH 04/40] Fix hyprctl keyword causing scrolling view move. rename INPUT_MODE_KB to INPUT_MODE_HARD since it seems to have little to do with keybinds --- src/config/legacy/ConfigManager.cpp | 2 +- src/layout/LayoutManager.cpp | 5 +++++ src/layout/LayoutManager.hpp | 3 ++- .../algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 12 +++++++----- .../algorithm/tiled/scrolling/ScrollingAlgorithm.hpp | 2 +- src/layout/space/Space.hpp | 1 + 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/config/legacy/ConfigManager.cpp b/src/config/legacy/ConfigManager.cpp index 2705c3959..f97fde7e9 100644 --- a/src/config/legacy/ConfigManager.cpp +++ b/src/config/legacy/ConfigManager.cpp @@ -1096,7 +1096,7 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std:: // invalidate layouts if they changed if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { for (auto const& m : g_pCompositor->m_monitors) { - g_layoutManager->recalculateMonitor(m); + g_layoutManager->recalculateMonitor(m, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD); } } diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index f9a2821e9..57fd99ea0 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -332,10 +332,13 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP reason) { if (m->m_activeSpecialWorkspace) { if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE) m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE); + else if (reason == RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD) + m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_HYPRCTL_KEYWORD); else m->m_activeSpecialWorkspace->m_space->recalculate(); return; @@ -344,6 +347,8 @@ void CLayoutManager::recalculateMonitor(PHLMONITOR m, std::optionalm_activeWorkspace) { if (reason == RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE) m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_WORKSPACE_CHANGE); + else if (reason == RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD) + m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_HYPRCTL_KEYWORD); else m->m_activeWorkspace->m_space->recalculate(); return; diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index 19f18f47b..977d21cbe 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -47,7 +47,8 @@ namespace Layout { enum eRecalculateMonitorReason : uint8_t { RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE, - RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE + RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE, + RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD, }; void newTarget(SP target, SP space); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 7bca0f9e1..2de2ec62a 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -535,7 +535,7 @@ CScrollingAlgorithm::CScrollingAlgorithm() { if (!TARGET || TARGET->floating()) return; - focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_KB : INPUT_MODE_SOFT)); + focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); }); // Initialize default widths and direction @@ -576,7 +576,7 @@ void CScrollingAlgorithm::focusOnInput(SP target, eInputMode input) { } // if we moved via non-kb, and it's fully visible, ignore - if (m_scrollingData->visible(TARGETDATA->column.lock(), true) && input != INPUT_MODE_KB) + if (m_scrollingData->visible(TARGETDATA->column.lock(), true) && input != INPUT_MODE_HARD) return; static const auto PFITMETHOD = CConfigValue("scrolling:focus_fit_method"); @@ -808,9 +808,11 @@ void CScrollingAlgorithm::recalculate(std::optional reason) const auto TARGETDATA = dataFor(TARGET); if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) { - // guard against scrolling tape move when switching workspaces when target is scrolling (special or not) - if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE) - focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_KB); + // TODO: erstarr Maybe do smt about this mess + // RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE => guard against scrolling view move when switching workspaces when target is scrolling (special or not) + // RECALCULATE_REASON_HYPRCTL_KEYWORD => guard against `hyprctl keyword` commands moving scrolling view + if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD) + focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp index 7c430a2a6..7addcd0ea 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp @@ -115,7 +115,7 @@ namespace Layout::Tiled { enum eInputMode : uint8_t { INPUT_MODE_SOFT = 0, INPUT_MODE_CLICK, - INPUT_MODE_KB + INPUT_MODE_HARD }; private: diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index c116c6d6e..18b26945d 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -16,6 +16,7 @@ namespace Layout { enum eRecalculateReason : uint8_t { RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, + RECALCULATE_REASON_HYPRCTL_KEYWORD, }; From 91b3bfac927648e7047eacf8d455fb7cf97884dc Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:37:15 +0200 Subject: [PATCH 05/40] fix fullscreen/maximise moving the scrolling view --- src/Compositor.cpp | 2 +- src/layout/LayoutManager.cpp | 4 ++++ src/layout/LayoutManager.hpp | 1 + src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 2 +- src/layout/space/Space.cpp | 2 +- src/layout/space/Space.hpp | 1 + 6 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Compositor.cpp b/src/Compositor.cpp index ab371df22..f6b54b226 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2259,7 +2259,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE); PWINDOW->updateDecorationValues(); - g_layoutManager->recalculateMonitor(PMONITOR); + g_layoutManager->recalculateMonitor(PMONITOR, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN); // make all windows and layers on the same workspace under the fullscreen window for (auto const& w : m_windows) { diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index 57fd99ea0..51e85be93 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -339,6 +339,8 @@ void CLayoutManager::recalculateMonitor(PHLMONITOR m, std::optionalm_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE); else if (reason == RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD) m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_HYPRCTL_KEYWORD); + else if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN) + m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN); else m->m_activeSpecialWorkspace->m_space->recalculate(); return; @@ -349,6 +351,8 @@ void CLayoutManager::recalculateMonitor(PHLMONITOR m, std::optionalm_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_WORKSPACE_CHANGE); else if (reason == RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD) m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_HYPRCTL_KEYWORD); + else if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN) + m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN); else m->m_activeWorkspace->m_space->recalculate(); return; diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index 977d21cbe..abc704516 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -49,6 +49,7 @@ namespace Layout { RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE, RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE, RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD, + RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN, }; void newTarget(SP target, SP space); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 2de2ec62a..0ae147f3a 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -811,7 +811,7 @@ void CScrollingAlgorithm::recalculate(std::optional reason) // TODO: erstarr Maybe do smt about this mess // RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE => guard against scrolling view move when switching workspaces when target is scrolling (special or not) // RECALCULATE_REASON_HYPRCTL_KEYWORD => guard against `hyprctl keyword` commands moving scrolling view - if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD) + if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index 7d95e1b3f..eca35df78 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -159,7 +159,7 @@ void CSpace::setFullscreen(SP t, eFullscreenMode mode) { if (mode == FSMODE_NONE && m_algorithm && t->floating()) m_algorithm->recenter(t); - recalculate(); + recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN); } Config::ErrorResult CSpace::layoutMsg(const std::string_view& sv) { diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 18b26945d..db0deb55b 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -17,6 +17,7 @@ namespace Layout { RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, RECALCULATE_REASON_HYPRCTL_KEYWORD, + RECALCULATE_REASON_TOGGLE_FULLSCREEN, }; From 8814b100d5a9b636cdeeb258c6e9fb70839758d7 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 06:45:21 +0200 Subject: [PATCH 06/40] fix opening of layer 3 programs moving scrolling view --- src/layout/LayoutManager.cpp | 2 +- src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 2 +- src/layout/space/Space.hpp | 2 ++ src/render/Renderer.cpp | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index 51e85be93..181fbb54f 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -363,7 +363,7 @@ void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) { for (const auto& ws : g_pCompositor->getWorkspaces()) { if (ws && ws->m_monitor == m) { ws->m_space->recheckWorkArea(); - ws->m_space->recalculate(); + ws->m_space->recalculate(RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES); } } } diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 0ae147f3a..53e8e32ce 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -811,7 +811,7 @@ void CScrollingAlgorithm::recalculate(std::optional reason) // TODO: erstarr Maybe do smt about this mess // RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE => guard against scrolling view move when switching workspaces when target is scrolling (special or not) // RECALCULATE_REASON_HYPRCTL_KEYWORD => guard against `hyprctl keyword` commands moving scrolling view - if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN) + if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN && reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index db0deb55b..13749a479 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -18,6 +18,8 @@ namespace Layout { RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, RECALCULATE_REASON_HYPRCTL_KEYWORD, RECALCULATE_REASON_TOGGLE_FULLSCREEN, + RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES, + RECALCULATE_REASON_RENDER_MOINTOR, }; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 6800c1857..e9749ca90 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1956,7 +1956,7 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { if (pMonitor->m_scheduledRecalc) { pMonitor->m_scheduledRecalc = false; if (pMonitor->m_activeWorkspace) // might be missing (mirror) - pMonitor->m_activeWorkspace->m_space->recalculate(); + pMonitor->m_activeWorkspace->m_space->recalculate(Layout::RECALCULATE_REASON_RENDER_MOINTOR); } if (!pMonitor->m_output->needsFrame && pMonitor->m_forceFullFrames == 0) From 61a1ec4d220b26d9d443f1b5669a846ff5978e76 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 07:34:09 +0200 Subject: [PATCH 07/40] refactor, clean up and clang-format --- src/desktop/state/FocusState.cpp | 3 ++- src/desktop/view/Group.cpp | 2 +- src/desktop/view/Group.hpp | 5 ++-- src/layout/LayoutManager.cpp | 26 +++---------------- .../tiled/scrolling/ScrollingAlgorithm.cpp | 8 +++--- src/layout/space/Space.cpp | 18 +++++++++++++ src/layout/space/Space.hpp | 15 ++++++----- 7 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/desktop/state/FocusState.cpp b/src/desktop/state/FocusState.cpp index f8699e664..d46a59e0f 100644 --- a/src/desktop/state/FocusState.cpp +++ b/src/desktop/state/FocusState.cpp @@ -301,5 +301,6 @@ void CFocusState::resetWindowFocus() { } bool Desktop::isHardInputFocusReason(eFocusReason r) { - return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE || r == FOCUS_REASON_UNMAP_WINDOW_TILING; + return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE || + r == FOCUS_REASON_UNMAP_WINDOW_TILING; } diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index dcfba62d9..1287a4733 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -173,7 +173,7 @@ void CGroup::remove(PHLWINDOW w, Math::eDirection dir, std::optionalm_target->assignToSpace(m_target->space(), focalPoint); } diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 46203fab2..0357a08e4 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -16,9 +16,8 @@ namespace Desktop::View { static SP create(std::vector&& windows); ~CGroup(); - - enum eRemoveFromGroupReason :uint8_t { - REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW, + enum eRemoveFromGroupReason : uint8_t { + REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW, }; bool has(PHLWINDOW w) const; diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index 181fbb54f..19833ba3b 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -334,29 +334,11 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP reason) { - if (m->m_activeSpecialWorkspace) { - if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE) - m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE); - else if (reason == RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD) - m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_HYPRCTL_KEYWORD); - else if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN) - m->m_activeSpecialWorkspace->m_space->recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN); - else - m->m_activeSpecialWorkspace->m_space->recalculate(); - return; - } + if (m->m_activeSpecialWorkspace) + m->m_activeSpecialWorkspace->m_space->recalculate(recalcMonitorReasontoRecalcReason(reason)); - if (m->m_activeWorkspace) { - if (reason == RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE) - m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_WORKSPACE_CHANGE); - else if (reason == RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD) - m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_HYPRCTL_KEYWORD); - else if (reason == RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN) - m->m_activeWorkspace->m_space->recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN); - else - m->m_activeWorkspace->m_space->recalculate(); - return; - } + if (m->m_activeWorkspace) + m->m_activeWorkspace->m_space->recalculate(recalcMonitorReasontoRecalcReason(reason)); } void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) { diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 53e8e32ce..d4e717e69 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -808,10 +808,10 @@ void CScrollingAlgorithm::recalculate(std::optional reason) const auto TARGETDATA = dataFor(TARGET); if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) { - // TODO: erstarr Maybe do smt about this mess - // RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE => guard against scrolling view move when switching workspaces when target is scrolling (special or not) - // RECALCULATE_REASON_HYPRCTL_KEYWORD => guard against `hyprctl keyword` commands moving scrolling view - if (reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN && reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR) + + // guard against unwanted scrolling viewport moves + // (e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) + if (Layout::isHardRecalculateReason(reason.value())) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index eca35df78..f7c593f06 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -202,6 +202,24 @@ SP CSpace::getNextCandidate(SP old) { return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old); } +bool Layout::isHardRecalculateReason(eRecalculateReason reason) { + return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD && + reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN && reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; +} + const std::vector>& CSpace::targets() const { return m_targets; } + +std::optional Layout::recalcMonitorReasontoRecalcReason(std::optional reason) { + if (!reason) + return std::nullopt; + + switch (reason.value()) { + case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE; + case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE; + case CLayoutManager::RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD: return RECALCULATE_REASON_HYPRCTL_KEYWORD; + case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN: return RECALCULATE_REASON_TOGGLE_FULLSCREEN; + default: return std::nullopt; + } +} \ No newline at end of file diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 13749a479..5620e100b 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -14,14 +14,15 @@ namespace Layout { enum eRecalculateReason : uint8_t { - RECALCULATE_REASON_WORKSPACE_CHANGE, - RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, - RECALCULATE_REASON_HYPRCTL_KEYWORD, - RECALCULATE_REASON_TOGGLE_FULLSCREEN, - RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES, - RECALCULATE_REASON_RENDER_MOINTOR, + RECALCULATE_REASON_WORKSPACE_CHANGE, + RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, + RECALCULATE_REASON_HYPRCTL_KEYWORD, + RECALCULATE_REASON_TOGGLE_FULLSCREEN, + RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES, + RECALCULATE_REASON_RENDER_MOINTOR, }; + std::optional recalcMonitorReasontoRecalcReason(std::optional reason); class ITarget; class CAlgorithm; @@ -79,4 +80,6 @@ namespace Layout { // for recalc CHyprSignalListener m_geomUpdateCallback; }; + + bool isHardRecalculateReason(eRecalculateReason reason); }; From f70c7ac039e9bfce8ea7cf1705f828738e5de72e Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 07:55:03 +0200 Subject: [PATCH 08/40] fix bad decisions --- src/layout/LayoutManager.cpp | 4 ++-- .../algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 2 +- src/layout/space/Space.cpp | 8 +++----- src/layout/space/Space.hpp | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index 19833ba3b..9b5618e3a 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -335,10 +335,10 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP reason) { if (m->m_activeSpecialWorkspace) - m->m_activeSpecialWorkspace->m_space->recalculate(recalcMonitorReasontoRecalcReason(reason)); + m->m_activeSpecialWorkspace->m_space->recalculate(reason.has_value() ? recalcMonitorReasontoRecalcReason(reason.value()) : std::nullopt); if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(recalcMonitorReasontoRecalcReason(reason)); + m->m_activeWorkspace->m_space->recalculate(reason.has_value() ? recalcMonitorReasontoRecalcReason(reason.value()) : std::nullopt); } void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) { diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index d4e717e69..b50084f21 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -811,7 +811,7 @@ void CScrollingAlgorithm::recalculate(std::optional reason) // guard against unwanted scrolling viewport moves // (e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) - if (Layout::isHardRecalculateReason(reason.value())) + if ( !reason.has_value() || Layout::isHardRecalculateReason(reason.value())) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index f7c593f06..0182d96d2 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -211,11 +211,9 @@ const std::vector>& CSpace::targets() const { return m_targets; } -std::optional Layout::recalcMonitorReasontoRecalcReason(std::optional reason) { - if (!reason) - return std::nullopt; - - switch (reason.value()) { +std::optional Layout::recalcMonitorReasontoRecalcReason(CLayoutManager::eRecalculateMonitorReason reason) { +// If eRecalculateMonitorReason doesn't have a eRecalculateReason pair, it'll return nullopt + switch (reason) { case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE; case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE; case CLayoutManager::RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD: return RECALCULATE_REASON_HYPRCTL_KEYWORD; diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 5620e100b..889b611c8 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -22,7 +22,7 @@ namespace Layout { RECALCULATE_REASON_RENDER_MOINTOR, }; - std::optional recalcMonitorReasontoRecalcReason(std::optional reason); + std::optional recalcMonitorReasontoRecalcReason(CLayoutManager::eRecalculateMonitorReason reason); class ITarget; class CAlgorithm; From e35827c3cac7947f7761a7be1a128696198d8520 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:00:49 +0200 Subject: [PATCH 09/40] fix includes, clang-format. --- src/desktop/view/Group.hpp | 1 - src/layout/algorithm/Algorithm.hpp | 2 +- src/layout/algorithm/ModeAlgorithm.hpp | 2 +- src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 2 +- src/layout/space/Space.cpp | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 0357a08e4..87b65d549 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -3,7 +3,6 @@ #include "../DesktopTypes.hpp" #include "../../helpers/math/Direction.hpp" -#include #include namespace Layout { diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp index 24701d0ec..4729a5be0 100644 --- a/src/layout/algorithm/Algorithm.hpp +++ b/src/layout/algorithm/Algorithm.hpp @@ -5,7 +5,7 @@ #include "../../helpers/memory/Memory.hpp" #include "../LayoutManager.hpp" -#include "layout/space/Space.hpp" +#include "../space/Space.hpp" #include #include diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp index 08de09eb4..0077b56e2 100644 --- a/src/layout/algorithm/ModeAlgorithm.hpp +++ b/src/layout/algorithm/ModeAlgorithm.hpp @@ -5,7 +5,7 @@ #include "../../helpers/memory/Memory.hpp" #include "../LayoutManager.hpp" -#include "layout/space/Space.hpp" +#include "../space/Space.hpp" #include diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index b50084f21..14423bcf3 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -811,7 +811,7 @@ void CScrollingAlgorithm::recalculate(std::optional reason) // guard against unwanted scrolling viewport moves // (e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) - if ( !reason.has_value() || Layout::isHardRecalculateReason(reason.value())) + if (!reason.has_value() || Layout::isHardRecalculateReason(reason.value())) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index 0182d96d2..a9d7943f6 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -212,7 +212,7 @@ const std::vector>& CSpace::targets() const { } std::optional Layout::recalcMonitorReasontoRecalcReason(CLayoutManager::eRecalculateMonitorReason reason) { -// If eRecalculateMonitorReason doesn't have a eRecalculateReason pair, it'll return nullopt + // If eRecalculateMonitorReason doesn't have a eRecalculateReason pair, it'll return nullopt switch (reason) { case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE; case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE; From 6d567729e71aace60fa9b727e8d30049dce5b9c8 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:12:05 +0200 Subject: [PATCH 10/40] remove TODO --- src/layout/LayoutManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index 9b5618e3a..cc558ff56 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -332,7 +332,6 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP reason) { if (m->m_activeSpecialWorkspace) m->m_activeSpecialWorkspace->m_space->recalculate(reason.has_value() ? recalcMonitorReasontoRecalcReason(reason.value()) : std::nullopt); From 424f65f4777dcea7b665d57524c050222876a634 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:07:09 +0200 Subject: [PATCH 11/40] `focuswindow` no longer moves scrolling view when `follow_focus = 0` --- src/desktop/state/FocusState.hpp | 1 + src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index b294c375b..9b6999c81 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -10,6 +10,7 @@ namespace Desktop { FOCUS_REASON_UNKNOWN = 0, FOCUS_REASON_FFM, FOCUS_REASON_KEYBIND, + FOCUS_REASON_DISPATCH_FOCUSWINDOW, FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 14423bcf3..6f42ce31b 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -535,7 +535,11 @@ CScrollingAlgorithm::CScrollingAlgorithm() { if (!TARGET || TARGET->floating()) return; - focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); + // if follow_focus != 0, focuswindow always moves scrolling view + if (*PFOLLOW_FOCUS && reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW) + focusOnInput(TARGET,INPUT_MODE_HARD); + else + focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); }); // Initialize default widths and direction From 0b1520d24e855731060ca46979dd83ee7e9b7d4e Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:00:00 +0200 Subject: [PATCH 12/40] fix: changes in a group's current window no longer casues scrolling view shift when `follow_focus = 0` --- src/desktop/state/FocusState.hpp | 1 + src/desktop/view/Group.cpp | 2 +- .../algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 8 +++++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index 9b6999c81..29f549865 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -14,6 +14,7 @@ namespace Desktop { FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, + FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE, FOCUS_REASON_WORKSPACE_CHANGE, FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE, FOCUS_REASON_UNMAP_WINDOW_TILING, diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index 1287a4733..b0d463559 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -219,7 +219,7 @@ void CGroup::setCurrent(size_t idx) { } if (WASFOCUS) - Desktop::focusState()->rawWindowFocus(current(), FOCUS_REASON_DESKTOP_STATE_CHANGE); + Desktop::focusState()->rawWindowFocus(current(), FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE); } void CGroup::setCurrent(PHLWINDOW w) { diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 6f42ce31b..4ff99dd69 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -536,7 +536,8 @@ CScrollingAlgorithm::CScrollingAlgorithm() { return; // if follow_focus != 0, focuswindow always moves scrolling view - if (*PFOLLOW_FOCUS && reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW) + // if follow_focus != 0, change in a group's current window state always moves scrolling view + if (*PFOLLOW_FOCUS && (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE)) focusOnInput(TARGET,INPUT_MODE_HARD); else focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); @@ -813,8 +814,9 @@ void CScrollingAlgorithm::recalculate(std::optional reason) if (TARGETDATA && !m_scrollingData->visible(TARGETDATA->column.lock(), true)) { - // guard against unwanted scrolling viewport moves - // (e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) + /* guard against unwanted scrolling viewport moves - If recalculate() was called, it is assumed that either the INPUT_MODE will be HARD (i.e. it is meant to move the scrolling viewport) or + it is not meant to move the scrolling viewport. + (e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) */ if (!reason.has_value() || Layout::isHardRecalculateReason(reason.value())) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } From 84d8b2a9ae3e26d7ad1fe0839963ee65c0c348fb Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:25:48 +0200 Subject: [PATCH 13/40] fix: `moveintogroup`, `movewindoworgroup` no longer causes scrolling view change if `follow_focus = 0` --- src/desktop/state/FocusState.hpp | 1 + src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index 29f549865..108b6f804 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -11,6 +11,7 @@ namespace Desktop { FOCUS_REASON_FFM, FOCUS_REASON_KEYBIND, FOCUS_REASON_DISPATCH_FOCUSWINDOW, + FOCUS_REASON_DISPATCH_MOVEINTOGROUP, FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 4ff99dd69..59fb0f80d 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -537,7 +537,8 @@ CScrollingAlgorithm::CScrollingAlgorithm() { // if follow_focus != 0, focuswindow always moves scrolling view // if follow_focus != 0, change in a group's current window state always moves scrolling view - if (*PFOLLOW_FOCUS && (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE)) + // if follow_focus != 0, moving a window into group via the corresponding dispatches `moveintogroup`, `movewindoworgroup` always moves scrolling view + if (*PFOLLOW_FOCUS && (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE || reason == Desktop::FOCUS_REASON_DISPATCH_MOVEINTOGROUP)) focusOnInput(TARGET,INPUT_MODE_HARD); else focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); From 1aa4cbadb749b3f46f5f41fb23cd25f028736128 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:18:29 +0200 Subject: [PATCH 14/40] clang-format --- .../algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 59fb0f80d..af2976c8e 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -538,9 +538,11 @@ CScrollingAlgorithm::CScrollingAlgorithm() { // if follow_focus != 0, focuswindow always moves scrolling view // if follow_focus != 0, change in a group's current window state always moves scrolling view // if follow_focus != 0, moving a window into group via the corresponding dispatches `moveintogroup`, `movewindoworgroup` always moves scrolling view - if (*PFOLLOW_FOCUS && (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE || reason == Desktop::FOCUS_REASON_DISPATCH_MOVEINTOGROUP)) - focusOnInput(TARGET,INPUT_MODE_HARD); - else + if (*PFOLLOW_FOCUS && + (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE || + reason == Desktop::FOCUS_REASON_DISPATCH_MOVEINTOGROUP)) + focusOnInput(TARGET, INPUT_MODE_HARD); + else focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); }); From 93b23336c96b3505687e15db967ce3deff75894b Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 16 Apr 2026 11:41:39 +0200 Subject: [PATCH 15/40] add `RECALCULATE_REASON_UNKNOWN`, `RECALCULATE_MONITOR_REASON_UNKNOWN`, `REMOVE_FROM_GROUP_REASON_UNKNOWN` remove optional wrapper from their enums, refactor code to work with the change --- src/desktop/view/Group.cpp | 2 +- src/desktop/view/Group.hpp | 3 ++- src/layout/LayoutManager.cpp | 6 +++--- src/layout/LayoutManager.hpp | 3 ++- src/layout/algorithm/Algorithm.cpp | 2 +- src/layout/algorithm/Algorithm.hpp | 2 +- src/layout/algorithm/FloatingAlgorithm.cpp | 2 +- src/layout/algorithm/FloatingAlgorithm.hpp | 2 +- src/layout/algorithm/ModeAlgorithm.hpp | 2 +- src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp | 2 +- src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp | 4 ++-- src/layout/algorithm/tiled/master/MasterAlgorithm.cpp | 2 +- src/layout/algorithm/tiled/master/MasterAlgorithm.hpp | 4 ++-- src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp | 2 +- src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp | 2 +- src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 4 ++-- src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp | 2 +- src/layout/space/Space.cpp | 6 +++--- src/layout/space/Space.hpp | 5 +++-- 19 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/desktop/view/Group.cpp b/src/desktop/view/Group.cpp index b0d463559..7073672a4 100644 --- a/src/desktop/view/Group.cpp +++ b/src/desktop/view/Group.cpp @@ -122,7 +122,7 @@ void CGroup::add(PHLWINDOW w) { m_target->recalc(); } -void CGroup::remove(PHLWINDOW w, Math::eDirection dir, std::optional reason) { +void CGroup::remove(PHLWINDOW w, Math::eDirection dir, eRemoveFromGroupReason reason) { std::optional idx; for (size_t i = 0; i < m_windows.size(); ++i) { if (m_windows.at(i) == w) { diff --git a/src/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 87b65d549..3ada4d6ef 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -16,13 +16,14 @@ namespace Desktop::View { ~CGroup(); enum eRemoveFromGroupReason : uint8_t { + REMOVE_FROM_GROUP_REASON_UNKNOWN, // when the remove from group reason is unknown or not important to preserve REMOVE_FROM_GROUP_REASON_UNMAP_WINDOW, }; bool has(PHLWINDOW w) const; void add(PHLWINDOW w); - void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT, std::optional reason = std::nullopt); + void remove(PHLWINDOW w, Math::eDirection dir = Math::DIRECTION_DEFAULT, eRemoveFromGroupReason reason = REMOVE_FROM_GROUP_REASON_UNKNOWN); void moveCurrent(bool next); void setCurrent(size_t idx); void setCurrent(PHLWINDOW w); diff --git a/src/layout/LayoutManager.cpp b/src/layout/LayoutManager.cpp index cc558ff56..f453dfeed 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -332,12 +332,12 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP reason) { +void CLayoutManager::recalculateMonitor(PHLMONITOR m, eRecalculateMonitorReason reason) { if (m->m_activeSpecialWorkspace) - m->m_activeSpecialWorkspace->m_space->recalculate(reason.has_value() ? recalcMonitorReasontoRecalcReason(reason.value()) : std::nullopt); + m->m_activeSpecialWorkspace->m_space->recalculate(recalcMonitorReasonToRecalcReason(reason)); if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(reason.has_value() ? recalcMonitorReasontoRecalcReason(reason.value()) : std::nullopt); + m->m_activeWorkspace->m_space->recalculate(recalcMonitorReasonToRecalcReason(reason)); } void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) { diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index abc704516..02c81d143 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -46,6 +46,7 @@ namespace Layout { ~CLayoutManager() = default; enum eRecalculateMonitorReason : uint8_t { + RECALCULATE_MONITOR_REASON_UNKNOWN, // when the recalculate monitor reason is unknown or not important to preserve RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE, RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE, RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD, @@ -83,7 +84,7 @@ namespace Layout { void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP target, eMouseBindMode mode, int corner, const Vector2D& beginSize); void invalidateMonitorGeometries(PHLMONITOR); - void recalculateMonitor(PHLMONITOR, std::optional reason = std::nullopt); + void recalculateMonitor(PHLMONITOR, eRecalculateMonitorReason reason = RECALCULATE_MONITOR_REASON_UNKNOWN); const UP& dragController(); diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp index 3867d352f..126aa9f8c 100644 --- a/src/layout/algorithm/Algorithm.cpp +++ b/src/layout/algorithm/Algorithm.cpp @@ -101,7 +101,7 @@ size_t CAlgorithm::floatingTargets() const { return m_floatingTargets.size(); } -void CAlgorithm::recalculate(std::optional reason) { +void CAlgorithm::recalculate(eRecalculateReason reason) { m_tiled->recalculate(reason); m_floating->recalculate(reason); diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp index 4729a5be0..9ab0ac62d 100644 --- a/src/layout/algorithm/Algorithm.hpp +++ b/src/layout/algorithm/Algorithm.hpp @@ -35,7 +35,7 @@ namespace Layout { Config::ErrorResult layoutMsg(const std::string_view& sv); std::optional predictSizeForNewTiledTarget(); - void recalculate(std::optional reason = std::nullopt); + void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); void recenter(SP t); void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); diff --git a/src/layout/algorithm/FloatingAlgorithm.cpp b/src/layout/algorithm/FloatingAlgorithm.cpp index f3c286277..ed9d2c463 100644 --- a/src/layout/algorithm/FloatingAlgorithm.cpp +++ b/src/layout/algorithm/FloatingAlgorithm.cpp @@ -4,7 +4,7 @@ using namespace Layout; -void IFloatingAlgorithm::recalculate(std::optional reason) { +void IFloatingAlgorithm::recalculate(eRecalculateReason reason) { ; } diff --git a/src/layout/algorithm/FloatingAlgorithm.hpp b/src/layout/algorithm/FloatingAlgorithm.hpp index 936f00b59..9232888d2 100644 --- a/src/layout/algorithm/FloatingAlgorithm.hpp +++ b/src/layout/algorithm/FloatingAlgorithm.hpp @@ -22,7 +22,7 @@ namespace Layout { virtual void recenter(SP t); - virtual void recalculate(std::optional reason = std::nullopt); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); protected: IFloatingAlgorithm() = default; diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp index 0077b56e2..ac43c4a77 100644 --- a/src/layout/algorithm/ModeAlgorithm.hpp +++ b/src/layout/algorithm/ModeAlgorithm.hpp @@ -31,7 +31,7 @@ namespace Layout { virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE) = 0; // recalculate layout - virtual void recalculate(std::optional reason = std::nullopt) = 0; + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN) = 0; // swap targets virtual void swapTargets(SP a, SP b) = 0; diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp index c8509da5d..ff1b1b4f2 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp @@ -488,7 +488,7 @@ void CDwindleAlgorithm::swapTargets(SP a, SP b) { nodeB->pTarget = a; } -void CDwindleAlgorithm::recalculate(std::optional reason) { +void CDwindleAlgorithm::recalculate(eRecalculateReason reason) { calculateWorkspace(); } diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index 85eea2fcf..45e857277 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp @@ -34,8 +34,8 @@ namespace Layout::Tiled { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(std::optional reason = std::nullopt); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp index abaf05d75..3294f2547 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.cpp @@ -424,7 +424,7 @@ void CMasterAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir } } -void CMasterAlgorithm::recalculate(std::optional reason) { +void CMasterAlgorithm::recalculate(eRecalculateReason reason) { calculateWorkspace(); } diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp index cd8fa278a..dc1822681 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp @@ -52,8 +52,8 @@ namespace Layout::Tiled { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(std::optional reason = std::nullopt); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp index c3e80053a..e619bf572 100644 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp +++ b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp @@ -125,7 +125,7 @@ void CMonocleAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRe // monocle layout doesn't support manual resizing, all windows are fullscreen } -void CMonocleAlgorithm::recalculate(std::optional reason) { +void CMonocleAlgorithm::recalculate(eRecalculateReason reason) { if (m_targetDatas.empty()) return; diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp index 60e52bd3e..ec3cc83d4 100644 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp @@ -26,7 +26,7 @@ namespace Layout::Tiled { virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(std::optional reason = std::nullopt); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index af2976c8e..14904dc1b 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -804,7 +804,7 @@ void CScrollingAlgorithm::resizeTarget(const Vector2D& delta, SP target m_scrollingData->recalculate(true); } -void CScrollingAlgorithm::recalculate(std::optional reason) { +void CScrollingAlgorithm::recalculate(eRecalculateReason reason) { // guard against recalculation during transitional monitor states // (e.g. monitor reconnecting after suspend where workspace/monitor may not be ready) if (!m_parent || !m_parent->space() || !m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor) @@ -820,7 +820,7 @@ void CScrollingAlgorithm::recalculate(std::optional reason) /* guard against unwanted scrolling viewport moves - If recalculate() was called, it is assumed that either the INPUT_MODE will be HARD (i.e. it is meant to move the scrolling viewport) or it is not meant to move the scrolling viewport. (e.g. changing workspace to a scrolling layout workspace fits the focused window in that workspace into view) */ - if (!reason.has_value() || Layout::isHardRecalculateReason(reason.value())) + if (Layout::isHardRecalculateReason(reason)) focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); } } diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp index 7addcd0ea..5f9456968 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp @@ -99,7 +99,7 @@ namespace Layout::Tiled { virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(std::optional reason = std::nullopt); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index a9d7943f6..f3ae50895 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -146,7 +146,7 @@ SP CSpace::algorithm() const { return m_algorithm; } -void CSpace::recalculate(std::optional reason) { +void CSpace::recalculate(eRecalculateReason reason) { recheckWorkArea(); if (m_algorithm) @@ -211,13 +211,13 @@ const std::vector>& CSpace::targets() const { return m_targets; } -std::optional Layout::recalcMonitorReasontoRecalcReason(CLayoutManager::eRecalculateMonitorReason reason) { +eRecalculateReason Layout::recalcMonitorReasonToRecalcReason(CLayoutManager::eRecalculateMonitorReason reason) { // If eRecalculateMonitorReason doesn't have a eRecalculateReason pair, it'll return nullopt switch (reason) { case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE; case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE; case CLayoutManager::RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD: return RECALCULATE_REASON_HYPRCTL_KEYWORD; case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN: return RECALCULATE_REASON_TOGGLE_FULLSCREEN; - default: return std::nullopt; + default: return RECALCULATE_REASON_UNKNOWN; } } \ No newline at end of file diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 889b611c8..87f80ce1b 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -14,6 +14,7 @@ namespace Layout { enum eRecalculateReason : uint8_t { + RECALCULATE_REASON_UNKNOWN, // when the recalculate reason is unknown or not important to preserve RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, RECALCULATE_REASON_HYPRCTL_KEYWORD, @@ -22,7 +23,7 @@ namespace Layout { RECALCULATE_REASON_RENDER_MOINTOR, }; - std::optional recalcMonitorReasontoRecalcReason(CLayoutManager::eRecalculateMonitorReason reason); + eRecalculateReason recalcMonitorReasonToRecalcReason(CLayoutManager::eRecalculateMonitorReason reason); class ITarget; class CAlgorithm; @@ -46,7 +47,7 @@ namespace Layout { void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - void recalculate(std::optional reason = std::nullopt); + void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); void toggleTargetFloating(SP t); From 909829ce4b0f1259a1dc0c233669dcadb0704663 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Tue, 21 Apr 2026 21:08:15 +0200 Subject: [PATCH 16/40] dispatches that call `switchtoWindow()` in CKeybindManager no longer cause scrolling view to move if `follow_focus = 0`. They still cause the move if `follow_focus = 1` Dispatches effected by this: `movefocus`, `cyclenext`, `focuscurrentorlast`, `focuscurrentorurgent` --- src/desktop/state/FocusState.cpp | 2 +- src/desktop/state/FocusState.hpp | 2 ++ src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/desktop/state/FocusState.cpp b/src/desktop/state/FocusState.cpp index d46a59e0f..e97be4c09 100644 --- a/src/desktop/state/FocusState.cpp +++ b/src/desktop/state/FocusState.cpp @@ -302,5 +302,5 @@ void CFocusState::resetWindowFocus() { bool Desktop::isHardInputFocusReason(eFocusReason r) { return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE || - r == FOCUS_REASON_UNMAP_WINDOW_TILING; + r == FOCUS_REASON_UNMAP_WINDOW_TILING || r == FOCUS_REASON_SWITCH_TO_WINDOW_HARD; } diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index 108b6f804..e99521b3c 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -15,6 +15,8 @@ namespace Desktop { FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, + FOCUS_REASON_SWITCH_TO_WINDOW_SOFT, + FOCUS_REASON_SWITCH_TO_WINDOW_HARD, FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE, FOCUS_REASON_WORKSPACE_CHANGE, FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE, diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 14904dc1b..7c60f2546 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -538,9 +538,10 @@ CScrollingAlgorithm::CScrollingAlgorithm() { // if follow_focus != 0, focuswindow always moves scrolling view // if follow_focus != 0, change in a group's current window state always moves scrolling view // if follow_focus != 0, moving a window into group via the corresponding dispatches `moveintogroup`, `movewindoworgroup` always moves scrolling view + // if follow_focus != 0, moving focus via dispatches that cause switching to a specific window via calling switchToWindow(), such as movefocus, cyclenext, focuscurrentor(last/urgent); always moves scrolling view if (*PFOLLOW_FOCUS && (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE || - reason == Desktop::FOCUS_REASON_DISPATCH_MOVEINTOGROUP)) + reason == Desktop::FOCUS_REASON_DISPATCH_MOVEINTOGROUP || reason == Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_SOFT)) focusOnInput(TARGET, INPUT_MODE_HARD); else focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); From 52a2df5c337491376476fe35c14e28796b053041 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:29:07 +0200 Subject: [PATCH 17/40] Fix for Lua: switchToWondow --- src/config/shared/actions/ConfigActions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp index 5db1aaf1e..b188ea046 100644 --- a/src/config/shared/actions/ConfigActions.cpp +++ b/src/config/shared/actions/ConfigActions.cpp @@ -60,10 +60,10 @@ static void switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCycle = fals g_pInputManager->unconstrainMouse(); if (PLASTWINDOW && PLASTWINDOW->m_workspace == PWINDOWTOCHANGETO->m_workspace && PLASTWINDOW->isFullscreen()) - Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle); + Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_HARD, nullptr, forceFSCycle); else { updateRelativeCursorCoords(); - Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle); + Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_SOFT, nullptr, forceFSCycle); PWINDOWTOCHANGETO->warpCursor(); if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) { From 9b1ad1524e5ee234270866cb3179c1e9b488f767 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:38:12 +0200 Subject: [PATCH 18/40] Fix for Lua: focus({window}) --- src/config/shared/actions/ConfigActions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp index b188ea046..f4e14b0a2 100644 --- a/src/config/shared/actions/ConfigActions.cpp +++ b/src/config/shared/actions/ConfigActions.cpp @@ -430,7 +430,7 @@ ActionResult Actions::focus(PHLWINDOW window) { Desktop::focusState()->monitor()->m_activeSpecialWorkspace != window->m_workspace) // NOLINTNEXTLINE Actions::changeWorkspace(PWORKSPACE); - Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_KEYBIND, nullptr, false); + Desktop::focusState()->fullWindowFocus(window, Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW, nullptr, false); window->warpCursor(); return {}; From 124990be7f52c93ce8d05f76c59d71f58e7a6418 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:38:33 +0200 Subject: [PATCH 19/40] Fix for Lua: Move Window Into Group --- src/config/shared/actions/ConfigActions.cpp | 2 +- src/desktop/state/FocusState.hpp | 2 +- src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp index f4e14b0a2..c86ccbf58 100644 --- a/src/config/shared/actions/ConfigActions.cpp +++ b/src/config/shared/actions/ConfigActions.cpp @@ -1254,7 +1254,7 @@ static void moveWindowIntoGroupHelper(PHLWINDOW pWindow, PHLWINDOW pWindowInDire pWindowInDirection->m_group->add(pWindow); pWindowInDirection->m_group->setCurrent(pWindow); pWindow->updateWindowDecos(); - Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND); + Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP); pWindow->warpCursor(); g_pEventManager->postEvent(SHyprIPCEvent{.event = "moveintogroup", .data = std::format("{:x}", rc(pWindow.get()))}); diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index e99521b3c..334c426d6 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -11,7 +11,7 @@ namespace Desktop { FOCUS_REASON_FFM, FOCUS_REASON_KEYBIND, FOCUS_REASON_DISPATCH_FOCUSWINDOW, - FOCUS_REASON_DISPATCH_MOVEINTOGROUP, + FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP, FOCUS_REASON_CLICK, FOCUS_REASON_OTHER, FOCUS_REASON_DESKTOP_STATE_CHANGE, diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp index 7c60f2546..9c2f4ba88 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -541,7 +541,7 @@ CScrollingAlgorithm::CScrollingAlgorithm() { // if follow_focus != 0, moving focus via dispatches that cause switching to a specific window via calling switchToWindow(), such as movefocus, cyclenext, focuscurrentor(last/urgent); always moves scrolling view if (*PFOLLOW_FOCUS && (reason == Desktop::FOCUS_REASON_DISPATCH_FOCUSWINDOW || reason == Desktop::FOCUS_REASON_GROUP_CURRENT_WINDOW_CHANGE || - reason == Desktop::FOCUS_REASON_DISPATCH_MOVEINTOGROUP || reason == Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_SOFT)) + reason == Desktop::FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP || reason == Desktop::FOCUS_REASON_SWITCH_TO_WINDOW_SOFT)) focusOnInput(TARGET, INPUT_MODE_HARD); else focusOnInput(TARGET, reason == Desktop::FOCUS_REASON_CLICK ? INPUT_MODE_CLICK : (Desktop::isHardInputFocusReason(reason) ? INPUT_MODE_HARD : INPUT_MODE_SOFT)); From d826c5390e2bef0e60e28c921c25c72a37f180b2 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:53:07 +0200 Subject: [PATCH 20/40] clang-format --- src/layout/LayoutManager.hpp | 4 ++-- src/layout/algorithm/Algorithm.hpp | 4 ++-- src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp | 6 +++--- src/layout/algorithm/tiled/master/MasterAlgorithm.hpp | 6 +++--- src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp | 4 ++-- src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp | 4 ++-- src/layout/space/Space.hpp | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index 02c81d143..3bb44437b 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -83,8 +83,8 @@ namespace Layout { void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP target, eMouseBindMode mode, int corner, const Vector2D& beginSize); - void invalidateMonitorGeometries(PHLMONITOR); - void recalculateMonitor(PHLMONITOR, eRecalculateMonitorReason reason = RECALCULATE_MONITOR_REASON_UNKNOWN); + void invalidateMonitorGeometries(PHLMONITOR); + void recalculateMonitor(PHLMONITOR, eRecalculateMonitorReason reason = RECALCULATE_MONITOR_REASON_UNKNOWN); const UP& dragController(); diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp index 9ab0ac62d..57d824a93 100644 --- a/src/layout/algorithm/Algorithm.hpp +++ b/src/layout/algorithm/Algorithm.hpp @@ -35,8 +35,8 @@ namespace Layout { Config::ErrorResult layoutMsg(const std::string_view& sv); std::optional predictSizeForNewTiledTarget(); - void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); - void recenter(SP t); + void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); + void recenter(SP t); void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); void moveTarget(const Vector2D& Δ, SP target); diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index 45e857277..fd277a5c2 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp @@ -30,9 +30,9 @@ namespace Layout::Tiled { CDwindleAlgorithm() = default; virtual ~CDwindleAlgorithm() = default; - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); + virtual void newTarget(SP target); + virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); + virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp index dc1822681..859c01aa2 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp @@ -48,9 +48,9 @@ namespace Layout::Tiled { CMasterAlgorithm() = default; virtual ~CMasterAlgorithm() = default; - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); + virtual void newTarget(SP target); + virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); + virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); diff --git a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp index ec3cc83d4..072f9cf09 100644 --- a/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp @@ -25,8 +25,8 @@ namespace Layout::Tiled { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp index 5f9456968..8d864d53c 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp @@ -98,8 +98,8 @@ namespace Layout::Tiled { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 87f80ce1b..7300e5b89 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -47,7 +47,7 @@ namespace Layout { void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); + void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); void toggleTargetFloating(SP t); From b0bbf03c60697f0256872ab7e871341aa21bb5bc Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:30:25 +0200 Subject: [PATCH 21/40] Drop `hyprctl keyword` change --- src/config/legacy/ConfigManager.cpp | 2 +- src/layout/LayoutManager.hpp | 1 - src/layout/space/Space.cpp | 3 +-- src/layout/space/Space.hpp | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/config/legacy/ConfigManager.cpp b/src/config/legacy/ConfigManager.cpp index f97fde7e9..2705c3959 100644 --- a/src/config/legacy/ConfigManager.cpp +++ b/src/config/legacy/ConfigManager.cpp @@ -1096,7 +1096,7 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std:: // invalidate layouts if they changed if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) { for (auto const& m : g_pCompositor->m_monitors) { - g_layoutManager->recalculateMonitor(m, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD); + g_layoutManager->recalculateMonitor(m); } } diff --git a/src/layout/LayoutManager.hpp b/src/layout/LayoutManager.hpp index 3bb44437b..c78ad30e6 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -49,7 +49,6 @@ namespace Layout { RECALCULATE_MONITOR_REASON_UNKNOWN, // when the recalculate monitor reason is unknown or not important to preserve RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE, RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE, - RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD, RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN, }; diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index f3ae50895..827f39a72 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -203,7 +203,7 @@ SP CSpace::getNextCandidate(SP old) { } bool Layout::isHardRecalculateReason(eRecalculateReason reason) { - return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_HYPRCTL_KEYWORD && + return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN && reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; } @@ -216,7 +216,6 @@ eRecalculateReason Layout::recalcMonitorReasonToRecalcReason(CLayoutManager::eRe switch (reason) { case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE: return RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE; case CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE: return RECALCULATE_REASON_WORKSPACE_CHANGE; - case CLayoutManager::RECALCULATE_MONITOR_REASON_HYPRCTL_KEYWORD: return RECALCULATE_REASON_HYPRCTL_KEYWORD; case CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_FULLSCREEN: return RECALCULATE_REASON_TOGGLE_FULLSCREEN; default: return RECALCULATE_REASON_UNKNOWN; } diff --git a/src/layout/space/Space.hpp b/src/layout/space/Space.hpp index 7300e5b89..6f7236966 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -17,7 +17,6 @@ namespace Layout { RECALCULATE_REASON_UNKNOWN, // when the recalculate reason is unknown or not important to preserve RECALCULATE_REASON_WORKSPACE_CHANGE, RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE, - RECALCULATE_REASON_HYPRCTL_KEYWORD, RECALCULATE_REASON_TOGGLE_FULLSCREEN, RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES, RECALCULATE_REASON_RENDER_MOINTOR, From 57ef1ea9da2fad175b7d58468e1bf001eb4951ca Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 00:24:46 +0200 Subject: [PATCH 22/40] test: `focus({window})` with `follow_focus = false` --- hyprtester/src/tests/main/scroll.cpp | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index bd33e345e..02fc37065 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -197,3 +197,59 @@ TEST_CASE(scrollWindowRule) { // not the greatest test, but as long as res and gaps don't change, we good. EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036"); } + + + + +TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { + + + /* + focuswindow DOES NOT move the scrolling view when follow_focus = 0 + --------------------------------------------------------------------------------- + */ + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD NOT move scrolling view when follow_focus = false", Colors::GREEN); + + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("Could not spawn kitty with win class `a`"); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("Could not spawn kitty with win class `b`"); + } + + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // if the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport + const std::string posA = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const int posAx = std::stoi(posA.substr(4, posA.find(',') - 4)); + if (posAx < 0) { + NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::GREEN, Colors::RESET, posAx); + } else { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, posAx); + } + + // clean up + + // revert the changes made to config + NLog::log("{}Restoring config state", Colors::YELLOW); + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} + + + From 655cd14f5c7d85aab7f5d92162459bf2a695d03a Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 00:37:00 +0200 Subject: [PATCH 23/40] test: `focus({window})` with `follow_focus = true` --- hyprtester/src/tests/main/scroll.cpp | 46 +++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 02fc37065..c1f8985fe 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -205,7 +205,7 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { /* - focuswindow DOES NOT move the scrolling view when follow_focus = 0 + focuswindow DOES NOT move the scrolling view when follow_focus = false --------------------------------------------------------------------------------- */ @@ -252,4 +252,48 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { } +TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { + + /* + focuswindow DOES move the view when follow_focus = true + -------------------------------------------------------------------- + */ + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN); + + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("Could not spawn kitty with win class `a`"); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("Could not spawn kitty with win class `b`"); + } + + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + + // If the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport. + // If it is not, the view moved, which is what we expect to happen. + const std::string posA = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const int posAx = std::stoi(posA.substr(4, posA.find(',') - 4)); + if (posAx < 0) { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::RED, Colors::RESET, posAx); + } else { + NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::GREEN, Colors::RESET, posAx); + } + + // clean up + + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} From 2d466c60cdda2097f807888db229ea0dd17f4e09 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 00:47:34 +0200 Subject: [PATCH 24/40] test: test Focus Fallback --- hyprtester/src/tests/main/scroll.cpp | 75 +++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index c1f8985fe..0264e3346 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -264,7 +264,7 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN); - + if (!Tests::spawnKitty("a")) { FAIL_TEST("Could not spawn kitty with win class `a`"); } @@ -297,3 +297,76 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); } + + +TEST_CASE(testScrollingViewBehaviourFocusFallback) { + + /* + Focus fallback from killing a floating window onto a tiled window must NOT move scrolling view, regardless of follow_focus + -------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a tiled window should not move scrolling view", Colors::GREEN); + + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with class `c`", Colors::RED); + } + + // make it (window of class:c) float - the view now mush have shifted to fit window class:b + OK(getFromSocket("/dispatch hl.dsp.window.float({action = 'enable', window = 'class:c'})")); + + // establish focus history + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + + + + // kill the floating window + // Expect the focus to fall back to the left tiled window + OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})")); + Tests::waitUntilWindowsN(2); + + // The focus now must have fallen back to tiled window of class "a". + + // If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport) + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); +} + + From 73dff984b69a44ce2a5514049ca3c01cfd29f7a2 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 01:02:21 +0200 Subject: [PATCH 25/40] test: Focus Fallback with groups --- hyprtester/src/tests/main/scroll.cpp | 71 ++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 0264e3346..1570ba2ef 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -370,3 +370,74 @@ TEST_CASE(testScrollingViewBehaviourFocusFallback) { } + +TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { + + // same idea as testScrollingViewBehaviourFocusFallback, but with window of class "a" being grouped. + + NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a grouped tiled should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + // ensure variables are correctly set for the test + + // to correctly set up windows for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + // only one tiled window will be grouped for the test + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + // make it a grouped. There need not be any other windows in the group for this test + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with class `c`", Colors::RED); + } + + // make it float - the view now mush have shifted to fit window class:b + OK(getFromSocket("/dispatch hl.dsp.window.float({action = 'enable', window = 'class:c'})")); + + // establish focus history + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); + + // kill the floating window + OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})")); + Tests::waitUntilWindowsN(2); + + // The focus now must have fallen back to tiled window of class "a". + + // If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport) + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + +} + From bc5589f61a2f9b1ce9f951d2dbbb891d43f1b4b1 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 01:11:13 +0200 Subject: [PATCH 26/40] test: workspace change --- hyprtester/src/tests/main/scroll.cpp | 61 +++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 1570ba2ef..553ef94ca 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -433,7 +433,6 @@ TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { // clean up - // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); @@ -441,3 +440,63 @@ TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { } +TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { + + /* + When you change to a scrolling workspace, the focused window in that workspace must not be pulled into view, regardless of follow_focus + --------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: changing to a scrolling workspace should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + + // switch to workspace 1 for this test + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); + + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + + // change to workspace 2, then back to workspace 1 again + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})")); + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + +} + From 13971173e1f201e3798b68a3f744e6fce697fe06 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 01:26:32 +0200 Subject: [PATCH 27/40] test: special workspace toggle --- hyprtester/src/tests/main/scroll.cpp | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 553ef94ca..59f71b7be 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -500,3 +500,68 @@ TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { } + + +TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { + + /* + When you change to a special scrolling workspace from a normal workspace, the focused window in that workspace must not be pulled into view, regardless of follow_focus + ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from a normal workspace should not move scrolling view", Colors::GREEN); + + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + // We'll test in this special workspace + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + + + // change to workspace 2, then back to special "scroll_S" workspace again + OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})")); + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + // Reestablish focus since it is finnicky in hyprtester - Harmless and does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); +} + + + From 1937203214df26e98d5e2abcf7ef73d57dc18feb Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:41:43 +0200 Subject: [PATCH 28/40] test: workspace change - special to special also minor refactoring --- hyprtester/src/tests/main/scroll.cpp | 83 ++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 59f71b7be..e8cd0dc92 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -209,11 +209,9 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { --------------------------------------------------------------------------------- */ - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - - NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD NOT move scrolling view when follow_focus = false", Colors::GREEN); - + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); // ensure variables are correctly set for the test OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); @@ -259,10 +257,10 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { focuswindow DOES move the view when follow_focus = true -------------------------------------------------------------------- */ - - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - + NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); if (!Tests::spawnKitty("a")) { @@ -366,7 +364,7 @@ TEST_CASE(testScrollingViewBehaviourFocusFallback) { // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } @@ -436,7 +434,7 @@ TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } @@ -495,7 +493,7 @@ TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } @@ -560,8 +558,71 @@ TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } +TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { + + + /* + We also test switching between 2 special workspaces + This follows the same idea and dependencies as the test testScrollingViewBehaviourSpecialWorkspaceChange() + */ + + NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from another special workspace should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + + // We'll test in this special workspace + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // change to special workspace "scroll_F", then back to special "scroll_S" workspace again + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_F')")); + OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); + + // Reestablish focus since it is finnicky in hyprtester - Harmless and does not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + + const std::string currentWindowPosSPECIAL = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosSPECIALX = currentWindowPosSPECIAL.substr(4, currentWindowPosSPECIAL.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosSPECIALX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosSPECIALX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosSPECIALX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); + +} \ No newline at end of file From 503997f865a4bcc88fe499f54ced15892932a5e4 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 00:44:48 +0200 Subject: [PATCH 29/40] test: close window in a group --- hyprtester/src/tests/main/scroll.cpp | 71 ++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index e8cd0dc92..3412278c2 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -625,4 +625,75 @@ TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); +} + + +TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { + + + /* + When you change close a window inside a group (NOT destroying the group!), it should not cause scrolling view to shift to pull that group into view, regardless of follow_focus + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + + + NLog::log("{}Testing scrolling view behaviour: closing a window in a group (> 1 window in group) should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + // ensure variables are correctly set for the test + + // this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + // We need 2 windows to be grouped, the third one not. + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // switch focus to group. This will not move view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // kill window class:b. we expect that this should cause not difference in the position of the group + OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:b'})")); + Tests::waitUntilWindowsN(2); + + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); + } \ No newline at end of file From 441ac3e2901a56d94b7f1cc99969ef2783911201 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 00:54:52 +0200 Subject: [PATCH 30/40] test: move window into group `follow_focus = false` minor fix in test: expect -> assert --- hyprtester/src/tests/main/scroll.cpp | 61 +++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 3412278c2..3b0aa42fe 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -195,7 +195,7 @@ TEST_CASE(scrollWindowRule) { ASSERT(Tests::windowCount(), 2); // not the greatest test, but as long as res and gaps don't change, we good. - EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036"); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036"); } @@ -696,4 +696,63 @@ TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); +} + + +TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) { + + /* + when a window is moved inside a group, scrolling view should not move to fit that group when follow_focus = false + ----------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: moving a window into a group SHOULD NOT move scrolling view if follow_focus = 0", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // the focus now should still be on class:b window. If the view did not move, its x coordinate for its `at:` value should be <0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'b' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'b' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); + } \ No newline at end of file From fdff86c08c627a42be39cac851f670bb47c14885 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 01:00:14 +0200 Subject: [PATCH 31/40] test: move window into group `follow_focus = true` --- hyprtester/src/tests/main/scroll.cpp | 63 +++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 3b0aa42fe..b44b9f644 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -755,4 +755,65 @@ TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) { Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); -} \ No newline at end of file +} + + + +TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) { + + /* + when a window is moved inside a group, scrolling view should move to fit that group when follow_focus = true + ------------------------------------------------------------------------------------------------------------ + */ + + NLog::log("{}Testing scrolling view behaviour: moving a window in a group SHOULD move scrolling view if follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // the focus now should still be on class:b window. If the scrolling view did move, its x coordinate for its `at:` value should be >= 0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test fail + if (std::stoi(currentWindowPosX) < 0) { + FAIL_TEST("{}window of class 'b' does not have x coordinates >= 0 for its position: {}", Colors::RED, currentWindowPosX); + } + // test pass + else { + NLog ::log("{}Passed: {}window of class 'b' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + + // clean up + + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} + From 8b9dfa0a5f4959d891eb96eaf1ca10409a5cf816 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 01:07:56 +0200 Subject: [PATCH 32/40] test: new layer --- hyprtester/src/tests/main/scroll.cpp | 62 ++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index b44b9f644..cd16642ad 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -817,3 +817,65 @@ TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) { ASSERT(Tests::windowCount(), 0); } + +TEST_CASE(testScrollingViewBehaviourNewLayer) { + + /* + Starting a program on a different layer shouldn't cause scrolling view to move to fit the window that was focused when this program was started, regardless of follow_focus + --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: new program occupying another layer shouldn't move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // focus class:a - this does not move scrolling view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + + NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, "myLayer"); + if (!Tests::spawnLayerKitty("myLayer")) { + FAIL_TEST("{}Error: {} layer did not spawn", Colors::RED, "myLayer"); + } + + // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + // kill all layers + NLog::log("{}Killing all layers", Colors::YELLOW); + Tests::killAllLayers(); + EXPECT(Tests::layerCount(), 0); +} + From 96baa6fe9f8636a6b8973446eb4766a25fc26b00 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 01:50:15 +0200 Subject: [PATCH 33/40] test: maximise fix in test: expect to assert. --- hyprtester/src/tests/main/scroll.cpp | 59 +++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index cd16642ad..cd7323ae4 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -876,6 +876,63 @@ TEST_CASE(testScrollingViewBehaviourNewLayer) { // kill all layers NLog::log("{}Killing all layers", Colors::YELLOW); Tests::killAllLayers(); - EXPECT(Tests::layerCount(), 0); + ASSERT(Tests::layerCount(), 0); } + +TEST_CASE(testScrollingViewBehaviourMaximise) { + + /* + maximising and then unmaximising a window shouldn't move scrolling view, regardless of follow_focus + --------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: maximising and then unmaximising a window shouldn't move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // focus class:a - this does not move scrolling view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // fullscreen class:a window + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'set', window = 'class:a'})")); + + // unfullscreen class:a window + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'unset', window = 'class:a'})")); + + // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); + +} From d16100b3dead597b70e827a2d986489367a1d3a9 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 02:12:02 +0200 Subject: [PATCH 34/40] test: fullscreen --- hyprtester/src/tests/main/scroll.cpp | 57 ++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index cd7323ae4..d13ef09a6 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -936,3 +936,60 @@ TEST_CASE(testScrollingViewBehaviourMaximise) { ASSERT(Tests::windowCount(), 0); } + +TEST_CASE(testScrollingViewBehaviourFullscreen) { + + /* + This is almost the same as the testScrollingViewBehaviourMaximise() test, just with fullscreen 1 (fullscreen) instead of fullscreen 0 (maximise) + + fullscreening and then unfullscreening a window shouldn't move scrolling view, regardless of follow_focus + */ + + NLog::log("{}Testing scrolling view behaviour: fullscreening and then unfullscreening a window shouldn't move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // focus class:a - this does not move scrolling view when follow_focus = 0 + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); + + // maximise class:a window + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'set', window = 'class:a'})")); + + // unmaximise class:a window + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'unset', window = 'class:a'})")); + + // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} + From c57eb2e14a2d8323818f3bdafe3f6aa1fe3de91e Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 02:31:26 +0200 Subject: [PATCH 35/40] test: `dispatch window.focus({ direction })` when `follow_focus = false` --- hyprtester/src/tests/main/scroll.cpp | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index d13ef09a6..1bd726574 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -993,3 +993,52 @@ TEST_CASE(testScrollingViewBehaviourFullscreen) { ASSERT(Tests::windowCount(), 0); } +TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusFalse) { + + + /* + dispatching movefocus when follow_focus = false should not cause scrolling view to move + --------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus does not cause scrolling view to move if follow_focus = false", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // we expect that after dispatching this, scrolling view must not have moved + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be < 0. + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + EXPECT(Tests::windowCount(), 0); + + +} From fc35134356a28dd3362ffb6d29fbd3ea21d1c07f Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 02:34:10 +0200 Subject: [PATCH 36/40] test: `dispatch window.focus({ direction })` when `follow_focus = true` test-fix: expect -> assert --- hyprtester/src/tests/main/scroll.cpp | 50 ++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 1bd726574..01e3e900e 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -1038,7 +1038,51 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusFalse) { // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - + ASSERT(Tests::windowCount(), 0); } + +TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) { + + /* + dispatching movefocus when follow_focus = true should cause scrolling view to move + ---------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus does cause scrolling view to move if follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + // we expect that after dispatching this, scrolling view must have moved since follow_focus = true + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // If the scrolling view moved, class:a window's x coordinate for its `at:` value should be >= 0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test fail + if (std::stoi(currentWindowPosX) < 0) { + FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + // test pass + else { + NLog ::log("{}Passed: {}window of class 'a' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} \ No newline at end of file From 3df98bc11788586d3e8637a0149eab7cd56eb2bf Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 02:41:05 +0200 Subject: [PATCH 37/40] test: move focus within group when `follow_focus = false` --- hyprtester/src/tests/main/scroll.cpp | 71 +++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 01e3e900e..f364c6a05 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -1085,4 +1085,73 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); -} \ No newline at end of file +} + + +TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { + + /* + When movefocus is dispatched within groups to move focus from one group member to another, scrolling view must not move if follow_focus = false + ----------------------------------------------------------------------------------------------------------------------------------------------- + */ + + NLog::log("{}Testing scrolling view behaviour: movefocus within groups does not cause scrolling view to move if follow_focus = false", Colors::GREEN); + + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + + // ensure variables are correctly set for the test + + // necessary to make sure movefocus first cycles through tabs in a group + OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // we move from one window of a group to another (from class:b to class:a) via movefocus + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // the focus now should still be on class:a window. If the scrolling view did not move, its x coordinate for its `at:` value should be < 0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test pass + if (std::stoi(currentWindowPosX) < 0) { + NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + // test fail + else { + FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} + + + + From 42c3057cc23938dfa3b79e66a08f21415d462545 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 02:43:51 +0200 Subject: [PATCH 38/40] test: move focus within group when `follow_focus = true` --- hyprtester/src/tests/main/scroll.cpp | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index f364c6a05..9b6c90586 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -1154,4 +1154,63 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { +TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) { + /* + When movefocus is dispatched within groups to move focus from one group member to another, scrolling view must move if follow_focus = true + ------------------------------------------------------------------------------------------------------------------------------------------ + */ + + + NLog::log("{}Testing scrolling view behaviour: movefocus within groups does causes scrolling view to move if follow_focus = true", Colors::GREEN); + + // ensure variables are correctly set for the test + + // necessary to make sure movefocus first cycles through tabs in a group + OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + + if (!Tests::spawnKitty("a")) { + FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); + } + + OK(getFromSocket("/dispatch hl.dsp.group.toggle({window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); + + if (!Tests::spawnKitty("b")) { + FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); + } + + if (!Tests::spawnKitty("c")) { + FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); + } + + // focus class:b. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:b'})")); + + // move it into the group where class:a is. This does not cause scrolling view to move when follow_focus = false + OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); + + // we move from one window of a group to another (from class:b to class:a) via movefocus + OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); + + // the focus now should still be on class:a window. If the scrolling view moved, its x coordinate for its `at:` value should be >= 0 + + const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + // test fail + if (std::stoi(currentWindowPosX) < 0) { + FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); + } + // test pass + else { + NLog ::log("{}Passed: {}window of class 'a' has x coordinates >= 0 for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); + } + + // clean up + + // kill all windows + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); +} From b645dd2d4dd57165043399aba457cca6e7aeec74 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 1 May 2026 03:34:17 +0200 Subject: [PATCH 39/40] test-fix: kill all layers before killing all windows in layers test. prevents internal post test cleanup function failing to kill all layers test-fix: maximized and fullscreen swapped between their tests test-fix: testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue missing layout initialization test-fix: testScrollingViewBehaviourWorkspaceChange, testScrollingViewBehaviourCloseWindowInGroup have stale window class in a comment clang-format --- hyprtester/src/tests/main/scroll.cpp | 116 ++++-------------- .../tiled/dwindle/DwindleAlgorithm.hpp | 10 +- .../tiled/master/MasterAlgorithm.hpp | 10 +- src/layout/space/Space.cpp | 4 +- 4 files changed, 35 insertions(+), 105 deletions(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 9b6c90586..7bf41c06c 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -198,19 +198,15 @@ TEST_CASE(scrollWindowRule) { ASSERT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036"); } - - - TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { - /* focuswindow DOES NOT move the scrolling view when follow_focus = false --------------------------------------------------------------------------------- */ NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD NOT move scrolling view when follow_focus = false", Colors::GREEN); - + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); // ensure variables are correctly set for the test @@ -222,7 +218,6 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); - if (!Tests::spawnKitty("b")) { FAIL_TEST("Could not spawn kitty with win class `b`"); } @@ -249,19 +244,16 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { ASSERT(Tests::windowCount(), 0); } - TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { - /* focuswindow DOES move the view when follow_focus = true -------------------------------------------------------------------- */ - - NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN); - - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + NLog::log("{}Testing scrolling view behaviour: focuswindow dispatch SHOULD move scrolling view when follow_focus = true", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); if (!Tests::spawnKitty("a")) { FAIL_TEST("Could not spawn kitty with win class `a`"); @@ -269,14 +261,12 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); - if (!Tests::spawnKitty("b")) { FAIL_TEST("Could not spawn kitty with win class `b`"); } OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); - // If the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport. // If it is not, the view moved, which is what we expect to happen. const std::string posA = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); @@ -289,14 +279,12 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { // clean up - // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); } - TEST_CASE(testScrollingViewBehaviourFocusFallback) { /* @@ -306,21 +294,17 @@ TEST_CASE(testScrollingViewBehaviourFocusFallback) { NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a tiled window should not move scrolling view", Colors::GREEN); - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); // ensure variables are correctly set for the test OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); - - if (!Tests::spawnKitty("a")) { FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); } OK(getFromSocket("/dispatch hl.dsp.layout('colresize 0.8')")); - if (!Tests::spawnKitty("b")) { FAIL_TEST("{}Failed to spawn kitty with class `b`", Colors::RED); } @@ -337,8 +321,6 @@ TEST_CASE(testScrollingViewBehaviourFocusFallback) { OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:c'})")); - - // kill the floating window // Expect the focus to fall back to the left tiled window OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:c'})")); @@ -367,16 +349,13 @@ TEST_CASE(testScrollingViewBehaviourFocusFallback) { ASSERT(Tests::windowCount(), 0); } - - TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { // same idea as testScrollingViewBehaviourFocusFallback, but with window of class "a" being grouped. - - NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a grouped tiled should not move scrolling view", Colors::GREEN); - - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + NLog::log("{}Testing scrolling view behaviour: focus fallback from floating window to a grouped tiled should not move scrolling view", Colors::GREEN); + + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); // ensure variables are correctly set for the test @@ -435,7 +414,6 @@ TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); - } TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { @@ -452,11 +430,9 @@ TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); - // switch to workspace 1 for this test OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); - if (!Tests::spawnKitty("a")) { FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); } @@ -470,12 +446,11 @@ TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { // does not move view when follow_focus = 0 OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); - - // change to workspace 2, then back to workspace 1 again + // change to workspace 2, then back to workspace 1 again OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})")); OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); - // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + // If the scrolling view did not move, the x value for `at:` of the currently focused window, class:a, must be <0 (must be left of the viewport) const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); @@ -494,12 +469,8 @@ TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); - - } - - TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { /* @@ -509,10 +480,8 @@ TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { NLog::log("{}Testing scrolling view behaviour: changing to a special scrolling workspace from a normal workspace should not move scrolling view", Colors::GREEN); - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); @@ -531,8 +500,6 @@ TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { // does not move view when follow_focus = 0 OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); - - // change to workspace 2, then back to special "scroll_S" workspace again OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '2'})")); @@ -561,11 +528,8 @@ TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { ASSERT(Tests::windowCount(), 0); } - - TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { - /* We also test switching between 2 special workspaces This follows the same idea and dependencies as the test testScrollingViewBehaviourSpecialWorkspaceChange() @@ -575,16 +539,12 @@ TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - - // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); - // We'll test in this special workspace OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('name:scroll_S')")); - if (!Tests::spawnKitty("a")) { FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); } @@ -624,24 +584,19 @@ TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); - } - TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { - /* When you change close a window inside a group (NOT destroying the group!), it should not cause scrolling view to shift to pull that group into view, regardless of follow_focus ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ - NLog::log("{}Testing scrolling view behaviour: closing a window in a group (> 1 window in group) should not move scrolling view", Colors::GREEN); OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - // ensure variables are correctly set for the test // this is to avoid unwanted view shifts when setting up the windows @@ -660,10 +615,7 @@ TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { FAIL_TEST("{}Failed to spawn kitty with win class `b`", Colors::RED); } - OK(getFromSocket("/dispatch hl.dsp.window.move({ into_group = 'left' })")); - - if (!Tests::spawnKitty("c")) { FAIL_TEST("{}Failed to spawn kitty with win class `c`", Colors::RED); @@ -676,7 +628,7 @@ TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { OK(getFromSocket("/dispatch hl.dsp.window.kill({window = 'class:b'})")); Tests::waitUntilWindowsN(2); - // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) + // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:a, must be <0 (must be left of the viewport) const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); @@ -695,10 +647,8 @@ TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); - } - TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) { /* @@ -754,23 +704,19 @@ TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) { NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); - } - - TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) { /* when a window is moved inside a group, scrolling view should move to fit that group when follow_focus = true ------------------------------------------------------------------------------------------------------------ */ - + NLog::log("{}Testing scrolling view behaviour: moving a window in a group SHOULD move scrolling view if follow_focus = true", Colors::GREEN); OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - // ensure variables are correctly set for the test OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); @@ -810,14 +756,12 @@ TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) { // clean up - // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); } - TEST_CASE(testScrollingViewBehaviourNewLayer) { /* @@ -829,7 +773,6 @@ TEST_CASE(testScrollingViewBehaviourNewLayer) { OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); @@ -845,7 +788,6 @@ TEST_CASE(testScrollingViewBehaviourNewLayer) { // focus class:a - this does not move scrolling view when follow_focus = 0 OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); - NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, "myLayer"); if (!Tests::spawnLayerKitty("myLayer")) { @@ -867,19 +809,17 @@ TEST_CASE(testScrollingViewBehaviourNewLayer) { // clean up + // kill all layers + NLog::log("{}Killing all layers", Colors::YELLOW); + Tests::killAllLayers(); + ASSERT(Tests::layerCount(), 0); // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); - - // kill all layers - NLog::log("{}Killing all layers", Colors::YELLOW); - Tests::killAllLayers(); - ASSERT(Tests::layerCount(), 0); } - TEST_CASE(testScrollingViewBehaviourMaximise) { /* @@ -908,10 +848,10 @@ TEST_CASE(testScrollingViewBehaviourMaximise) { OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); // fullscreen class:a window - OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'set', window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'set', window = 'class:a'})")); // unfullscreen class:a window - OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'unset', window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'unset', window = 'class:a'})")); // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 @@ -926,15 +866,12 @@ TEST_CASE(testScrollingViewBehaviourMaximise) { FAIL_TEST("{}Failed: {}window of class 'a' does not have negative x coordinates for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); } - - // clean up // kill all windows NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); ASSERT(Tests::windowCount(), 0); - } TEST_CASE(testScrollingViewBehaviourFullscreen) { @@ -944,16 +881,14 @@ TEST_CASE(testScrollingViewBehaviourFullscreen) { fullscreening and then unfullscreening a window shouldn't move scrolling view, regardless of follow_focus */ - + NLog::log("{}Testing scrolling view behaviour: fullscreening and then unfullscreening a window shouldn't move scrolling view", Colors::GREEN); OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - // ensure variables are correctly set for the test - this is to avoid unwanted view shifts when setting up the windows OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); - if (!Tests::spawnKitty("a")) { FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); } @@ -968,10 +903,10 @@ TEST_CASE(testScrollingViewBehaviourFullscreen) { OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); // maximise class:a window - OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'set', window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'set', window = 'class:a'})")); // unmaximise class:a window - OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'maximized', action = 'unset', window = 'class:a'})")); + OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'unset', window = 'class:a'})")); // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); @@ -995,7 +930,6 @@ TEST_CASE(testScrollingViewBehaviourFullscreen) { TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusFalse) { - /* dispatching movefocus when follow_focus = false should not cause scrolling view to move --------------------------------------------------------------------------------------- @@ -1052,7 +986,6 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) { OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - if (!Tests::spawnKitty("a")) { FAIL_TEST("{}Failed to spawn kitty with win class `a`", Colors::RED); } @@ -1087,7 +1020,6 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) { ASSERT(Tests::windowCount(), 0); } - TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { /* @@ -1097,14 +1029,13 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { NLog::log("{}Testing scrolling view behaviour: movefocus within groups does not cause scrolling view to move if follow_focus = false", Colors::GREEN); - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); // ensure variables are correctly set for the test // necessary to make sure movefocus first cycles through tabs in a group OK(getFromSocket("/eval hl.config({ binds = {movefocus_cycles_groupfirst = true}})")); - OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); + OK(getFromSocket("/eval hl.config({group = {auto_group = false}})")); OK(getFromSocket("/eval hl.config({scrolling = {follow_focus = false}})")); if (!Tests::spawnKitty("a")) { @@ -1152,8 +1083,6 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { ASSERT(Tests::windowCount(), 0); } - - TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) { /* @@ -1161,9 +1090,10 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) { ------------------------------------------------------------------------------------------------------------------------------------------ */ - NLog::log("{}Testing scrolling view behaviour: movefocus within groups does causes scrolling view to move if follow_focus = true", Colors::GREEN); + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + // ensure variables are correctly set for the test // necessary to make sure movefocus first cycles through tabs in a group diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index fd277a5c2..cf5440897 100644 --- a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp +++ b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp @@ -30,12 +30,12 @@ namespace Layout::Tiled { CDwindleAlgorithm() = default; virtual ~CDwindleAlgorithm() = default; - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); + virtual void newTarget(SP target); + virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); + virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp index 859c01aa2..b29542c61 100644 --- a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp +++ b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp @@ -48,12 +48,12 @@ namespace Layout::Tiled { CMasterAlgorithm() = default; virtual ~CMasterAlgorithm() = default; - virtual void newTarget(SP target); - virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); - virtual void removeTarget(SP target); + virtual void newTarget(SP target); + virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); + virtual void removeTarget(SP target); - virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); - virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); + virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE); + virtual void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); diff --git a/src/layout/space/Space.cpp b/src/layout/space/Space.cpp index 827f39a72..ae2a12f7d 100644 --- a/src/layout/space/Space.cpp +++ b/src/layout/space/Space.cpp @@ -203,8 +203,8 @@ SP CSpace::getNextCandidate(SP old) { } bool Layout::isHardRecalculateReason(eRecalculateReason reason) { - return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && - reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN && reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; + return reason != RECALCULATE_REASON_WORKSPACE_CHANGE && reason != RECALCULATE_REASON_SPECIAL_WORKSPACE_TOGGLE && reason != RECALCULATE_REASON_TOGGLE_FULLSCREEN && + reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; } const std::vector>& CSpace::targets() const { From ebc8f419a5fc1efdff429f62e065f620ffbf7c30 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Wed, 6 May 2026 02:38:56 +0200 Subject: [PATCH 40/40] catch up with main: move to new test function --- hyprtester/src/tests/main/scroll.cpp | 68 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 7bf41c06c..8e379d827 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -225,8 +225,8 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocusFalse) { OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); // if the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport - const std::string posA = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const int posAx = std::stoi(posA.substr(4, posA.find(',') - 4)); + const std::string posA = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const int posAx = std::stoi(posA.substr(0, posA.find(','))); if (posAx < 0) { NLog::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors::GREEN, Colors::RESET, posAx); } else { @@ -269,8 +269,8 @@ TEST_CASE(testScrollingViewBehaviourDispatchFocusWindowFollowFocustrue) { // If the view does not move, we expect the x coordinate of the window of class "a" to be negative, as it would be to the left of the viewport. // If it is not, the view moved, which is what we expect to happen. - const std::string posA = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const int posAx = std::stoi(posA.substr(4, posA.find(',') - 4)); + const std::string posA = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const int posAx = std::stoi(posA.substr(0, posA.find(','))); if (posAx < 0) { FAIL_TEST("{}Failed: {}Expected the x coordinate of window of class \"a\" to be >= 0, got {}.", Colors::RED, Colors::RESET, posAx); } else { @@ -330,8 +330,8 @@ TEST_CASE(testScrollingViewBehaviourFocusFallback) { // If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport) - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}Expected the x coordinate of window of class \"a\" to be < 0, got {}.", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -396,8 +396,8 @@ TEST_CASE(testScrollingViewBehaviourFocusFallbackWithGroups) { // If the view did not move, we expect currently focused window's (class:a) to have "at: " x coordinat value <0 (must be left of the viewport) - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { @@ -451,8 +451,8 @@ TEST_CASE(testScrollingViewBehaviourWorkspaceChange) { OK(getFromSocket("/dispatch hl.dsp.focus({workspace = '1'})")); // If the scrolling view did not move, the x value for `at:` of the currently focused window, class:a, must be <0 (must be left of the viewport) - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { @@ -509,8 +509,8 @@ TEST_CASE(testScrollingViewBehaviourSpecialWorkspaceChange) { OK(getFromSocket("/dispatch hl.dsp.focus({window = 'class:a'})")); // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -567,8 +567,8 @@ TEST_CASE(testScrollingViewBehaviourSpecialToSpecialWorkspaceChange) { // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:c, must be <0 (must be left of the viewport) - const std::string currentWindowPosSPECIAL = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosSPECIALX = currentWindowPosSPECIAL.substr(4, currentWindowPosSPECIAL.find(',') - 4); + const std::string currentWindowPosSPECIAL = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosSPECIALX = currentWindowPosSPECIAL.substr(0, currentWindowPosSPECIAL.find(',')); // test pass if (std::stoi(currentWindowPosSPECIALX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosSPECIALX); @@ -630,8 +630,8 @@ TEST_CASE(testScrollingViewBehaviourCloseWindowInGroup) { // If the scrolling view did not move, the x value for `at:` of the currently focused windows, class:a, must be <0 (must be left of the viewport) - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -687,8 +687,8 @@ TEST_CASE(testScrollingViewBehaviourMoveWindowIntoGroupFollowFocusFalse) { // the focus now should still be on class:b window. If the view did not move, its x coordinate for its `at:` value should be <0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'b' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -743,8 +743,8 @@ TEST_CASE(testScrollingViewBehaviourMoveWindowInGroupFollowFocusTrue) { // the focus now should still be on class:b window. If the scrolling view did move, its x coordinate for its `at:` value should be >= 0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test fail if (std::stoi(currentWindowPosX) < 0) { FAIL_TEST("{}window of class 'b' does not have x coordinates >= 0 for its position: {}", Colors::RED, currentWindowPosX); @@ -796,8 +796,8 @@ TEST_CASE(testScrollingViewBehaviourNewLayer) { // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -855,8 +855,8 @@ TEST_CASE(testScrollingViewBehaviourMaximise) { // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -909,8 +909,8 @@ TEST_CASE(testScrollingViewBehaviourFullscreen) { OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({mode = 'fullscreen', action = 'unset', window = 'class:a'})")); // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be <0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -956,8 +956,8 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusFalse) { OK(getFromSocket("/dispatch hl.dsp.focus({direction = 'left'})")); // If the scrolling view did not move, class:a window's x coordinate for its `at:` value should be < 0. - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -1001,8 +1001,8 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusFollowFocusTrue) { // If the scrolling view moved, class:a window's x coordinate for its `at:` value should be >= 0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test fail if (std::stoi(currentWindowPosX) < 0) { FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX); @@ -1064,8 +1064,8 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusFalse) { // the focus now should still be on class:a window. If the scrolling view did not move, its x coordinate for its `at:` value should be < 0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test pass if (std::stoi(currentWindowPosX) < 0) { NLog ::log("{}Passed: {}window of class 'a' has negative x coordinates for its position: {}", Colors ::GREEN, Colors::RESET, currentWindowPosX); @@ -1126,8 +1126,8 @@ TEST_CASE(testScrollingViewBehaviourMoveFocusInGroupFollowFocusTrue) { // the focus now should still be on class:a window. If the scrolling view moved, its x coordinate for its `at:` value should be >= 0 - const std::string currentWindowPos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); - const std::string currentWindowPosX = currentWindowPos.substr(4, currentWindowPos.find(',') - 4); + const std::string currentWindowPos = Tests::getAttribute(getFromSocket("/activewindow"), "at"); + const std::string currentWindowPosX = currentWindowPos.substr(0, currentWindowPos.find(',')); // test fail if (std::stoi(currentWindowPosX) < 0) { FAIL_TEST("{}Failed: {}window of class 'a' does not have x coordinates >= 0 for its position: {}", Colors::RED, Colors::RESET, currentWindowPosX);