From 8ee3f900474e11ec635b1655a61c683cf523a9fb Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Tue, 22 Sep 2026 15:38:22 +0200 Subject: [PATCH] Fix multi-second Ctrl+V stall and cursor jump on paste Two issues on the interactive paste path: 1. Stall: DiagramEventAddPaste's constructor called Diagram::fromXml() with no database batching, so every addItem() emitted dataBaseUpdated() and each connected table model re-ran its full SQL query. A typical paste (~40 elements + ~40 conductors) triggered ~77 rebuilds of the table models -- measured at ~2.1 s of pure fromXml time on a large project. Project loading already batches this via setUpdateBlocked()/blockSignals() (QETProject::readProjectXml); the paste path now does the same: block during fromXml, one updateDB() after. Measured fromXml: 2114 ms -> 143 ms. 2. Cursor jump: m_initial_cursor was set to the group origin but the physical cursor stayed at the Ctrl+V press location, so the first mouseMoveEvent computed a large delta and the items jumped on first touch. Warp the cursor to the group origin after placement so the baseline and the actual cursor position match. --- sources/diagramevent/diagrameventaddpaste.cpp | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sources/diagramevent/diagrameventaddpaste.cpp b/sources/diagramevent/diagrameventaddpaste.cpp index a22d7d485..5a853d754 100644 --- a/sources/diagramevent/diagrameventaddpaste.cpp +++ b/sources/diagramevent/diagrameventaddpaste.cpp @@ -22,6 +22,7 @@ #include "../qetapp.h" #include "../qetdiagrameditor.h" #include "../qetgraphicsitem/conductor.h" +#include "../qetproject.h" #include @@ -52,8 +53,25 @@ QDomDocument document_xml; if (!document_xml.setContent(clipboard_text)) return; + //Batch the database work the same way project loading does + //(QETProject::readProjectXml): without this, every addItem() + //below emits dataBaseUpdated(), which makes each connected + //table model re-run its full SQL query -- ~77 queries for a + //typical paste, i.e. the multi-second stall on Ctrl+V. + auto *db = m_diagram->project() ? m_diagram->project()->dataBase() : nullptr; + if (db) { + db->blockSignals(true); + db->setUpdateBlocked(true); + } + //Load items at their original XML coordinates. m_diagram->fromXml(document_xml, QPointF(), false, &m_content); + + if (db) { + db->blockSignals(false); + db->setUpdateBlocked(false); + db->updateDB(); + } if (!m_content.count()) return; const QList movable = m_content.items(MovableItems); @@ -116,6 +134,14 @@ if (const auto qde = QETApp::diagramEditorAncestorOf(view)) { m_status_bar = qde->statusBar(); } + //Warp the cursor to the group's grid-snapped origin so + //the actual cursor position matches m_initial_cursor. + //Without this the first mouseMoveEvent computes a large + //delta (cursor is still at the Ctrl+V press location) + //and the items jump on first touch. + const QPoint view_pos = view->mapFromScene(m_initial_cursor); + const QPoint global_pos = view->viewport()->mapToGlobal(view_pos); + QCursor::setPos(global_pos); } } showHint();