From 5cad0c6159b87fc03ad1a22484872b4668d712c8 Mon Sep 17 00:00:00 2001 From: Beat Hangartner Date: Tue, 6 Oct 2026 09:46:17 +0200 Subject: [PATCH] Keep the text resize handles working after a scene change A scene drops the event filters of an item that leaves it. The handles installed theirs once, in the constructor, and only if the text was in a scene: after the text was removed and added back (undoing a delete does that), or when the handles were made before the text was in a scene, they no longer reacted, and a drag moved the text instead. TextResizeHandles now installs the filters again whenever it enters a scene. Tests: dragAfterLeavingTheScene, dragWhenCreatedOutsideAScene (both fail before). Co-Authored-By: Claude Opus 5.5 --- .../textresizehandles.cpp | 34 +++++++++++++++++-- .../textresizehandles.h | 2 ++ tests/qttest/tst_textresizehandles.cpp | 32 +++++++++++++++++ 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/sources/QetGraphicsItemModeler/textresizehandles.cpp b/sources/QetGraphicsItemModeler/textresizehandles.cpp index c92a02b2c..53c90d77a 100644 --- a/sources/QetGraphicsItemModeler/textresizehandles.cpp +++ b/sources/QetGraphicsItemModeler/textresizehandles.cpp @@ -26,11 +26,12 @@ #include #include +#include /** @brief TextResizeHandles::TextResizeHandles @param text : the text to resize, becomes the parent of the handles. - It must be in a scene, the handles filter their own events. + The text may be in a scene or not yet. @param handle_size : see QETUtils::graphicsHandlerSize() */ TextResizeHandles::TextResizeHandles(QGraphicsTextItem *text, qreal handle_size) : @@ -48,10 +49,9 @@ TextResizeHandles::TextResizeHandles(QGraphicsTextItem *text, qreal handle_size) "double-cliquer pour une largeur automatique")); handle->setCursor(i == TextResize::TopLeft || i == TextResize::BottomRight ? Qt::SizeFDiagCursor : Qt::SizeBDiagCursor); - if (scene()) - handle->installSceneEventFilter(this); m_handles << handle; } + installHandleFilters(); m_size_connection = connect(text->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, @@ -90,6 +90,34 @@ void TextResizeHandles::updateHandlesPos() m_handles.at(i)->setPos(TextResize::cornerOf(rect, TextResize::Corner(i))); } +/** + @brief TextResizeHandles::itemChange + A scene drops the event filters of an item that leaves it, so the + handles stop reacting to the mouse when the text is removed from its + scene and added back (undoing a delete does that), or when they were + created before the text was in a scene. Install them again each time + these handles enter a scene. +*/ +QVariant TextResizeHandles::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemSceneHasChanged) + installHandleFilters(); + return QGraphicsObject::itemChange(change, value); +} + +/** + @brief TextResizeHandles::installHandleFilters + Filter the mouse events of the handles, when they are in a scene. +*/ +void TextResizeHandles::installHandleFilters() +{ + if (!scene()) + return; + for (QetGraphicsHandlerItem *handle : std::as_const(m_handles)) + if (handle->scene() == scene()) + handle->installSceneEventFilter(this); +} + bool TextResizeHandles::sceneEventFilter(QGraphicsItem *watched, QEvent *event) { const int index = m_handles.indexOf(static_cast(watched)); diff --git a/sources/QetGraphicsItemModeler/textresizehandles.h b/sources/QetGraphicsItemModeler/textresizehandles.h index e098b54d4..2f92ef905 100644 --- a/sources/QetGraphicsItemModeler/textresizehandles.h +++ b/sources/QetGraphicsItemModeler/textresizehandles.h @@ -65,8 +65,10 @@ class TextResizeHandles : public QGraphicsObject protected: bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override; + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; private: + void installHandleFilters(); void pressed(TextResize::Corner corner, const QPointF &scene_pos); void moved(const QPointF &scene_pos); void released(); diff --git a/tests/qttest/tst_textresizehandles.cpp b/tests/qttest/tst_textresizehandles.cpp index d4e7f34e7..99f4c4779 100644 --- a/tests/qttest/tst_textresizehandles.cpp +++ b/tests/qttest/tst_textresizehandles.cpp @@ -215,6 +215,38 @@ private slots: } } + // The handles still work after the text left its scene and came back, + // as undoing a delete does: Qt drops scene event filters on removal. + void dragAfterLeavingTheScene() + { + QGraphicsScene scene; + auto *text = new WidthText(QStringLiteral("Motor protection switch Q12")); + scene.addItem(text); + auto *handles = new TextResizeHandles(text); + + scene.removeItem(text); + scene.addItem(text); + + const QPointF start_pos = text->pos(); + drag(scene, *handles, *text, TextResize::BottomRight, QPointF(-40, 0)); + QVERIFY(text->textWidth() > 0); + QCOMPARE(text->pos(), start_pos); + delete text; + } + + // The handles also work when they are created before the text is in a + // scene. + void dragWhenCreatedOutsideAScene() + { + QGraphicsScene scene; + auto *text = new WidthText(QStringLiteral("Motor protection switch Q12")); + auto *handles = new TextResizeHandles(text); + scene.addItem(text); + + drag(scene, *handles, *text, TextResize::BottomRight, QPointF(-40, 0)); + QVERIFY(text->textWidth() > 0); + } + // A text is never made narrower than its longest word. void dragStopsAtTheLongestWord() {