diff --git a/src/core/layersurfacecontainer.cpp b/src/core/layersurfacecontainer.cpp index bab103117..5571e6b9c 100644 --- a/src/core/layersurfacecontainer.cpp +++ b/src/core/layersurfacecontainer.cpp @@ -64,19 +64,26 @@ void LayerSurfaceContainer::removeOutput(Output *output) Q_ASSERT(container); m_surfaceContainers.removeOne(container); + OutputLayerSurfaceContainer *targetContainer = m_surfaceContainers.isEmpty() + ? nullptr + : m_surfaceContainers.first(); + const auto surfaces = container->surfaces(); for (SurfaceWrapper *surface : surfaces) { - auto layerSurface = qobject_cast(surface->shellSurface()); - Q_ASSERT(layerSurface); - // Needs to be moved to the new primary output - if (!layerSurface->output() && rootContainer()->primaryOutput()) { - container->removeSurface(surface); - addSurfaceToContainer(surface); + container->removeSurface(surface); + if (targetContainer) { + targetContainer->addSurface(surface); } else { - layerSurface->closed(); + auto layerSurface = qobject_cast(surface->shellSurface()); + if (layerSurface) + layerSurface->closed(); } } + if (targetContainer) { + targetContainer->output()->arrangeLayerSurfaces(); + } + container->deleteLater(); } diff --git a/src/core/rootsurfacecontainer.cpp b/src/core/rootsurfacecontainer.cpp index 7aaf942cd..8b63fba52 100644 --- a/src/core/rootsurfacecontainer.cpp +++ b/src/core/rootsurfacecontainer.cpp @@ -149,6 +149,13 @@ void RootSurfaceContainer::removeOutput(Output *output) } } + // Save positions of remaining outputs before layout removal + QMap oldPositions; + for (auto o : outputs()) { + if (o != output) + oldPositions[o] = o->outputItem()->position(); + } + m_outputLayout->remove(output->output()); if (m_primaryOutput == output) { const auto outputs = m_outputLayout->outputs(); @@ -158,6 +165,28 @@ void RootSurfaceContainer::removeOutput(Output *output) } } + // Correct positions of surfaces on remaining outputs after layout change + for (auto o : outputs()) { + if (o == output) + continue; + auto it = oldPositions.find(o); + if (it == oldPositions.end()) + continue; + QPointF delta = o->outputItem()->position() - it.value(); + if (!delta.isNull()) { + for (auto *surface : surfaces()) { + if (surface->type() != SurfaceWrapper::Type::Layer && surface->ownsOutput() == o) { + surface->setPosition(surface->position() + delta); + if (surface->type() == SurfaceWrapper::Type::XdgToplevel) { + auto normalGeo = surface->normalGeometry(); + normalGeo.moveTopLeft(normalGeo.topLeft() + delta); + surface->moveNormalGeometryInOutput(normalGeo.topLeft()); + } + } + } + } + } + // ensure cursor within output const auto outputPos = output->outputItem()->position(); if (output->geometry().contains(m_cursor->position()) && m_primaryOutput) { diff --git a/src/output/output.h b/src/output/output.h index b0bfa5fb2..08939e8f7 100644 --- a/src/output/output.h +++ b/src/output/output.h @@ -90,6 +90,7 @@ class Output : public SurfaceListModel void adjustToOutputBounds(QPointF &pos, const QRectF &normalGeo, const QRectF &outputRect) const; + void arrangeLayerSurfaces(); Q_SIGNALS: void exclusiveZoneChanged(); @@ -110,7 +111,6 @@ public Q_SLOTS: void setExclusiveZone(Qt::Edge edge, QObject *object, int value); bool removeExclusiveZone(QObject *object); void arrangeLayerSurface(SurfaceWrapper *surface); - void arrangeLayerSurfaces(); void arrangeNonLayerSurface(SurfaceWrapper *surface, const QSizeF &sizeDiff); void arrangePopupSurface(SurfaceWrapper *surface); void arrangeNonLayerSurfaces(); diff --git a/src/output/outputlifecyclemanager.cpp b/src/output/outputlifecyclemanager.cpp index 3652021af..6f02b7aa5 100644 --- a/src/output/outputlifecyclemanager.cpp +++ b/src/output/outputlifecyclemanager.cpp @@ -59,6 +59,23 @@ void OutputLifecycleManager::switchPrimaryOutput(Output *from, m_rootContainer->moveSurfacesToOutput(surfaces, to, from); } +void OutputLifecycleManager::migrateSurfacesToNewPrimary(Output *removedOutput, + const QList &surfaces) +{ + // NOTE: This must be called while removedOutput's Output item still has valid position + // data. The call chain is: Helper::onOutputRemoved -> removeOutput (switches primary) + // -> onScreenRemoved -> migrateSurfacesToNewPrimary. At this point the removed output + // has been removed from the layout, but the Output object (and its item position) still + // exists because `delete o` happens after onScreenRemoved returns. + if (!m_rootContainer || surfaces.isEmpty()) + return; + + auto newPrimary = m_rootContainer->primaryOutput(); + if (newPrimary) { + m_rootContainer->moveSurfacesToOutput(surfaces, newPrimary, removedOutput); + } +} + void OutputLifecycleManager::onScreenAdded(Output *output) { if (!output || !m_configState) @@ -91,11 +108,8 @@ void OutputLifecycleManager::onScreenRemoved(Output *output, markScreenAsPrimaryIntent(output); } - if (!isCurrentPrimary && !wasPrimaryBeforeRemoval) { - auto primaryOutput = m_rootContainer->primaryOutput(); - if (primaryOutput) { - m_rootContainer->moveSurfacesToOutput(surfaces, primaryOutput, output); - } + if (!isCurrentPrimary) { + migrateSurfacesToNewPrimary(output, surfaces); } } @@ -120,10 +134,7 @@ void OutputLifecycleManager::onScreenDisabled(Output *output, switchPrimaryOutput(output, nextPrimary, surfaces); } } else if (!isCurrentPrimary) { - auto primaryOutput = m_rootContainer->primaryOutput(); - if (primaryOutput) { - m_rootContainer->moveSurfacesToOutput(surfaces, primaryOutput, output); - } + migrateSurfacesToNewPrimary(output, surfaces); } } diff --git a/src/output/outputlifecyclemanager.h b/src/output/outputlifecyclemanager.h index 9eac0d683..1ab5a05cc 100644 --- a/src/output/outputlifecyclemanager.h +++ b/src/output/outputlifecyclemanager.h @@ -1,4 +1,4 @@ -// Copyright (C) 2025 UnionTech Software Technology Co., Ltd. +// Copyright (C) 2025-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #pragma once @@ -32,6 +32,7 @@ class OutputLifecycleManager : public QObject { void onScreenRemoved(Output *output, const QList &surfaces); void onScreenDisabled(Output *output, const QList &surfaces); void onScreenEnabled(Output *output); + void migrateSurfacesToNewPrimary(Output *removedOutput, const QList &surfaces); void setMode(Mode mode) { m_mode = mode; }