#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 "../../desktop/state/FocusState.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) { std::erase(m_floatingTargets, target); m_floating->removeTarget(target); return; } const bool IS_TILED = std::ranges::contains(m_tiledTargets, target); if (IS_TILED) { std::erase(m_tiledTargets, target); m_tiled->removeTarget(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(); if (!PWORKSPACE) return; 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; const auto FOCUSED_WINDOW = Desktop::focusState()->window(); const auto FOCUSED_TARGET = FOCUSED_WINDOW ? FOCUSED_WINDOW->layoutTarget() : nullptr; 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); } if (FOCUSED_TARGET && FOCUSED_TARGET->space() == m_space && FOCUSED_TARGET->floating()) Desktop::focusState()->fullWindowFocus(FOCUSED_WINDOW, Desktop::eFocusReason::FOCUS_REASON_DESKTOP_STATE_CHANGE); m_floating = std::move(algo); } void CAlgorithm::updateTiledAlgo(UP&& algo) { algo->m_parent = m_self; const auto FOCUSED_WINDOW = Desktop::focusState()->window(); const auto FOCUSED_TARGET = FOCUSED_WINDOW ? FOCUSED_WINDOW->layoutTarget() : nullptr; 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); } if (FOCUSED_TARGET && FOCUSED_TARGET->space() == m_space && !FOCUSED_TARGET->floating()) Desktop::focusState()->fullWindowFocus(FOCUSED_WINDOW, Desktop::eFocusReason::FOCUS_REASON_DESKTOP_STATE_CHANGE); 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) { static auto FOCUSONCLOSE = CConfigValue("input:focus_on_close"); if (old->floating() || *FOCUSONCLOSE == 2) { // 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; } void CAlgorithm::setTargetGeom(const CBox& box, SP target) { if (!target->floating() || !std::ranges::contains(m_floatingTargets, target)) return; m_floating->setTargetGeom(box, target); }