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 <noreply@anthropic.com>
This commit is contained in:
Beat Hangartner
2026-10-06 09:46:17 +02:00
parent 010e44ab8a
commit 5cad0c6159
3 changed files with 65 additions and 3 deletions
@@ -26,11 +26,12 @@
#include <QTextDocument>
#include <cmath>
#include <utility>
/**
@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<QetGraphicsHandlerItem *>(watched));
@@ -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();
+32
View File
@@ -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()
{