From 723870337f299d4c80aa0048144f3a1ca1c885bd Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Sat, 21 Feb 2026 21:30:39 +0000 Subject: layout: rethonk layouts from the ground up (#12890) Rewrites layouts to be much smaller, and deal with much less annoying BS. Improves the overall architecture, unifies handling of pseudotiling, and various other improvements. --- src/layout/algorithm/Algorithm.cpp | 264 +++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 src/layout/algorithm/Algorithm.cpp (limited to 'src/layout/algorithm/Algorithm.cpp') diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp new file mode 100644 index 00000000..cd8cfac4 --- /dev/null +++ b/src/layout/algorithm/Algorithm.cpp @@ -0,0 +1,264 @@ +#include "Algorithm.hpp" + +#include "FloatingAlgorithm.hpp" +#include "TiledAlgorithm.hpp" +#include "../target/WindowTarget.hpp" +#include "../space/Space.hpp" +#include "../../desktop/view/Window.hpp" +#include "../../desktop/history/WindowHistoryTracker.hpp" +#include "../../helpers/Monitor.hpp" +#include "../../render/Renderer.hpp" + +#include "../../debug/log/Logger.hpp" + +using namespace Layout; + +SP CAlgorithm::create(UP&& tiled, UP&& floating, SP space) { + auto algo = SP(new CAlgorithm(std::move(tiled), std::move(floating), space)); + algo->m_self = algo; + algo->m_tiled->m_parent = algo; + algo->m_floating->m_parent = algo; + return algo; +} + +CAlgorithm::CAlgorithm(UP&& tiled, UP&& floating, SP space) : + m_tiled(std::move(tiled)), m_floating(std::move(floating)), m_space(space) { + ; +} + +void CAlgorithm::addTarget(SP target) { + const bool SHOULD_FLOAT = target->floating(); + + if (SHOULD_FLOAT) { + m_floatingTargets.emplace_back(target); + m_floating->newTarget(target); + } else { + m_tiledTargets.emplace_back(target); + m_tiled->newTarget(target); + } +} + +void CAlgorithm::removeTarget(SP target) { + const bool IS_FLOATING = std::ranges::contains(m_floatingTargets, target); + + if (IS_FLOATING) { + m_floating->removeTarget(target); + std::erase(m_floatingTargets, target); + return; + } + + const bool IS_TILED = std::ranges::contains(m_tiledTargets, target); + + if (IS_TILED) { + m_tiled->removeTarget(target); + std::erase(m_tiledTargets, target); + return; + } + + Log::logger->log(Log::ERR, "BUG THIS: CAlgorithm::removeTarget, but not found"); +} + +void CAlgorithm::moveTarget(SP target, std::optional focalPoint, bool reposition) { + const bool SHOULD_FLOAT = target->floating(); + + if (SHOULD_FLOAT) { + m_floatingTargets.emplace_back(target); + if (reposition) + m_floating->newTarget(target); + else + m_floating->movedTarget(target, focalPoint); + } else { + m_tiledTargets.emplace_back(target); + if (reposition) + m_tiled->newTarget(target); + else + m_tiled->movedTarget(target, focalPoint); + } +} + +SP CAlgorithm::space() const { + return m_space.lock(); +} + +void CAlgorithm::setFloating(SP target, bool floating, bool reposition) { + removeTarget(target); + + g_pHyprRenderer->damageWindow(target->window()); + + target->setFloating(floating); + + moveTarget(target, std::nullopt, reposition); + + g_pHyprRenderer->damageWindow(target->window()); +} + +size_t CAlgorithm::tiledTargets() const { + return m_tiledTargets.size(); +} + +size_t CAlgorithm::floatingTargets() const { + return m_floatingTargets.size(); +} + +void CAlgorithm::recalculate() { + m_tiled->recalculate(); + m_floating->recalculate(); + + const auto PWORKSPACE = m_space->workspace(); + const auto PMONITOR = PWORKSPACE->m_monitor; + + if (PWORKSPACE->m_hasFullscreenWindow && PMONITOR) { + // massive hack from the fullscreen func + const auto PFULLWINDOW = PWORKSPACE->getFullscreenWindow(); + + if (PFULLWINDOW) { + if (PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) { + *PFULLWINDOW->m_realPosition = PMONITOR->m_position; + *PFULLWINDOW->m_realSize = PMONITOR->m_size; + } else if (PWORKSPACE->m_fullscreenMode == FSMODE_MAXIMIZED) + PFULLWINDOW->layoutTarget()->setPositionGlobal(m_space->workArea()); + } + + return; + } +} + +void CAlgorithm::recenter(SP t) { + if (t->floating()) + m_floating->recenter(t); +} + +std::expected CAlgorithm::layoutMsg(const std::string_view& sv) { + if (const auto ret = m_floating->layoutMsg(sv); !ret) + return ret; + return m_tiled->layoutMsg(sv); +} + +std::optional CAlgorithm::predictSizeForNewTiledTarget() { + return m_tiled->predictSizeForNewTarget(); +} + +void CAlgorithm::resizeTarget(const Vector2D& Δ, SP target, eRectCorner corner) { + if (target->floating()) + m_floating->resizeTarget(Δ, target, corner); + else + m_tiled->resizeTarget(Δ, target, corner); +} + +void CAlgorithm::moveTarget(const Vector2D& Δ, SP target) { + if (target->floating()) + m_floating->moveTarget(Δ, target); +} + +void CAlgorithm::swapTargets(SP a, SP b) { + auto swapFirst = [&a, &b](std::vector>& targets) -> bool { + auto ia = std::ranges::find(targets, a); + auto ib = std::ranges::find(targets, b); + + if (ia != std::ranges::end(targets) && ib != std::ranges::end(targets)) { + std::iter_swap(ia, ib); + return true; + } else if (ia != std::ranges::end(targets)) + *ia = b; + else if (ib != std::ranges::end(targets)) + *ib = a; + + return false; + }; + + if (!swapFirst(m_tiledTargets)) + swapFirst(m_floatingTargets); + + const WP algA = a->floating() ? WP(m_floating) : WP(m_tiled); + const WP algB = b->floating() ? WP(m_floating) : WP(m_tiled); + + algA->swapTargets(a, b); + if (algA != algB) + algB->swapTargets(b, a); +} + +void CAlgorithm::moveTargetInDirection(SP t, Math::eDirection dir, bool silent) { + if (t->floating()) + m_floating->moveTargetInDirection(t, dir, silent); + else + m_tiled->moveTargetInDirection(t, dir, silent); +} + +void CAlgorithm::updateFloatingAlgo(UP&& algo) { + algo->m_parent = m_self; + + for (const auto& t : m_floatingTargets) { + const auto TARGET = t.lock(); + if (!TARGET) + continue; + + // Unhide windows when switching layouts to prevent them from being permanently lost + const auto WINDOW = TARGET->window(); + if (WINDOW) + WINDOW->setHidden(false); + + m_floating->removeTarget(TARGET); + algo->newTarget(TARGET); + } + + m_floating = std::move(algo); +} + +void CAlgorithm::updateTiledAlgo(UP&& algo) { + algo->m_parent = m_self; + + for (const auto& t : m_tiledTargets) { + const auto TARGET = t.lock(); + if (!TARGET) + continue; + + // Unhide windows when switching layouts to prevent them from being permanently lost + // This is a safeguard for layouts (including third-party plugins) that use setHidden + const auto WINDOW = TARGET->window(); + if (WINDOW) + WINDOW->setHidden(false); + + m_tiled->removeTarget(TARGET); + algo->newTarget(TARGET); + } + + m_tiled = std::move(algo); +} + +const UP& CAlgorithm::tiledAlgo() const { + return m_tiled; +} + +const UP& CAlgorithm::floatingAlgo() const { + return m_floating; +} + +SP CAlgorithm::getNextCandidate(SP old) { + if (old->floating()) { + // use window history to determine best target + for (const auto& w : Desktop::History::windowTracker()->fullHistory() | std::views::reverse) { + if (!w->m_workspace || w->m_workspace->m_space != m_space || !w->layoutTarget() || !w->layoutTarget()->space()) + continue; + + return w->layoutTarget(); + } + + // no history, fall back + } else { + // ask the layout + const auto CANDIDATE = m_tiled->getNextCandidate(old); + if (CANDIDATE) + return CANDIDATE; + + // no candidate, fall back + } + + // fallback: try to focus anything + if (!m_tiledTargets.empty()) + return m_tiledTargets.back().lock(); + if (!m_floatingTargets.empty()) + return m_floatingTargets.back().lock(); + + // god damn it, maybe empty? + return nullptr; +} -- cgit v1.2.3