summaryrefslogtreecommitdiff
path: root/src/layout/algorithm
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-05-07 15:21:06 +0100
committerGitHub <noreply@github.com>2026-05-07 15:21:06 +0100
commit58833eb1d79d7f03402cee0730c309b79f2e4501 (patch)
tree180cadc5d2e6fcb9e2f090eeccc6fb4ccace9b51 /src/layout/algorithm
parent6b8b20100253c083227e611be20360f4d8e7794e (diff)
layout/scrolling: handle fullscreen manually (#14190)
Makes scroll handle fullscreen by itself, allowing for fs scrolls.
Diffstat (limited to 'src/layout/algorithm')
-rw-r--r--src/layout/algorithm/Algorithm.cpp19
-rw-r--r--src/layout/algorithm/Algorithm.hpp4
-rw-r--r--src/layout/algorithm/ModeAlgorithm.cpp13
-rw-r--r--src/layout/algorithm/ModeAlgorithm.hpp13
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp53
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp1
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp379
-rw-r--r--src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp63
8 files changed, 497 insertions, 48 deletions
diff --git a/src/layout/algorithm/Algorithm.cpp b/src/layout/algorithm/Algorithm.cpp
index b22eb9bf..ae96a7d8 100644
--- a/src/layout/algorithm/Algorithm.cpp
+++ b/src/layout/algorithm/Algorithm.cpp
@@ -154,6 +154,25 @@ void CAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
m_floating->moveTarget(Δ, target);
}
+eFullscreenRequestResult CAlgorithm::requestFullscreen(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
+ if (!target)
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ const SFullscreenRequest request = {.target = target, .currentEffectiveMode = currentEffectiveMode, .effectiveMode = effectiveMode};
+ return target->floating() ? m_floating->requestFullscreen(request) : m_tiled->requestFullscreen(request);
+}
+
+SP<ITarget> CAlgorithm::layoutFullscreenTarget() const {
+ if (const auto TARGET = m_tiled->layoutFullscreenTarget(); TARGET)
+ return TARGET;
+
+ return m_floating->layoutFullscreenTarget();
+}
+
+bool CAlgorithm::layoutFullscreenCoversMonitor() const {
+ return m_tiled->layoutFullscreenCoversMonitor() || m_floating->layoutFullscreenCoversMonitor();
+}
+
void CAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
auto swapFirst = [&a, &b](std::vector<WP<ITarget>>& targets) -> bool {
auto ia = std::ranges::find(targets, a);
diff --git a/src/layout/algorithm/Algorithm.hpp b/src/layout/algorithm/Algorithm.hpp
index 8b9e471d..61c40285 100644
--- a/src/layout/algorithm/Algorithm.hpp
+++ b/src/layout/algorithm/Algorithm.hpp
@@ -42,6 +42,10 @@ namespace Layout {
void setTargetGeom(const CBox& box, SP<ITarget> target); // only for float
+ eFullscreenRequestResult requestFullscreen(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
+ SP<ITarget> layoutFullscreenTarget() const;
+ bool layoutFullscreenCoversMonitor() const;
+
void updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo);
void updateTiledAlgo(UP<ITiledAlgorithm>&& algo);
diff --git a/src/layout/algorithm/ModeAlgorithm.cpp b/src/layout/algorithm/ModeAlgorithm.cpp
index c029336f..5927e861 100644
--- a/src/layout/algorithm/ModeAlgorithm.cpp
+++ b/src/layout/algorithm/ModeAlgorithm.cpp
@@ -15,6 +15,19 @@ std::optional<Vector2D> IModeAlgorithm::predictSizeForNewTarget() {
return std::nullopt;
}
+eFullscreenRequestResult IModeAlgorithm::requestFullscreen(const SFullscreenRequest& request) {
+ (void)request;
+ return FULLSCREEN_REQUEST_DEFAULT;
+}
+
+SP<ITarget> IModeAlgorithm::layoutFullscreenTarget() const {
+ return nullptr;
+}
+
+bool IModeAlgorithm::layoutFullscreenCoversMonitor() const {
+ return false;
+}
+
std::optional<Vector2D> IModeAlgorithm::focalPointForDir(SP<ITarget> t, Math::eDirection dir) {
Vector2D focalPoint;
diff --git a/src/layout/algorithm/ModeAlgorithm.hpp b/src/layout/algorithm/ModeAlgorithm.hpp
index e53d4391..1348a6f4 100644
--- a/src/layout/algorithm/ModeAlgorithm.hpp
+++ b/src/layout/algorithm/ModeAlgorithm.hpp
@@ -13,6 +13,12 @@ namespace Layout {
class ITarget;
class CAlgorithm;
+ struct SFullscreenRequest {
+ SP<ITarget> target;
+ eFullscreenMode currentEffectiveMode = static_cast<eFullscreenMode>(0);
+ eFullscreenMode effectiveMode = static_cast<eFullscreenMode>(0);
+ };
+
class IModeAlgorithm {
public:
virtual ~IModeAlgorithm() = default;
@@ -44,6 +50,13 @@ namespace Layout {
// optional: predict new window's size
virtual std::optional<Vector2D> predictSizeForNewTarget();
+ // optional: allow algorithms to own fullscreen semantics for a target.
+ virtual eFullscreenRequestResult requestFullscreen(const SFullscreenRequest& request);
+
+ // optional: expose an algorithm-owned fullscreen target and whether it is monitor-exclusive.
+ virtual SP<ITarget> layoutFullscreenTarget() const;
+ virtual bool layoutFullscreenCoversMonitor() const;
+
// Impl'd here: focal point for dir
virtual std::optional<Vector2D> focalPointForDir(SP<ITarget> t, Math::eDirection dir);
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
index 657b6542..722b9bd8 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
@@ -147,14 +147,10 @@ double CScrollTapeController::calculateStripSize(size_t stripIndex, const CBox&
return usablePrimary * m_strips[stripIndex].size;
}
-CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
+CBox CScrollTapeController::calculateStripBox(size_t stripIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
if (stripIndex >= m_strips.size())
return {};
- const auto& strip = m_strips[stripIndex];
- if (targetIndex >= strip.targetSizes.size())
- return {};
-
const double usableSecondary = getSecondary(usableArea.size());
const double usablePrimary = getPrimary(usableArea.size());
const double cameraOffset = calculateCameraOffset(usableArea, fullscreenOnOne);
@@ -163,13 +159,6 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
double primaryPos = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
double primarySize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
- // calculate position along secondary axis (within strip)
- double secondaryPos = 0.0;
- for (size_t i = 0; i < targetIndex; ++i) {
- secondaryPos += strip.targetSizes[i] * usableSecondary;
- }
- double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
-
// apply camera offset based on direction
// for RIGHT/DOWN: scroll offset moves content left/up (subtract)
// for LEFT/UP: scroll offset moves content right/down (different coordinate system)
@@ -185,8 +174,8 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
}
// create the box in primary/secondary coordinates
- Vector2D pos = makeVector(primaryPos, secondaryPos);
- Vector2D size = makeVector(primarySize, secondarySize);
+ Vector2D pos = makeVector(primaryPos, 0.0);
+ Vector2D size = makeVector(primarySize, usableSecondary);
// translate to workspace position
pos = pos + workspaceOffset;
@@ -194,6 +183,35 @@ CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetI
return CBox{pos, size};
}
+CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
+ if (stripIndex >= m_strips.size())
+ return {};
+
+ const auto& strip = m_strips[stripIndex];
+ if (targetIndex >= strip.targetSizes.size())
+ return {};
+
+ CBox stripBox = calculateStripBox(stripIndex, usableArea, workspaceOffset, fullscreenOnOne);
+ const double usableSecondary = getSecondary(usableArea.size());
+
+ double secondaryPos = 0.0;
+ for (size_t i = 0; i < targetIndex; ++i) {
+ secondaryPos += strip.targetSizes[i] * usableSecondary;
+ }
+
+ const double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
+
+ if (isPrimaryHorizontal()) {
+ stripBox.y = workspaceOffset.y + secondaryPos;
+ stripBox.h = secondarySize;
+ } else {
+ stripBox.x = workspaceOffset.x + secondaryPos;
+ stripBox.w = secondarySize;
+ }
+
+ return stripBox;
+}
+
double CScrollTapeController::calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne) {
const double maxExtent = calculateMaxExtent(usableArea, fullscreenOnOne);
const double usablePrimary = getPrimary(usableArea.size());
@@ -275,13 +293,14 @@ size_t CScrollTapeController::getStripAtCenter(const CBox& usableArea, bool full
return 0;
const double usablePrimary = getPrimary(usableArea.size());
- double currentPos = m_offset;
+ const double viewCenter = m_offset + usablePrimary / 2.0;
+ double currentEnd = 0.0;
for (size_t i = 0; i < m_strips.size(); ++i) {
const double stripSize = calculateStripSize(i, usableArea, fullscreenOnOne);
- currentPos += stripSize;
+ currentEnd += stripSize;
- if (currentPos >= usablePrimary / 2.0 - 2.0)
+ if (currentEnd >= viewCenter - 2.0)
return i;
}
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp
index da2efbba..8006c4a5 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollTapeController.hpp
@@ -55,6 +55,7 @@ namespace Layout::Tiled {
double calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
double calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
+ CBox calculateStripBox(size_t stripIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
CBox calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
double calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne = false);
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
index 28941109..2914ae8b 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
@@ -11,6 +11,7 @@
#include "../../../../config/shared/workspace/WorkspaceRuleManager.hpp"
#include "../../../../render/Renderer.hpp"
#include "../../../../managers/input/InputManager.hpp"
+#include "../../../../managers/animation/DesktopAnimationManager.hpp"
#include "../../../../event/EventBus.hpp"
#include <hyprutils/string/VarList2.hpp>
@@ -427,23 +428,83 @@ void SScrollingData::recalculate(bool forceInstant) {
algorithm->m_parent->space()->workspace()->m_hasFullscreenWindow)
return;
+ algorithm->syncFullscreenTargets();
+
static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
const CBox USABLE = algorithm->usableArea();
const auto WORKAREA = algorithm->m_parent->space()->workArea();
+ const CBox MONBOX = algorithm->m_parent->space()->workspace()->m_monitor->logicalBox();
+
+ const auto WORKSPACERULE = Config::workspaceRuleMgr()->getWorkspaceRuleFor(algorithm->m_parent->space()->workspace());
+ static auto PGAPSINDATA = CConfigValue<Config::IComplexConfigValue>("general:gaps_in");
+ auto* const PGAPSIN = sc<Config::CCssGapData*>((PGAPSINDATA.ptr()));
+ const auto GAPSIN = (WORKSPACERULE && WORKSPACERULE->m_gapsIn.has_value()) ? WORKSPACERULE->m_gapsIn.value() : *PGAPSIN;
+
+ bool anyFullscreenCovers = false;
+ for (const auto& COL : columns) {
+ if (algorithm->fullscreenTargetDataForColumn(COL) && algorithm->fullscreenColumnCoversMonitor(COL)) {
+ anyFullscreenCovers = true;
+ break;
+ }
+ }
controller->setDirection(algorithm->getDynamicDirection());
+ algorithm->updateFullscreenFade(anyFullscreenCovers);
+
+ const auto targetBoxWithGaps = [&](const CBox& logical, size_t colIdx, size_t targetIdx, bool fullscreenOrHidden) -> STargetBox {
+ if (fullscreenOrHidden)
+ return {.logicalBox = logical, .visualBox = logical};
+
+ CBox visual = logical;
+ const bool PRIMARY_HORIZ = controller->isPrimaryHorizontal();
+
+ const bool GAP_LEFT = PRIMARY_HORIZ ? colIdx > 0 : targetIdx > 0;
+ const bool GAP_RIGHT = PRIMARY_HORIZ ? colIdx + 1 < columns.size() : targetIdx + 1 < columns[colIdx]->targetDatas.size();
+ const bool GAP_TOP = PRIMARY_HORIZ ? targetIdx > 0 : colIdx > 0;
+ const bool GAP_BOTTOM = PRIMARY_HORIZ ? targetIdx + 1 < columns[colIdx]->targetDatas.size() : colIdx + 1 < columns.size();
+
+ const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(GAP_LEFT ? GAPSIN.m_left : 0), sc<double>(GAP_TOP ? GAPSIN.m_top : 0));
+ const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(GAP_RIGHT ? GAPSIN.m_right : 0), sc<double>(GAP_BOTTOM ? GAPSIN.m_bottom : 0));
+
+ visual.x += GAPOFFSETTOPLEFT.x;
+ visual.y += GAPOFFSETTOPLEFT.y;
+ visual.w = std::max(1.0, visual.w - GAPOFFSETTOPLEFT.x - GAPOFFSETBOTTOMRIGHT.x);
+ visual.h = std::max(1.0, visual.h - GAPOFFSETTOPLEFT.y - GAPOFFSETBOTTOMRIGHT.y);
+
+ return {.logicalBox = logical, .visualBox = visual};
+ };
for (size_t i = 0; i < columns.size(); ++i) {
const auto& COL = columns[i];
+ const auto FS = algorithm->fullscreenTargetDataForColumn(COL);
for (size_t j = 0; j < COL->targetDatas.size(); ++j) {
const auto& TARGET = COL->targetDatas[j];
- TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE);
+ if (FS) {
+ if (TARGET == FS) {
+ if (algorithm->fullscreenColumnCoversMonitor(COL))
+ TARGET->layoutBox = MONBOX;
+ else {
+ TARGET->layoutBox = controller->calculateStripBox(i, USABLE, WORKAREA.pos(), *PFSONONE);
+
+ if (controller->isPrimaryHorizontal()) {
+ TARGET->layoutBox.y = MONBOX.y;
+ TARGET->layoutBox.h = MONBOX.h;
+ } else {
+ TARGET->layoutBox.x = MONBOX.x;
+ TARGET->layoutBox.w = MONBOX.w;
+ }
+ }
+ } else
+ TARGET->layoutBox = CBox{WORKAREA.pos() - Vector2D{100000.0, 100000.0}, Vector2D{1.0, 1.0}};
+ } else
+ TARGET->layoutBox = controller->calculateTargetBox(i, j, USABLE, WORKAREA.pos(), *PFSONONE);
if (TARGET->target)
- TARGET->target->setPositionGlobal(TARGET->layoutBox);
+ TARGET->target->setPositionGlobal(targetBoxWithGaps(TARGET->layoutBox, i, j, FS));
+
if (forceInstant && TARGET->target)
TARGET->target->warpPositionSize();
}
@@ -544,6 +605,9 @@ CScrollingAlgorithm::CScrollingAlgorithm() {
}
CScrollingAlgorithm::~CScrollingAlgorithm() {
+ clearFullscreenTarget();
+ updateFullscreenFade(false);
+
m_configCallback.reset();
m_focusCallback.reset();
}
@@ -631,6 +695,8 @@ void CScrollingAlgorithm::removeTarget(SP<ITarget> target) {
if (!DATA)
return;
+ clearFullscreenTarget(target);
+
if (!m_scrollingData->next(DATA->column.lock()) && DATA->column->targetDatas.size() <= 1) {
// move the view if this is the last column
const auto USABLE = usableArea();
@@ -814,6 +880,303 @@ void CScrollingAlgorithm::recalculate() {
m_scrollingData->recalculate();
}
+void CScrollingAlgorithm::syncFullscreenTargets() {
+ for (auto it = m_fullscreenTargets.begin(); it != m_fullscreenTargets.end();) {
+ const auto TARGET = it->target.lock();
+
+ if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() != FSMODE_FULLSCREEN || TARGET->space() != m_parent->space()) {
+ it = m_fullscreenTargets.erase(it);
+ continue;
+ }
+
+ const auto TDATA = dataFor(TARGET);
+ if (!TDATA) {
+ ++it;
+ continue;
+ }
+
+ if (const auto COL = TDATA->column.lock())
+ COL->setColumnWidth(fullscreenColumnWidth());
+
+ ++it;
+ }
+
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ const auto TARGET = TDATA->target.lock();
+ if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() != FSMODE_FULLSCREEN || TARGET->space() != m_parent->space())
+ continue;
+
+ if (!fullscreenStateForTarget(TARGET))
+ m_fullscreenTargets.emplace_back(SFullscreenScrollState{.target = TARGET, .restoreColumnWidth = COL ? std::optional<float>{COL->getColumnWidth()} : std::nullopt});
+
+ COL->setColumnWidth(fullscreenColumnWidth());
+ }
+ }
+}
+
+CScrollingAlgorithm::SFullscreenScrollState* CScrollingAlgorithm::fullscreenStateForTarget(SP<ITarget> target) {
+ if (!target)
+ return nullptr;
+
+ for (auto& state : m_fullscreenTargets) {
+ if (state.target.lock() == target)
+ return &state;
+ }
+
+ return nullptr;
+}
+
+CScrollingAlgorithm::SFullscreenScrollState* CScrollingAlgorithm::fullscreenStateForData(SP<SScrollingTargetData> target) {
+ if (!target)
+ return nullptr;
+
+ return fullscreenStateForTarget(target->target.lock());
+}
+
+void CScrollingAlgorithm::expelTarget(SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx) {
+ auto col = !insertIdx ? m_scrollingData->add() : m_scrollingData->add(*insertIdx);
+ srcCol->remove(tdata->target.lock());
+ col->add(tdata);
+ m_scrollingData->centerOrFitCol(col);
+}
+
+eFullscreenRequestResult CScrollingAlgorithm::requestFullscreen(const SFullscreenRequest& request) {
+ if (!request.target || !m_parent || request.target->space() != m_parent->space())
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ const auto TDATA = dataFor(request.target);
+ if (!TDATA)
+ return FULLSCREEN_REQUEST_DEFAULT;
+
+ if (request.effectiveMode == FSMODE_FULLSCREEN) {
+ if (!fullscreenStateForTarget(request.target)) {
+ const auto COL = TDATA->column.lock();
+ m_fullscreenTargets.emplace_back(
+ SFullscreenScrollState{.target = request.target, .restoreColumnWidth = COL ? std::optional<float>{COL->getColumnWidth()} : std::nullopt});
+ }
+
+ if (const auto COL = TDATA->column.lock()) {
+ COL->setColumnWidth(fullscreenColumnWidth());
+ m_scrollingData->centerOrFitCol(COL);
+ }
+
+ request.target->setFullscreenMode(FSMODE_FULLSCREEN);
+
+ return FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
+ } else if (request.effectiveMode == FSMODE_MAXIMIZED) {
+ // expel, then max width
+ const auto CURRENT_COL = TDATA->column.lock();
+
+ if (CURRENT_COL->targetDatas.size() > 1) {
+ const auto lastTarget = CURRENT_COL->targetDatas.back();
+ const auto currentIdx = m_scrollingData->idx(CURRENT_COL);
+ const auto NEXT_COL = m_scrollingData->next(CURRENT_COL);
+ const auto insertIdx = !NEXT_COL ? std::nullopt : std::optional<int64_t>{currentIdx};
+
+ expelTarget(lastTarget, CURRENT_COL, insertIdx);
+
+ TDATA->column->setColumnWidth(1.F);
+ } else
+ CURRENT_COL->setColumnWidth(1.F);
+
+ request.target->setFullscreenMode(FSMODE_NONE);
+
+ return FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT;
+ }
+
+ if (isFullscreenTarget(TDATA) || request.target->layoutManagedFullscreen()) {
+ clearFullscreenTarget(request.target);
+ request.target->setFullscreenMode(FSMODE_NONE);
+ return request.effectiveMode == FSMODE_NONE ? FULLSCREEN_REQUEST_HANDLED_BY_LAYOUT : FULLSCREEN_REQUEST_DEFAULT;
+ }
+
+ return FULLSCREEN_REQUEST_DEFAULT;
+}
+
+SP<ITarget> CScrollingAlgorithm::layoutFullscreenTarget() const {
+ SP<SScrollingTargetData> fallback;
+
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ if (!isFullscreenTarget(TDATA))
+ continue;
+
+ if (!fallback)
+ fallback = TDATA;
+
+ if (fullscreenColumnCoversMonitor(TDATA->column.lock()))
+ return TDATA->target.lock();
+ }
+ }
+
+ return fallback ? fallback->target.lock() : nullptr;
+}
+
+bool CScrollingAlgorithm::layoutFullscreenCoversMonitor() const {
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ if (!isFullscreenTarget(TDATA))
+ continue;
+
+ if (fullscreenColumnCoversMonitor(TDATA->column.lock()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SP<SScrollingTargetData> CScrollingAlgorithm::fullscreenTargetDataForColumn(SP<SColumnData> col) const {
+ if (!col)
+ return nullptr;
+
+ for (const auto& TDATA : col->targetDatas) {
+ if (!isFullscreenTarget(TDATA))
+ continue;
+
+ return TDATA;
+ }
+
+ return nullptr;
+}
+
+bool CScrollingAlgorithm::isFullscreenTarget(SP<SScrollingTargetData> target) const {
+ if (!target)
+ return false;
+
+ const auto TARGET = target->target.lock();
+ if (!TARGET || !TARGET->layoutManagedFullscreen() || TARGET->fullscreenMode() == FSMODE_NONE)
+ return false;
+
+ return dataFor(TARGET) == target;
+}
+
+float CScrollingAlgorithm::fullscreenColumnWidth() const {
+ if (!m_parent || !m_parent->space() || !m_parent->space()->workspace() || !m_parent->space()->workspace()->m_monitor || !m_scrollingData || !m_scrollingData->controller)
+ return 1.F;
+
+ const auto USABLE = usableArea();
+ const auto MONBOX = m_parent->space()->workspace()->m_monitor->logicalBox();
+ const bool PRIMARY_HORIZ = m_scrollingData->controller->isPrimaryHorizontal();
+ const double usablePrimary = PRIMARY_HORIZ ? USABLE.w : USABLE.h;
+ const double monitorPrimary = PRIMARY_HORIZ ? MONBOX.w : MONBOX.h;
+
+ if (usablePrimary <= 0.0)
+ return 1.F;
+
+ return std::max(1.F, sc<float>(monitorPrimary / usablePrimary));
+}
+
+bool CScrollingAlgorithm::fullscreenColumnCoversMonitor(SP<SColumnData> col) const {
+ if (!col || !m_scrollingData || !m_scrollingData->controller || !m_parent || !m_parent->space() || !m_parent->space()->workspace() ||
+ !m_parent->space()->workspace()->m_monitor)
+ return false;
+
+ if (!fullscreenTargetDataForColumn(col))
+ return false;
+
+ const int64_t COL_IDX = m_scrollingData->idx(col);
+ if (COL_IDX < 0)
+ return false;
+
+ static const auto PFSONONE = CConfigValue<Config::INTEGER>("scrolling:fullscreen_on_one_column");
+
+ const auto USABLE = usableArea();
+ const bool PRIMARY_HORIZ = m_scrollingData->controller->isPrimaryHorizontal();
+ const double VIEW_SIZE = PRIMARY_HORIZ ? USABLE.w : USABLE.h;
+ const double VIEW_START = m_scrollingData->controller->getOffset();
+ const double VIEW_END = VIEW_START + VIEW_SIZE;
+ const double COL_START = m_scrollingData->controller->calculateStripStart(COL_IDX, USABLE, *PFSONONE);
+ const double COL_END = COL_START + m_scrollingData->controller->calculateStripSize(COL_IDX, USABLE, *PFSONONE);
+
+ return COL_START <= VIEW_START + 1.0 && COL_END >= VIEW_END - 1.0;
+}
+
+void CScrollingAlgorithm::updateFullscreenFade(bool coversMonitor) {
+ if (m_lastFullscreenCover == coversMonitor)
+ return;
+
+ m_lastFullscreenCover = coversMonitor;
+
+ if (!coversMonitor) {
+ // prevent stuck focus
+ g_pInputManager->unconstrainMouse();
+ for (const auto& fs : m_fullscreenTargets) {
+ if (!fs.target || !fs.target->window())
+ continue;
+
+ auto w = fs.target->window();
+
+ w->m_layoutFlags.cantLockCursor = true;
+ }
+ } else {
+ for (const auto& fs : m_fullscreenTargets) {
+ if (!fs.target || !fs.target->window())
+ continue;
+
+ auto w = fs.target->window();
+
+ w->m_layoutFlags.cantLockCursor = false;
+ }
+ }
+
+ if (!m_parent || !m_parent->space() || !m_parent->space()->workspace())
+ return;
+
+ // properly update things on top / bottom
+ m_parent->space()->workspace()->setNoMembersAboveFullscreen();
+
+ g_pDesktopAnimationManager->setFullscreenFadeAnimation(m_parent->space()->workspace(),
+ coversMonitor ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
+}
+
+void CScrollingAlgorithm::clearFullscreenTarget(SP<ITarget> target) {
+ bool cleared = false;
+
+ auto clear = [&](SP<ITarget> t) {
+ t->setLayoutManagedFullscreen(false);
+ if (t->window())
+ t->window()->m_layoutFlags.cantLockCursor = false;
+ cleared = true;
+ };
+
+ for (auto it = m_fullscreenTargets.begin(); it != m_fullscreenTargets.end();) {
+ const auto TARGET = it->target.lock();
+
+ if (!TARGET || (target && TARGET != target)) {
+ if (!TARGET)
+ it = m_fullscreenTargets.erase(it);
+ else
+ ++it;
+ continue;
+ }
+
+ const auto TDATA = dataFor(TARGET);
+
+ clear(TARGET);
+
+ if (const auto COL = TDATA ? TDATA->column.lock() : nullptr; COL && it->restoreColumnWidth)
+ COL->setColumnWidth(*it->restoreColumnWidth);
+
+ it = m_fullscreenTargets.erase(it);
+ }
+
+ if (target && target->layoutManagedFullscreen())
+ clear(target);
+ else if (!target) {
+ for (const auto& COL : m_scrollingData->columns) {
+ for (const auto& TDATA : COL->targetDatas) {
+ const auto TARGET = TDATA->target.lock();
+ if (!TARGET || !TARGET->layoutManagedFullscreen())
+ continue;
+
+ clear(TARGET);
+ }
+ }
+ }
+}
+
SP<SScrollingTargetData> CScrollingAlgorithm::closestNode(const Vector2D& posGlobglobgabgalab) {
SP<SScrollingTargetData> res = nullptr;
double distClosest = -1;
@@ -1372,14 +1735,6 @@ Config::ErrorResult CScrollingAlgorithm::layoutMsg(const std::string_view& sv) {
if (!CURRENT_COL)
return stateErr("no current col");
- // expel a target from srcCol into its own new column at insertIdx
- auto expelTarget = [&](SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx) {
- auto col = !insertIdx ? m_scrollingData->add() : m_scrollingData->add(*insertIdx);
- srcCol->remove(tdata->target.lock());
- col->add(tdata);
- m_scrollingData->centerOrFitCol(col);
- };
-
// consume the first target from adjCol into dstCol
auto consumeTarget = [&](SP<SColumnData> dstCol, SP<SColumnData> adjCol) {
const auto target = adjCol->targetDatas.front();
@@ -1546,7 +1901,7 @@ SP<SScrollingTargetData> CScrollingAlgorithm::findBestNeighbor(SP<SScrollingTarg
return nullptr;
}
-SP<SScrollingTargetData> CScrollingAlgorithm::dataFor(SP<ITarget> t) {
+SP<SScrollingTargetData> CScrollingAlgorithm::dataFor(SP<ITarget> t) const {
if (!t)
return nullptr;
@@ -1584,7 +1939,7 @@ eScrollDirection CScrollingAlgorithm::getDynamicDirection() {
return SCROLL_DIR_RIGHT; // default
}
-CBox CScrollingAlgorithm::usableArea() {
+CBox CScrollingAlgorithm::usableArea() const {
if (!m_parent || !m_parent->space())
return {};
diff --git a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
index 288d15a6..19eb9ba6 100644
--- a/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
+++ b/src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
@@ -5,6 +5,7 @@
#include "ScrollTapeController.hpp"
#include "../../../../helpers/signal/Signal.hpp"
+#include <optional>
#include <vector>
namespace Layout::Tiled {
@@ -94,23 +95,27 @@ namespace Layout::Tiled {
CScrollingAlgorithm();
virtual ~CScrollingAlgorithm();
- virtual void newTarget(SP<ITarget> target);
- virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
- virtual void removeTarget(SP<ITarget> target);
+ virtual void newTarget(SP<ITarget> target);
+ virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
+ virtual void removeTarget(SP<ITarget> target);
- virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
- virtual void recalculate();
+ virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
+ virtual void recalculate();
- virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
+ virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
- virtual Config::ErrorResult layoutMsg(const std::string_view& sv);
- virtual std::optional<Vector2D> predictSizeForNewTarget();
+ virtual Config::ErrorResult layoutMsg(const std::string_view& sv);
+ virtual std::optional<Vector2D> predictSizeForNewTarget();
- virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
- virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
+ virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
+ virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
- CBox usableArea();
- SP<SScrollingTargetData> dataFor(SP<ITarget> t);
+ virtual eFullscreenRequestResult requestFullscreen(const SFullscreenRequest& request);
+ virtual SP<ITarget> layoutFullscreenTarget() const;
+ virtual bool layoutFullscreenCoversMonitor() const;
+
+ CBox usableArea() const;
+ SP<SScrollingTargetData> dataFor(SP<ITarget> t) const;
enum eInputMode : uint8_t {
INPUT_MODE_SOFT = 0,
@@ -129,16 +134,36 @@ namespace Layout::Tiled {
std::vector<float> configuredWidths;
} m_config;
- eScrollDirection getDynamicDirection();
+ eScrollDirection getDynamicDirection();
+
+ struct SFullscreenScrollState {
+ WP<ITarget> target;
+ std::optional<float> restoreColumnWidth;
+ };
+
+ void syncFullscreenTargets();
+ SFullscreenScrollState* fullscreenStateForTarget(SP<ITarget> target);
+ SFullscreenScrollState* fullscreenStateForData(SP<SScrollingTargetData> target);
+ SP<SScrollingTargetData> fullscreenTargetDataForColumn(SP<SColumnData> col) const;
+ bool isFullscreenTarget(SP<SScrollingTargetData> target) const;
+ float fullscreenColumnWidth() const;
+ bool fullscreenColumnCoversMonitor(SP<SColumnData> col) const;
+ void updateFullscreenFade(bool coversMonitor);
+ void clearFullscreenTarget(SP<ITarget> target = nullptr);
+
+ SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
+ SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
+
+ void focusTargetUpdate(SP<ITarget> target);
+ void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
+ void focusOnInput(SP<ITarget> target, eInputMode input);
- SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
- SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
+ void expelTarget(SP<SScrollingTargetData> tdata, SP<SColumnData> srcCol, std::optional<int64_t> insertIdx);
- void focusTargetUpdate(SP<ITarget> target);
- void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
- void focusOnInput(SP<ITarget> target, eInputMode input);
+ float defaultColumnWidth();
- float defaultColumnWidth();
+ std::vector<SFullscreenScrollState> m_fullscreenTargets;
+ bool m_lastFullscreenCover = false;
friend struct SScrollingData;
};