From bd7f695363898826d5bd04fe830f19581ac4c5b2 Mon Sep 17 00:00:00 2001 From: erstarr <253168930+erstarr@users.noreply.github.com> Date: Fri, 8 May 2026 17:45:43 +0200 Subject: algo/scrolling: various scrolling view related bugfixes (#13974) * 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 * fix focus_fallback after unmapping a window floating moving view * 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 * 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 * fix fullscreen/maximise moving the scrolling view * fix opening of layer 3 programs moving scrolling view * refactor, clean up and clang-format * fix bad decisions * fix includes, clang-format. * remove TODO * `focuswindow` no longer moves scrolling view when `follow_focus = 0` * fix: changes in a group's current window no longer casues scrolling view shift when `follow_focus = 0` * fix: `moveintogroup`, `movewindoworgroup` no longer causes scrolling view change if `follow_focus = 0` * clang-format * 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 * 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` * Fix for Lua: switchToWondow * Fix for Lua: focus({window}) * Fix for Lua: Move Window Into Group * clang-format * Drop `hyprctl keyword` change * test: `focus({window})` with `follow_focus = false` * test: `focus({window})` with `follow_focus = true` * test: test Focus Fallback * test: Focus Fallback with groups * test: workspace change * test: special workspace toggle * test: workspace change - special to special also minor refactoring * test: close window in a group * test: move window into group `follow_focus = false` minor fix in test: expect -> assert * test: move window into group `follow_focus = true` * test: new layer * test: maximise fix in test: expect to assert. * test: fullscreen * test: `dispatch window.focus({ direction })` when `follow_focus = false` * test: `dispatch window.focus({ direction })` when `follow_focus = true` test-fix: expect -> assert * test: move focus within group when `follow_focus = false` * test: move focus within group when `follow_focus = true` * 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 * catch up with main: move to new test function * clean-up the clean-up part of the tests: redundant * clang-format * pain * suffering: fix rebase fail * test: manually kill all layers since the cleanup function fails * remove test: fullscreen and maximise. scrolling has its own behaviour now --- src/Compositor.cpp | 2 +- src/config/lua/layout/LuaLayoutProvider.cpp | 2 +- src/config/lua/layout/LuaLayoutProvider.hpp | 2 +- src/config/shared/actions/ConfigActions.cpp | 8 +++---- src/desktop/state/FocusState.cpp | 3 ++- src/desktop/state/FocusState.hpp | 10 +++++++++ src/desktop/view/Group.cpp | 8 +++++-- src/desktop/view/Group.hpp | 7 +++++- src/desktop/view/Window.cpp | 4 ++-- src/helpers/Monitor.cpp | 14 ++++++------ src/layout/LayoutManager.cpp | 9 ++++---- src/layout/LayoutManager.hpp | 9 +++++++- src/layout/algorithm/Algorithm.cpp | 6 +++--- src/layout/algorithm/Algorithm.hpp | 3 ++- src/layout/algorithm/FloatingAlgorithm.cpp | 2 +- src/layout/algorithm/FloatingAlgorithm.hpp | 2 +- src/layout/algorithm/ModeAlgorithm.hpp | 3 ++- .../algorithm/tiled/dwindle/DwindleAlgorithm.cpp | 2 +- .../algorithm/tiled/dwindle/DwindleAlgorithm.hpp | 2 +- .../algorithm/tiled/master/MasterAlgorithm.cpp | 2 +- .../algorithm/tiled/master/MasterAlgorithm.hpp | 2 +- .../algorithm/tiled/monocle/MonocleAlgorithm.cpp | 2 +- .../algorithm/tiled/monocle/MonocleAlgorithm.hpp | 2 +- .../tiled/scrolling/ScrollingAlgorithm.cpp | 25 +++++++++++++++++----- .../tiled/scrolling/ScrollingAlgorithm.hpp | 4 ++-- src/layout/space/Space.cpp | 21 +++++++++++++++--- src/layout/space/Space.hpp | 16 +++++++++++++- src/render/Renderer.cpp | 2 +- 28 files changed, 124 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/Compositor.cpp b/src/Compositor.cpp index bf3cf624..4c1861e4 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -2288,7 +2288,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/config/lua/layout/LuaLayoutProvider.cpp b/src/config/lua/layout/LuaLayoutProvider.cpp index 344362ff..c2603d1b 100644 --- a/src/config/lua/layout/LuaLayoutProvider.cpp +++ b/src/config/lua/layout/LuaLayoutProvider.cpp @@ -46,7 +46,7 @@ void CLuaTiledAlgorithm::resizeTarget(const Vector2D& Δ, SP ta recalculate(); } -void CLuaTiledAlgorithm::recalculate() { +void CLuaTiledAlgorithm::recalculate(Layout::eRecalculateReason reason) { auto targets = liveTargets(); if (targets.empty()) return; diff --git a/src/config/lua/layout/LuaLayoutProvider.hpp b/src/config/lua/layout/LuaLayoutProvider.hpp index 4e223670..e456d503 100644 --- a/src/config/lua/layout/LuaLayoutProvider.hpp +++ b/src/config/lua/layout/LuaLayoutProvider.hpp @@ -38,7 +38,7 @@ namespace Config::Lua::Layouts { virtual void movedTarget(SP target, std::optional focalPoint = std::nullopt); virtual void removeTarget(SP target); virtual void resizeTarget(const Vector2D& Δ, SP target, Layout::eRectCorner corner = Layout::CORNER_NONE); - virtual void recalculate(); + virtual void recalculate(Layout::eRecalculateReason reason = Layout::RECALCULATE_REASON_UNKNOWN); virtual void swapTargets(SP a, SP b); virtual void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); virtual Config::ErrorResult layoutMsg(const std::string_view& sv); diff --git a/src/config/shared/actions/ConfigActions.cpp b/src/config/shared/actions/ConfigActions.cpp index a59b4ab3..52b2a359 100644 --- a/src/config/shared/actions/ConfigActions.cpp +++ b/src/config/shared/actions/ConfigActions.cpp @@ -61,10 +61,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) { @@ -431,7 +431,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 {}; @@ -1270,7 +1270,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.cpp b/src/desktop/state/FocusState.cpp index 48fb6456..702b02c4 100644 --- a/src/desktop/state/FocusState.cpp +++ b/src/desktop/state/FocusState.cpp @@ -308,5 +308,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; + 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_SWITCH_TO_WINDOW_HARD; } diff --git a/src/desktop/state/FocusState.hpp b/src/desktop/state/FocusState.hpp index 71330a3e..334c426d 100644 --- a/src/desktop/state/FocusState.hpp +++ b/src/desktop/state/FocusState.hpp @@ -10,9 +10,19 @@ namespace Desktop { FOCUS_REASON_UNKNOWN = 0, FOCUS_REASON_FFM, FOCUS_REASON_KEYBIND, + FOCUS_REASON_DISPATCH_FOCUSWINDOW, + FOCUS_REASON_DISPATCH_MOVEWINDOWINTOGROUP, 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, + 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/Group.cpp b/src/desktop/view/Group.cpp index f2b2a564..7073672a 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, eRemoveFromGroupReason 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); } } @@ -215,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/desktop/view/Group.hpp b/src/desktop/view/Group.hpp index 048c5023..3ada4d6e 100644 --- a/src/desktop/view/Group.hpp +++ b/src/desktop/view/Group.hpp @@ -15,10 +15,15 @@ namespace Desktop::View { static SP create(std::vector&& windows); ~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); + 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/desktop/view/Window.cpp b/src/desktop/view/Window.cpp index 19f8f88d..22967a62 100644 --- a/src/desktop/view/Window.cpp +++ b/src/desktop/view/Window.cpp @@ -2397,9 +2397,9 @@ 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); + 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); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index eebc2daa..6bfc3336 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1424,13 +1424,13 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo pWindow = pWorkspace->getFocusCandidate(); } - 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)}); @@ -1493,11 +1493,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(); } @@ -1523,7 +1523,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}); @@ -1588,11 +1588,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 875ae2ae..738a2286 100644 --- a/src/layout/LayoutManager.cpp +++ b/src/layout/LayoutManager.cpp @@ -334,18 +334,19 @@ void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SPm_activeSpecialWorkspace) - m->m_activeSpecialWorkspace->m_space->recalculate(); + m->m_activeSpecialWorkspace->m_space->recalculate(recalcMonitorReasonToRecalcReason(reason)); + if (m->m_activeWorkspace) - m->m_activeWorkspace->m_space->recalculate(); + m->m_activeWorkspace->m_space->recalculate(recalcMonitorReasonToRecalcReason(reason)); } 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/LayoutManager.hpp b/src/layout/LayoutManager.hpp index ef886daa..1e509586 100644 --- a/src/layout/LayoutManager.hpp +++ b/src/layout/LayoutManager.hpp @@ -50,6 +50,13 @@ namespace Layout { CLayoutManager(); ~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_TOGGLE_FULLSCREEN, + }; + void newTarget(SP target, SP space); void removeTarget(SP target); @@ -81,7 +88,7 @@ namespace Layout { void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP target, eMouseBindMode mode, int corner, const Vector2D& beginSize); void invalidateMonitorGeometries(PHLMONITOR); - void recalculateMonitor(PHLMONITOR); + 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 ae96a7d8..3b3accc1 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(eRecalculateReason 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 61c40285..c4a1d2e0 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 "../space/Space.hpp" #include #include @@ -34,7 +35,7 @@ namespace Layout { Config::ErrorResult layoutMsg(const std::string_view& sv); std::optional predictSizeForNewTiledTarget(); - void recalculate(); + 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 058887bf..ed9d2c46 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(eRecalculateReason reason) { ; } diff --git a/src/layout/algorithm/FloatingAlgorithm.hpp b/src/layout/algorithm/FloatingAlgorithm.hpp index 40e53034..9232888d 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(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); protected: IFloatingAlgorithm() = default; diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp index 1348a6f4..cfbeafd3 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 "../space/Space.hpp" #include @@ -36,7 +37,7 @@ namespace Layout { virtual void resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner = CORNER_NONE) = 0; // recalculate layout - virtual void recalculate() = 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 100856be..ff1b1b4f 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(eRecalculateReason reason) { calculateWorkspace(); } diff --git a/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp b/src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp index 300daa63..cf544089 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(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 c57a3639..b4d4eea1 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(eRecalculateReason reason) { calculateWorkspace(); } diff --git a/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp b/src/layout/algorithm/tiled/master/MasterAlgorithm.hpp index 00e82784..b29542c6 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(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 84b4f9e2..e619bf57 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(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 3f07748b..072f9cf0 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(); + 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 613ebe19..842e5b85 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp @@ -601,7 +601,16 @@ 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)); + // 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_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)); }); // Initialize default widths and direction @@ -645,7 +654,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"); @@ -867,7 +876,7 @@ void CScrollingAlgorithm::resizeTarget(const Vector2D& delta, SP target m_scrollingData->recalculate(true); } -void CScrollingAlgorithm::recalculate() { +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) @@ -878,8 +887,14 @@ 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 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 (Layout::isHardRecalculateReason(reason)) + focusOnInput(Desktop::focusState()->window()->layoutTarget(), INPUT_MODE_HARD); + } } m_scrollingData->recalculate(); diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp index a7c64aa8..867b86e8 100644 --- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp +++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp @@ -100,7 +100,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(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); virtual SP getNextCandidate(SP old); @@ -131,7 +131,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.cpp b/src/layout/space/Space.cpp index 35741a14..f4785d76 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(eRecalculateReason reason) { recheckWorkArea(); if (m_algorithm) - m_algorithm->recalculate(); + m_algorithm->recalculate(reason); } eFullscreenRequestResult CSpace::setFullscreen(SP t, eFullscreenMode currentEffectiveMode, eFullscreenMode mode) { @@ -173,7 +173,7 @@ eFullscreenRequestResult CSpace::setFullscreen(SP t, eFullscreenMode cu if (mode == FSMODE_NONE && m_algorithm && t->floating()) m_algorithm->recenter(t); - recalculate(); + recalculate(RECALCULATE_REASON_TOGGLE_FULLSCREEN); return REQUEST_RESULT; } @@ -218,6 +218,21 @@ 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_TOGGLE_FULLSCREEN && + reason != RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES && reason != RECALCULATE_REASON_RENDER_MOINTOR; +} + const std::vector>& CSpace::targets() const { return m_targets; } + +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_TOGGLE_FULLSCREEN: return RECALCULATE_REASON_TOGGLE_FULLSCREEN; + 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 a42dd55c..af731a50 100644 --- a/src/layout/space/Space.hpp +++ b/src/layout/space/Space.hpp @@ -12,6 +12,18 @@ #include 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_TOGGLE_FULLSCREEN, + RECALCULATE_REASON_INVALIDATE_MONITOR_GEOMETRIES, + RECALCULATE_REASON_RENDER_MOINTOR, + }; + + eRecalculateReason recalcMonitorReasonToRecalcReason(CLayoutManager::eRecalculateMonitorReason reason); + class ITarget; class CAlgorithm; @@ -34,7 +46,7 @@ namespace Layout { void moveTargetInDirection(SP t, Math::eDirection dir, bool silent); - void recalculate(); + void recalculate(eRecalculateReason reason = RECALCULATE_REASON_UNKNOWN); void toggleTargetFloating(SP t); @@ -68,4 +80,6 @@ namespace Layout { // for recalc CHyprSignalListener m_geomUpdateCallback; }; + + bool isHardRecalculateReason(eRecalculateReason reason); }; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 57d0398a..3c466dd9 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -2002,7 +2002,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) -- cgit v1.2.3