diff --git a/Plugins/ZShell/Blobs/blobgroup.cpp b/Plugins/ZShell/Blobs/blobgroup.cpp index a24a750..1ccd85f 100644 --- a/Plugins/ZShell/Blobs/blobgroup.cpp +++ b/Plugins/ZShell/Blobs/blobgroup.cpp @@ -67,27 +67,48 @@ void BlobGroup::markDirty() { } } -void BlobGroup::markShapeDirty(BlobShape* source) { +void BlobGroup::markShapeDirty(BlobShape* source, const QRectF& oldGeometry) { m_physicsUpdated = false; source->polish(); source->update(); - // Use actual current geometry (not cached rects) to find spatial neighbors. - // Cached rects can be stale when multiple shapes move simultaneously, - // causing the blob background to desync from panels that changed size. const float pad = static_cast(m_smoothing) * 2.0f; - const QRectF srcRect(static_cast(source->m_cachedPaddedX - pad), - static_cast(source->m_cachedPaddedY - pad), static_cast(source->m_cachedPaddedW + pad * 2.0f), - static_cast(source->m_cachedPaddedH + pad * 2.0f)); + // Compute the source's dirty rect in scene space using its old position + const QPointF oldScene(oldGeometry.x(), oldGeometry.y()); + const QRectF oldDirtyRect(oldScene.x() - static_cast(pad), + oldScene.y() - static_cast(pad), + static_cast(oldGeometry.width() + pad * 2.0), + static_cast(oldGeometry.height() + pad * 2.0)); + + // Compute the source's dirty rect using its new position (fresh from polish) + const QRectF newDirtyRect(static_cast(source->m_cachedPaddedX - pad), + static_cast(source->m_cachedPaddedY - pad), + static_cast(source->m_cachedPaddedW + pad * 2.0), + static_cast(source->m_cachedPaddedH + pad * 2.0)); + + // Mark shapes near the old position as dirty (prevents ghost blobs after panel moves) for (auto* shape : std::as_const(m_shapes)) { if (shape == source) continue; const QPointF shapeScene = shape->mapToScene(QPointF(0, 0)); - const QRectF otherRect(shapeScene.x(), shapeScene.y(), + const QRectF shapeRect(shapeScene.x(), shapeScene.y(), static_cast(shape->width()), static_cast(shape->height())); - if (srcRect.intersects(otherRect)) { + if (oldDirtyRect.intersects(shapeRect)) { + shape->polish(); + shape->update(); + } + } + + // Mark shapes near the new position as dirty + for (auto* shape : std::as_const(m_shapes)) { + if (shape == source) + continue; + const QPointF shapeScene = shape->mapToScene(QPointF(0, 0)); + const QRectF shapeRect(shapeScene.x(), shapeScene.y(), + static_cast(shape->width()), static_cast(shape->height())); + if (newDirtyRect.intersects(shapeRect)) { shape->polish(); shape->update(); } diff --git a/Plugins/ZShell/Blobs/blobgroup.hpp b/Plugins/ZShell/Blobs/blobgroup.hpp index ce5d933..44d5f87 100644 --- a/Plugins/ZShell/Blobs/blobgroup.hpp +++ b/Plugins/ZShell/Blobs/blobgroup.hpp @@ -45,7 +45,7 @@ BlobInvertedRect* invertedRect() const { } void markDirty(); -void markShapeDirty(BlobShape* source); +void markShapeDirty(BlobShape* source, const QRectF& oldGeometry); void ensurePhysicsUpdated(); signals: diff --git a/Plugins/ZShell/Blobs/blobshape.cpp b/Plugins/ZShell/Blobs/blobshape.cpp index 85bebf2..306a666 100644 --- a/Plugins/ZShell/Blobs/blobshape.cpp +++ b/Plugins/ZShell/Blobs/blobshape.cpp @@ -77,7 +77,7 @@ void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeome if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f || dw > 0.5 || dh > 0.5) { m_pendingDx = 0; m_pendingDy = 0; - m_group->markShapeDirty(this); + m_group->markShapeDirty(this, oldGeometry); } } }